s4-torture: ran minimal_includes.pl over source4/torture
[ira/wip.git] / source4 / torture / util_smb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester utility functions
4    Copyright (C) Andrew Tridgell 2003
5    Copyright (C) Jelmer Vernooij 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/cmdline/popt_common.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/raw/raw_proto.h"
25 #include "libcli/raw/ioctl.h"
26 #include "libcli/libcli.h"
27 #include "system/filesys.h"
28 #include "system/shmem.h"
29 #include "system/wait.h"
30 #include "system/time.h"
31 #include "torture/torture.h"
32 #include "../lib/util/dlinklist.h"
33 #include "libcli/resolve/resolve.h"
34 #include "param/param.h"
35 #include "libcli/security/security.h"
36 #include "libcli/util/clilsa.h"
37
38
39 /**
40   setup a directory ready for a test
41 */
42 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
43 {
44         smb_raw_exit(cli->session);
45         if (smbcli_deltree(cli->tree, dname) == -1 ||
46             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
47                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
48                 return false;
49         }
50         return true;
51 }
52
53 /*
54   create a directory, returning a handle to it
55 */
56 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
57 {
58         NTSTATUS status;
59         union smb_open io;
60         TALLOC_CTX *mem_ctx;
61
62         mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
63
64         io.generic.level = RAW_OPEN_NTCREATEX;
65         io.ntcreatex.in.root_fid.fnum = 0;
66         io.ntcreatex.in.flags = 0;
67         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
68         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
69         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
70         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
71         io.ntcreatex.in.alloc_size = 0;
72         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
73         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
74         io.ntcreatex.in.security_flags = 0;
75         io.ntcreatex.in.fname = dname;
76
77         status = smb_raw_open(tree, mem_ctx, &io);
78         talloc_free(mem_ctx);
79
80         if (NT_STATUS_IS_OK(status)) {
81                 *fnum = io.ntcreatex.out.file.fnum;
82         }
83
84         return status;
85 }
86
87
88 /**
89   sometimes we need a fairly complex file to work with, so we can test
90   all possible attributes. 
91 */
92 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
93 {
94         int fnum;
95         char buf[7] = "abc";
96         union smb_setfileinfo setfile;
97         union smb_fileinfo fileinfo;
98         time_t t = (time(NULL) & ~1);
99         NTSTATUS status;
100
101         smbcli_unlink(cli->tree, fname);
102         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
103                                      SEC_RIGHTS_FILE_ALL,
104                                      FILE_ATTRIBUTE_NORMAL,
105                                      NTCREATEX_SHARE_ACCESS_DELETE|
106                                      NTCREATEX_SHARE_ACCESS_READ|
107                                      NTCREATEX_SHARE_ACCESS_WRITE, 
108                                      NTCREATEX_DISP_OVERWRITE_IF,
109                                      0, 0);
110         if (fnum == -1) return -1;
111
112         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
113
114         if (strchr(fname, ':') == NULL) {
115                 /* setup some EAs */
116                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
117                 setfile.generic.in.file.fnum = fnum;
118                 setfile.ea_set.in.num_eas = 2;  
119                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
120                 setfile.ea_set.in.eas[0].flags = 0;
121                 setfile.ea_set.in.eas[0].name.s = "EAONE";
122                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
123                 setfile.ea_set.in.eas[1].flags = 0;
124                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
125                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
126                 status = smb_raw_setfileinfo(cli->tree, &setfile);
127                 if (!NT_STATUS_IS_OK(status)) {
128                         printf("Failed to setup EAs\n");
129                 }
130         }
131
132         /* make sure all the timestamps aren't the same, and are also 
133            in different DST zones*/
134         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
135         setfile.generic.in.file.fnum = fnum;
136
137         setfile.setattre.in.create_time = t + 9*30*24*60*60;
138         setfile.setattre.in.access_time = t + 6*30*24*60*60;
139         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
140
141         status = smb_raw_setfileinfo(cli->tree, &setfile);
142         if (!NT_STATUS_IS_OK(status)) {
143                 printf("Failed to setup file times - %s\n", nt_errstr(status));
144         }
145
146         /* make sure all the timestamps aren't the same */
147         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
148         fileinfo.generic.in.file.fnum = fnum;
149
150         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
151         if (!NT_STATUS_IS_OK(status)) {
152                 printf("Failed to query file times - %s\n", nt_errstr(status));
153         }
154
155         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
156                 printf("create_time not setup correctly\n");
157         }
158         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
159                 printf("access_time not setup correctly\n");
160         }
161         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
162                 printf("write_time not setup correctly\n");
163         }
164
165         return fnum;
166 }
167
168
169 /*
170   sometimes we need a fairly complex directory to work with, so we can test
171   all possible attributes. 
172 */
173 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
174 {
175         int fnum;
176         union smb_setfileinfo setfile;
177         union smb_fileinfo fileinfo;
178         time_t t = (time(NULL) & ~1);
179         NTSTATUS status;
180
181         smbcli_deltree(cli->tree, dname);
182         fnum = smbcli_nt_create_full(cli->tree, dname, 0, 
183                                      SEC_RIGHTS_DIR_ALL,
184                                      FILE_ATTRIBUTE_DIRECTORY,
185                                      NTCREATEX_SHARE_ACCESS_READ|
186                                      NTCREATEX_SHARE_ACCESS_WRITE, 
187                                      NTCREATEX_DISP_OPEN_IF,
188                                      NTCREATEX_OPTIONS_DIRECTORY, 0);
189         if (fnum == -1) return -1;
190
191         if (strchr(dname, ':') == NULL) {
192                 /* setup some EAs */
193                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
194                 setfile.generic.in.file.fnum = fnum;
195                 setfile.ea_set.in.num_eas = 2;  
196                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
197                 setfile.ea_set.in.eas[0].flags = 0;
198                 setfile.ea_set.in.eas[0].name.s = "EAONE";
199                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
200                 setfile.ea_set.in.eas[1].flags = 0;
201                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
202                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
203                 status = smb_raw_setfileinfo(cli->tree, &setfile);
204                 if (!NT_STATUS_IS_OK(status)) {
205                         printf("Failed to setup EAs\n");
206                 }
207         }
208
209         /* make sure all the timestamps aren't the same, and are also 
210            in different DST zones*/
211         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
212         setfile.generic.in.file.fnum = fnum;
213
214         setfile.setattre.in.create_time = t + 9*30*24*60*60;
215         setfile.setattre.in.access_time = t + 6*30*24*60*60;
216         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
217
218         status = smb_raw_setfileinfo(cli->tree, &setfile);
219         if (!NT_STATUS_IS_OK(status)) {
220                 printf("Failed to setup file times - %s\n", nt_errstr(status));
221         }
222
223         /* make sure all the timestamps aren't the same */
224         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
225         fileinfo.generic.in.file.fnum = fnum;
226
227         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
228         if (!NT_STATUS_IS_OK(status)) {
229                 printf("Failed to query file times - %s\n", nt_errstr(status));
230         }
231
232         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
233                 printf("create_time not setup correctly\n");
234         }
235         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
236                 printf("access_time not setup correctly\n");
237         }
238         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
239                 printf("write_time not setup correctly\n");
240         }
241
242         return fnum;
243 }
244
245
246
247 /* return a pointer to a anonymous shared memory segment of size "size"
248    which will persist across fork() but will disappear when all processes
249    exit 
250
251    The memory is not zeroed 
252
253    This function uses system5 shared memory. It takes advantage of a property
254    that the memory is not destroyed if it is attached when the id is removed
255    */
256 void *shm_setup(int size)
257 {
258         int shmid;
259         void *ret;
260
261 #ifdef __QNXNTO__
262         shmid = shm_open("private", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
263         if (shmid == -1) {
264                 printf("can't get shared memory\n");
265                 exit(1);
266         }
267         shm_unlink("private");
268         if (ftruncate(shmid, size) == -1) {
269                 printf("can't set shared memory size\n");
270                 exit(1);
271         }
272         ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
273         if (ret == MAP_FAILED) {
274                 printf("can't map shared memory\n");
275                 exit(1);
276         }
277 #else
278         shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
279         if (shmid == -1) {
280                 printf("can't get shared memory\n");
281                 exit(1);
282         }
283         ret = (void *)shmat(shmid, 0, 0);
284         if (!ret || ret == (void *)-1) {
285                 printf("can't attach to shared memory\n");
286                 return NULL;
287         }
288         /* the following releases the ipc, but note that this process
289            and all its children will still have access to the memory, its
290            just that the shmid is no longer valid for other shm calls. This
291            means we don't leave behind lots of shm segments after we exit 
292
293            See Stevens "advanced programming in unix env" for details
294            */
295         shmctl(shmid, IPC_RMID, 0);
296 #endif
297         
298         return ret;
299 }
300
301
302 /**
303   check that a wire string matches the flags specified 
304   not 100% accurate, but close enough for testing
305 */
306 bool wire_bad_flags(struct smb_wire_string *str, int flags, 
307                     struct smbcli_transport *transport)
308 {
309         bool server_unicode;
310         int len;
311         if (!str || !str->s) return true;
312         len = strlen(str->s);
313         if (flags & STR_TERMINATE) len++;
314
315         server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
316         if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) {
317                 server_unicode = false;
318         }
319
320         if ((flags & STR_UNICODE) || server_unicode) {
321                 len *= 2;
322         } else if (flags & STR_TERMINATE_ASCII) {
323                 len++;
324         }
325         if (str->private_length != len) {
326                 printf("Expected wire_length %d but got %d for '%s'\n", 
327                        len, str->private_length, str->s);
328                 return true;
329         }
330         return false;
331 }
332
333 /*
334   dump a all_info QFILEINFO structure
335 */
336 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
337 {
338         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
339         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
340         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
341         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
342         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
343         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
344         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
345         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
346         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
347         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
348         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
349         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
350 }
351
352 /*
353   dump file infor by name
354 */
355 void torture_all_info(struct smbcli_tree *tree, const char *fname)
356 {
357         TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
358         union smb_fileinfo finfo;
359         NTSTATUS status;
360
361         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
362         finfo.generic.in.file.path = fname;
363         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
364         if (!NT_STATUS_IS_OK(status)) {
365                 d_printf("%s - %s\n", fname, nt_errstr(status));
366                 return;
367         }
368
369         d_printf("%s:\n", fname);
370         dump_all_info(mem_ctx, &finfo);
371         talloc_free(mem_ctx);
372 }
373
374
375 /*
376   set a attribute on a file
377 */
378 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
379 {
380         union smb_setfileinfo sfinfo;
381         NTSTATUS status;
382
383         ZERO_STRUCT(sfinfo.basic_info.in);
384         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
385         sfinfo.basic_info.in.file.path = fname;
386         sfinfo.basic_info.in.attrib = attrib;
387         status = smb_raw_setpathinfo(tree, &sfinfo);
388         return NT_STATUS_IS_OK(status);
389 }
390
391
392 /*
393   set a file descriptor as sparse
394 */
395 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
396 {
397         union smb_ioctl nt;
398         NTSTATUS status;
399         TALLOC_CTX *mem_ctx;
400
401         mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
402         if (!mem_ctx) {
403                 return NT_STATUS_NO_MEMORY;
404         }
405
406         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
407         nt.ntioctl.in.function = FSCTL_SET_SPARSE;
408         nt.ntioctl.in.file.fnum = fnum;
409         nt.ntioctl.in.fsctl = true;
410         nt.ntioctl.in.filter = 0;
411         nt.ntioctl.in.max_data = 0;
412         nt.ntioctl.in.blob = data_blob(NULL, 0);
413
414         status = smb_raw_ioctl(tree, mem_ctx, &nt);
415
416         talloc_free(mem_ctx);
417
418         return status;
419 }
420
421 /*
422   check that an EA has the right value 
423 */
424 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
425                           const char *fname, const char *eaname, const char *value)
426 {
427         union smb_fileinfo info;
428         NTSTATUS status;
429         struct ea_name ea;
430         TALLOC_CTX *mem_ctx = talloc_new(cli);
431
432         info.ea_list.level = RAW_FILEINFO_EA_LIST;
433         info.ea_list.in.file.path = fname;
434         info.ea_list.in.num_names = 1;
435         info.ea_list.in.ea_names = &ea;
436
437         ea.name.s = eaname;
438
439         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
440         if (!NT_STATUS_IS_OK(status)) {
441                 talloc_free(mem_ctx);
442                 return status;
443         }
444
445         if (info.ea_list.out.num_eas != 1) {
446                 printf("Expected 1 ea in ea_list\n");
447                 talloc_free(mem_ctx);
448                 return NT_STATUS_EA_CORRUPT_ERROR;
449         }
450
451         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
452                 printf("Expected ea '%s' not '%s' in ea_list\n",
453                        eaname, info.ea_list.out.eas[0].name.s);
454                 talloc_free(mem_ctx);
455                 return NT_STATUS_EA_CORRUPT_ERROR;
456         }
457
458         if (value == NULL) {
459                 if (info.ea_list.out.eas[0].value.length != 0) {
460                         printf("Expected zero length ea for %s\n", eaname);
461                         talloc_free(mem_ctx);
462                         return NT_STATUS_EA_CORRUPT_ERROR;
463                 }
464                 talloc_free(mem_ctx);
465                 return NT_STATUS_OK;
466         }
467
468         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
469             memcmp(value, info.ea_list.out.eas[0].value.data,
470                    info.ea_list.out.eas[0].value.length) == 0) {
471                 talloc_free(mem_ctx);
472                 return NT_STATUS_OK;
473         }
474
475         printf("Expected value '%s' not '%*.*s' for ea %s\n",
476                value, 
477                (int)info.ea_list.out.eas[0].value.length,
478                (int)info.ea_list.out.eas[0].value.length,
479                info.ea_list.out.eas[0].value.data,
480                eaname);
481
482         talloc_free(mem_ctx);
483
484         return NT_STATUS_EA_CORRUPT_ERROR;
485 }
486
487 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
488                                    struct smbcli_state **c, 
489                                    struct torture_context *tctx,
490                                    const char *hostname, 
491                                    const char *sharename,
492                                    struct tevent_context *ev)
493 {
494         NTSTATUS status;
495
496         struct smbcli_options options;
497         struct smbcli_session_options session_options;
498
499         lp_smbcli_options(tctx->lp_ctx, &options);
500         lp_smbcli_session_options(tctx->lp_ctx, &session_options);
501
502         options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true);
503         options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true);
504
505         status = smbcli_full_connection(mem_ctx, c, hostname, 
506                                         lp_smb_ports(tctx->lp_ctx),
507                                         sharename, NULL,
508                                         lp_socket_options(tctx->lp_ctx),
509                                         cmdline_credentials, 
510                                         lp_resolve_context(tctx->lp_ctx),
511                                         ev, &options, &session_options,
512                                         lp_iconv_convenience(tctx->lp_ctx),
513                                         lp_gensec_settings(tctx, tctx->lp_ctx));
514         if (!NT_STATUS_IS_OK(status)) {
515                 printf("Failed to open connection - %s\n", nt_errstr(status));
516                 return false;
517         }
518
519         return true;
520 }
521
522 _PUBLIC_ bool torture_get_conn_index(int conn_index,
523                                      TALLOC_CTX *mem_ctx,
524                                      struct torture_context *tctx,
525                                      char **host, char **share)
526 {
527         char **unc_list = NULL;
528         int num_unc_names = 0;
529         const char *p;
530
531         (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
532         (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
533         
534         p = torture_setting_string(tctx, "unclist", NULL);
535         if (!p) {
536                 return true;
537         }
538
539         unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
540         if (!unc_list || num_unc_names <= 0) {
541                 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
542                 return false;
543         }
544
545         if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
546                               mem_ctx, host, share)) {
547                 DEBUG(0, ("Failed to parse UNC name %s\n",
548                           unc_list[conn_index % num_unc_names]));
549                 return false;
550         }
551
552         talloc_free(unc_list);
553         return true;
554 }
555
556
557
558 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
559                                          int conn_index,
560                                          struct torture_context *tctx,
561                                          struct tevent_context *ev)
562 {
563         char *host, *share;
564         bool ret;
565
566         if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
567                 return false;
568         }
569
570         ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
571         talloc_free(host);
572         talloc_free(share);
573
574         return ret;
575 }
576
577 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
578 {
579         return torture_open_connection_ev(c, conn_index, tctx, tctx->ev);
580 }
581
582
583
584 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
585 {
586         bool ret = true;
587         if (!c) return true;
588         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
589                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
590                 ret = false;
591         }
592         talloc_free(c);
593         return ret;
594 }
595
596
597 /* check if the server produced the expected error code */
598 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
599                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
600 {
601         NTSTATUS status;
602         
603         status = smbcli_nt_error(c->tree);
604         if (NT_STATUS_IS_DOS(status)) {
605                 int classnum, num;
606                 classnum = NT_STATUS_DOS_CLASS(status);
607                 num = NT_STATUS_DOS_CODE(status);
608                 if (eclass != classnum || ecode != num) {
609                         printf("unexpected error code %s\n", nt_errstr(status));
610                         printf(" expected %s or %s (at %s)\n", 
611                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
612                                nt_errstr(nterr), location);
613                         return false;
614                 }
615         } else {
616                 if (!NT_STATUS_EQUAL(nterr, status)) {
617                         printf("unexpected error code %s\n", nt_errstr(status));
618                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
619                         return false;
620                 }
621         }
622
623         return true;
624 }
625
626 static struct smbcli_state *current_cli;
627 static int procnum; /* records process count number when forking */
628
629 static void sigcont(int sig)
630 {
631 }
632
633 double torture_create_procs(struct torture_context *tctx, 
634                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
635 {
636         int i, status;
637         volatile pid_t *child_status;
638         volatile bool *child_status_out;
639         int synccount;
640         int tries = 8;
641         int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
642         double start_time_limit = 10 + (torture_nprocs * 1.5);
643         struct timeval tv;
644
645         *result = true;
646
647         synccount = 0;
648
649         signal(SIGCONT, sigcont);
650
651         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
652         if (!child_status) {
653                 printf("Failed to setup shared memory\n");
654                 return -1;
655         }
656
657         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
658         if (!child_status_out) {
659                 printf("Failed to setup result status shared memory\n");
660                 return -1;
661         }
662
663         for (i = 0; i < torture_nprocs; i++) {
664                 child_status[i] = 0;
665                 child_status_out[i] = true;
666         }
667
668         tv = timeval_current();
669
670         for (i=0;i<torture_nprocs;i++) {
671                 procnum = i;
672                 if (fork() == 0) {
673                         char *myname;
674
675                         pid_t mypid = getpid();
676                         srandom(((int)mypid) ^ ((int)time(NULL)));
677
678                         if (asprintf(&myname, "CLIENT%d", i) == -1) {
679                                 printf("asprintf failed\n");
680                                 return -1;
681                         }
682                         lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
683                         free(myname);
684
685
686                         while (1) {
687                                 if (torture_open_connection(&current_cli, tctx, i)) {
688                                         break;
689                                 }
690                                 if (tries-- == 0) {
691                                         printf("pid %d failed to start\n", (int)getpid());
692                                         _exit(1);
693                                 }
694                                 msleep(100);    
695                         }
696
697                         child_status[i] = getpid();
698
699                         pause();
700
701                         if (child_status[i]) {
702                                 printf("Child %d failed to start!\n", i);
703                                 child_status_out[i] = 1;
704                                 _exit(1);
705                         }
706
707                         child_status_out[i] = fn(tctx, current_cli, i);
708                         _exit(0);
709                 }
710         }
711
712         do {
713                 synccount = 0;
714                 for (i=0;i<torture_nprocs;i++) {
715                         if (child_status[i]) synccount++;
716                 }
717                 if (synccount == torture_nprocs) break;
718                 msleep(100);
719         } while (timeval_elapsed(&tv) < start_time_limit);
720
721         if (synccount != torture_nprocs) {
722                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
723                 *result = false;
724                 return timeval_elapsed(&tv);
725         }
726
727         printf("Starting %d clients\n", torture_nprocs);
728
729         /* start the client load */
730         tv = timeval_current();
731         for (i=0;i<torture_nprocs;i++) {
732                 child_status[i] = 0;
733         }
734
735         printf("%d clients started\n", torture_nprocs);
736
737         kill(0, SIGCONT);
738
739         for (i=0;i<torture_nprocs;i++) {
740                 int ret;
741                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
742                 if (ret == -1 || WEXITSTATUS(status) != 0) {
743                         *result = false;
744                 }
745         }
746
747         printf("\n");
748         
749         for (i=0;i<torture_nprocs;i++) {
750                 if (!child_status_out[i]) {
751                         *result = false;
752                 }
753         }
754         return timeval_elapsed(&tv);
755 }
756
757 static bool wrap_smb_multi_test(struct torture_context *torture,
758                                                                 struct torture_tcase *tcase,
759                                                                 struct torture_test *test)
760 {
761         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
762         bool result;
763
764         torture_create_procs(torture, fn, &result);
765
766         return result;
767 }
768
769 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
770                                                                         struct torture_suite *suite,
771                                                                         const char *name,
772                                                                         bool (*run) (struct torture_context *,
773                                                                                                  struct smbcli_state *,
774                                                                                                 int i))
775 {
776         struct torture_test *test; 
777         struct torture_tcase *tcase;
778         
779         tcase = torture_suite_add_tcase(suite, name);
780
781         test = talloc(tcase, struct torture_test);
782
783         test->name = talloc_strdup(test, name);
784         test->description = NULL;
785         test->run = wrap_smb_multi_test;
786         test->fn = run;
787         test->dangerous = false;
788
789         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
790
791         return test;
792
793 }
794
795 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
796                                                                         struct torture_tcase *tcase,
797                                                                         struct torture_test *test)
798 {
799         bool (*fn) (struct torture_context *, struct smbcli_state *,
800                                 struct smbcli_state *);
801         bool ret;
802
803         struct smbcli_state *cli1, *cli2;
804
805         if (!torture_open_connection(&cli1, torture_ctx, 0) || 
806                 !torture_open_connection(&cli2, torture_ctx, 1))
807                 return false;
808
809         fn = test->fn;
810
811         ret = fn(torture_ctx, cli1, cli2);
812
813         talloc_free(cli1);
814         talloc_free(cli2);
815
816         return ret;
817 }
818
819
820
821 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
822                                                                         struct torture_suite *suite,
823                                                                         const char *name,
824                                                                         bool (*run) (struct torture_context *,
825                                                                                                 struct smbcli_state *,
826                                                                                                 struct smbcli_state *))
827 {
828         struct torture_test *test; 
829         struct torture_tcase *tcase;
830         
831         tcase = torture_suite_add_tcase(suite, name);
832
833         test = talloc(tcase, struct torture_test);
834
835         test->name = talloc_strdup(test, name);
836         test->description = NULL;
837         test->run = wrap_simple_2smb_test;
838         test->fn = run;
839         test->dangerous = false;
840
841         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
842
843         return test;
844
845 }
846
847 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
848                                                                         struct torture_tcase *tcase,
849                                                                         struct torture_test *test)
850 {
851         bool (*fn) (struct torture_context *, struct smbcli_state *);
852         bool ret;
853
854         struct smbcli_state *cli1;
855
856         if (!torture_open_connection(&cli1, torture_ctx, 0))
857                 return false;
858
859         fn = test->fn;
860
861         ret = fn(torture_ctx, cli1);
862
863         talloc_free(cli1);
864
865         return ret;
866 }
867
868 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
869                                 struct torture_suite *suite,
870                                 const char *name,
871                                 bool (*run) (struct torture_context *, struct smbcli_state *))
872 {
873         struct torture_test *test; 
874         struct torture_tcase *tcase;
875         
876         tcase = torture_suite_add_tcase(suite, name);
877
878         test = talloc(tcase, struct torture_test);
879
880         test->name = talloc_strdup(test, name);
881         test->description = NULL;
882         test->run = wrap_simple_1smb_test;
883         test->fn = run;
884         test->dangerous = false;
885
886         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
887
888         return test;
889 }
890
891
892 NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx,
893                              struct smbcli_session *session,
894                              const char *sharename,
895                              struct smbcli_tree **res)
896 {
897         union smb_tcon tcon;
898         struct smbcli_tree *result;
899         TALLOC_CTX *tmp_ctx;
900         NTSTATUS status;
901
902         if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) {
903                 return NT_STATUS_NO_MEMORY;
904         }
905
906         result = smbcli_tree_init(session, tmp_ctx, false);
907         if (result == NULL) {
908                 talloc_free(tmp_ctx);
909                 return NT_STATUS_NO_MEMORY;
910         }
911
912         tcon.generic.level = RAW_TCON_TCONX;
913         tcon.tconx.in.flags = 0;
914
915         /* Ignore share mode security here */
916         tcon.tconx.in.password = data_blob(NULL, 0);
917         tcon.tconx.in.path = sharename;
918         tcon.tconx.in.device = "?????";
919
920         status = smb_raw_tcon(result, tmp_ctx, &tcon);
921         if (!NT_STATUS_IS_OK(status)) {
922                 talloc_free(tmp_ctx);
923                 return status;
924         }
925
926         result->tid = tcon.tconx.out.tid;
927         *res = talloc_steal(mem_ctx, result);
928         talloc_free(tmp_ctx);
929         return NT_STATUS_OK;
930 }
931
932 /* 
933    a wrapper around smblsa_sid_check_privilege, that tries to take
934    account of the fact that the lsa privileges calls don't expand
935    group memberships, using an explicit check for administrator. There
936    must be a better way ...
937  */
938 NTSTATUS torture_check_privilege(struct smbcli_state *cli, 
939                                  const char *sid_str,
940                                  const char *privilege)
941 {
942         struct dom_sid *sid;
943         TALLOC_CTX *tmp_ctx = talloc_new(cli);
944         uint32_t rid;
945         NTSTATUS status;
946
947         sid = dom_sid_parse_talloc(tmp_ctx, sid_str);
948         if (sid == NULL) {
949                 talloc_free(tmp_ctx);
950                 return NT_STATUS_INVALID_SID;
951         }
952
953         status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
954         NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx);
955
956         if (rid == DOMAIN_RID_ADMINISTRATOR) {
957                 /* assume the administrator has them all */
958                 return NT_STATUS_OK;
959         }
960
961         talloc_free(tmp_ctx);
962
963         return smblsa_sid_check_privilege(cli, sid_str, privilege);
964 }