lib: Give lib/util/util_file.c its own header file
[samba.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/util/util_file.h"
23 #include "lib/cmdline/cmdline.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "../libcli/smb/smb_constants.h"
27 #include "libcli/libcli.h"
28 #include "system/filesys.h"
29 #include "system/shmem.h"
30 #include "system/wait.h"
31 #include "system/time.h"
32 #include "torture/torture.h"
33 #include "../lib/util/dlinklist.h"
34 #include "libcli/resolve/resolve.h"
35 #include "param/param.h"
36 #include "libcli/security/security.h"
37 #include "libcli/smb2/smb2.h"
38 #include "libcli/util/clilsa.h"
39 #include "torture/util.h"
40 #include "libcli/smb/smbXcli_base.h"
41 #include "auth/credentials/credentials.h"
42 #include "auth/credentials/credentials_krb5.h"
43
44 /**
45   setup a directory ready for a test
46 */
47 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
48 {
49         smb_raw_exit(cli->session);
50         if (smbcli_deltree(cli->tree, dname) == -1 ||
51             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
52                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
53                 return false;
54         }
55         return true;
56 }
57
58 /*
59   create a directory, returning a handle to it
60 */
61 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
62 {
63         NTSTATUS status;
64         union smb_open io;
65         TALLOC_CTX *mem_ctx;
66
67         mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
68
69         io.generic.level = RAW_OPEN_NTCREATEX;
70         io.ntcreatex.in.root_fid.fnum = 0;
71         io.ntcreatex.in.flags = 0;
72         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
73         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
74         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
75         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
76         io.ntcreatex.in.alloc_size = 0;
77         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
78         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
79         io.ntcreatex.in.security_flags = 0;
80         io.ntcreatex.in.fname = dname;
81
82         status = smb_raw_open(tree, mem_ctx, &io);
83         talloc_free(mem_ctx);
84
85         if (NT_STATUS_IS_OK(status)) {
86                 *fnum = io.ntcreatex.out.file.fnum;
87         }
88
89         return status;
90 }
91
92
93 /**
94   sometimes we need a fairly complex file to work with, so we can test
95   all possible attributes. 
96 */
97 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
98 {
99         int fnum;
100         char buf[7] = "abc";
101         union smb_setfileinfo setfile;
102         union smb_fileinfo fileinfo;
103         time_t t = (time(NULL) & ~1);
104         NTSTATUS status;
105
106         smbcli_unlink(cli->tree, fname);
107         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
108                                      SEC_RIGHTS_FILE_ALL,
109                                      FILE_ATTRIBUTE_NORMAL,
110                                      NTCREATEX_SHARE_ACCESS_DELETE|
111                                      NTCREATEX_SHARE_ACCESS_READ|
112                                      NTCREATEX_SHARE_ACCESS_WRITE, 
113                                      NTCREATEX_DISP_OVERWRITE_IF,
114                                      0, 0);
115         if (fnum == -1) return -1;
116
117         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
118
119         if (strchr(fname, ':') == NULL) {
120                 /* setup some EAs */
121                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
122                 setfile.generic.in.file.fnum = fnum;
123                 setfile.ea_set.in.num_eas = 2;  
124                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
125                 setfile.ea_set.in.eas[0].flags = 0;
126                 setfile.ea_set.in.eas[0].name.s = "EAONE";
127                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
128                 setfile.ea_set.in.eas[1].flags = 0;
129                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
130                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
131                 status = smb_raw_setfileinfo(cli->tree, &setfile);
132                 if (!NT_STATUS_IS_OK(status)) {
133                         printf("Failed to setup EAs\n");
134                 }
135         }
136
137         /* make sure all the timestamps aren't the same */
138         ZERO_STRUCT(setfile);
139         setfile.generic.level = RAW_SFILEINFO_BASIC_INFO;
140         setfile.generic.in.file.fnum = fnum;
141
142         unix_to_nt_time(&setfile.basic_info.in.create_time,
143             t + 9*30*24*60*60);
144         unix_to_nt_time(&setfile.basic_info.in.access_time,
145             t + 6*30*24*60*60);
146         unix_to_nt_time(&setfile.basic_info.in.write_time,
147             t + 3*30*24*60*60);
148
149         status = smb_raw_setfileinfo(cli->tree, &setfile);
150         if (!NT_STATUS_IS_OK(status)) {
151                 printf("Failed to setup file times - %s\n", nt_errstr(status));
152         }
153
154         /* make sure all the timestamps aren't the same */
155         fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO;
156         fileinfo.generic.in.file.fnum = fnum;
157
158         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
159         if (!NT_STATUS_IS_OK(status)) {
160                 printf("Failed to query file times - %s\n", nt_errstr(status));
161         }
162
163         if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) {
164                 printf("create_time not setup correctly\n");
165         }
166         if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) {
167                 printf("access_time not setup correctly\n");
168         }
169         if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) {
170                 printf("write_time not setup correctly\n");
171         }
172
173         return fnum;
174 }
175
176
177 /*
178   sometimes we need a fairly complex directory to work with, so we can test
179   all possible attributes. 
180 */
181 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
182 {
183         int fnum;
184         union smb_setfileinfo setfile;
185         union smb_fileinfo fileinfo;
186         time_t t = (time(NULL) & ~1);
187         NTSTATUS status;
188
189         smbcli_deltree(cli->tree, dname);
190         fnum = smbcli_nt_create_full(cli->tree, dname, 0, 
191                                      SEC_RIGHTS_DIR_ALL,
192                                      FILE_ATTRIBUTE_DIRECTORY,
193                                      NTCREATEX_SHARE_ACCESS_READ|
194                                      NTCREATEX_SHARE_ACCESS_WRITE, 
195                                      NTCREATEX_DISP_OPEN_IF,
196                                      NTCREATEX_OPTIONS_DIRECTORY, 0);
197         if (fnum == -1) return -1;
198
199         if (strchr(dname, ':') == NULL) {
200                 /* setup some EAs */
201                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
202                 setfile.generic.in.file.fnum = fnum;
203                 setfile.ea_set.in.num_eas = 2;  
204                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
205                 setfile.ea_set.in.eas[0].flags = 0;
206                 setfile.ea_set.in.eas[0].name.s = "EAONE";
207                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
208                 setfile.ea_set.in.eas[1].flags = 0;
209                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
210                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
211                 status = smb_raw_setfileinfo(cli->tree, &setfile);
212                 if (!NT_STATUS_IS_OK(status)) {
213                         printf("Failed to setup EAs\n");
214                 }
215         }
216
217         /* make sure all the timestamps aren't the same */
218         ZERO_STRUCT(setfile);
219         setfile.generic.level = RAW_SFILEINFO_BASIC_INFO;
220         setfile.generic.in.file.fnum = fnum;
221
222         unix_to_nt_time(&setfile.basic_info.in.create_time,
223             t + 9*30*24*60*60);
224         unix_to_nt_time(&setfile.basic_info.in.access_time,
225             t + 6*30*24*60*60);
226         unix_to_nt_time(&setfile.basic_info.in.write_time,
227             t + 3*30*24*60*60);
228
229         status = smb_raw_setfileinfo(cli->tree, &setfile);
230         if (!NT_STATUS_IS_OK(status)) {
231                 printf("Failed to setup file times - %s\n", nt_errstr(status));
232         }
233
234         /* make sure all the timestamps aren't the same */
235         fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO;
236         fileinfo.generic.in.file.fnum = fnum;
237
238         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
239         if (!NT_STATUS_IS_OK(status)) {
240                 printf("Failed to query file times - %s\n", nt_errstr(status));
241         }
242
243         if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) {
244                 printf("create_time not setup correctly\n");
245         }
246         if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) {
247                 printf("access_time not setup correctly\n");
248         }
249         if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) {
250                 printf("write_time not setup correctly\n");
251         }
252
253         return fnum;
254 }
255
256 /**
257   check that a wire string matches the flags specified 
258   not 100% accurate, but close enough for testing
259 */
260 bool wire_bad_flags(struct smb_wire_string *str, int flags, 
261                     struct smbcli_transport *transport)
262 {
263         bool server_unicode;
264         int len;
265         if (!str || !str->s) return true;
266         len = strlen(str->s);
267         if (flags & STR_TERMINATE) len++;
268
269         server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
270         if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) {
271                 server_unicode = false;
272         }
273
274         if ((flags & STR_UNICODE) || server_unicode) {
275                 len *= 2;
276         } else if (flags & STR_TERMINATE_ASCII) {
277                 len++;
278         }
279         if (str->private_length != len) {
280                 printf("Expected wire_length %d but got %d for '%s'\n", 
281                        len, str->private_length, str->s);
282                 return true;
283         }
284         return false;
285 }
286
287 /*
288   dump a all_info QFILEINFO structure
289 */
290 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
291 {
292         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
293         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
294         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
295         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
296         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
297         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
298         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
299         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
300         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
301         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
302         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
303         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
304 }
305
306 /*
307   dump file info by name
308 */
309 void torture_all_info(struct smbcli_tree *tree, const char *fname)
310 {
311         TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
312         union smb_fileinfo finfo;
313         NTSTATUS status;
314
315         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
316         finfo.generic.in.file.path = fname;
317         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
318         if (!NT_STATUS_IS_OK(status)) {
319                 d_printf("%s - %s\n", fname, nt_errstr(status));
320                 return;
321         }
322
323         d_printf("%s:\n", fname);
324         dump_all_info(mem_ctx, &finfo);
325         talloc_free(mem_ctx);
326 }
327
328
329 /*
330   set a attribute on a file
331 */
332 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
333 {
334         union smb_setfileinfo sfinfo;
335         NTSTATUS status;
336
337         ZERO_STRUCT(sfinfo.basic_info.in);
338         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
339         sfinfo.basic_info.in.file.path = fname;
340         sfinfo.basic_info.in.attrib = attrib;
341         status = smb_raw_setpathinfo(tree, &sfinfo);
342         return NT_STATUS_IS_OK(status);
343 }
344
345
346 /*
347   set a file descriptor as sparse
348 */
349 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
350 {
351         union smb_ioctl nt;
352         NTSTATUS status;
353         TALLOC_CTX *mem_ctx;
354
355         mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
356         if (!mem_ctx) {
357                 return NT_STATUS_NO_MEMORY;
358         }
359
360         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
361         nt.ntioctl.in.function = FSCTL_SET_SPARSE;
362         nt.ntioctl.in.file.fnum = fnum;
363         nt.ntioctl.in.fsctl = true;
364         nt.ntioctl.in.filter = 0;
365         nt.ntioctl.in.max_data = 0;
366         nt.ntioctl.in.blob = data_blob(NULL, 0);
367
368         status = smb_raw_ioctl(tree, mem_ctx, &nt);
369
370         talloc_free(mem_ctx);
371
372         return status;
373 }
374
375 /*
376   check that an EA has the right value 
377 */
378 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
379                           const char *fname, const char *eaname, const char *value)
380 {
381         union smb_fileinfo info;
382         NTSTATUS status;
383         struct ea_name ea;
384         TALLOC_CTX *mem_ctx = talloc_new(cli);
385
386         info.ea_list.level = RAW_FILEINFO_EA_LIST;
387         info.ea_list.in.file.path = fname;
388         info.ea_list.in.num_names = 1;
389         info.ea_list.in.ea_names = &ea;
390
391         ea.name.s = eaname;
392
393         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
394         if (!NT_STATUS_IS_OK(status)) {
395                 talloc_free(mem_ctx);
396                 return status;
397         }
398
399         if (info.ea_list.out.num_eas != 1) {
400                 printf("Expected 1 ea in ea_list\n");
401                 talloc_free(mem_ctx);
402                 return NT_STATUS_EA_CORRUPT_ERROR;
403         }
404
405         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
406                 printf("Expected ea '%s' not '%s' in ea_list\n",
407                        eaname, info.ea_list.out.eas[0].name.s);
408                 talloc_free(mem_ctx);
409                 return NT_STATUS_EA_CORRUPT_ERROR;
410         }
411
412         if (value == NULL) {
413                 if (info.ea_list.out.eas[0].value.length != 0) {
414                         printf("Expected zero length ea for %s\n", eaname);
415                         talloc_free(mem_ctx);
416                         return NT_STATUS_EA_CORRUPT_ERROR;
417                 }
418                 talloc_free(mem_ctx);
419                 return NT_STATUS_OK;
420         }
421
422         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
423             memcmp(value, info.ea_list.out.eas[0].value.data,
424                    info.ea_list.out.eas[0].value.length) == 0) {
425                 talloc_free(mem_ctx);
426                 return NT_STATUS_OK;
427         }
428
429         printf("Expected value '%s' not '%*.*s' for ea %s\n",
430                value, 
431                (int)info.ea_list.out.eas[0].value.length,
432                (int)info.ea_list.out.eas[0].value.length,
433                info.ea_list.out.eas[0].value.data,
434                eaname);
435
436         talloc_free(mem_ctx);
437
438         return NT_STATUS_EA_CORRUPT_ERROR;
439 }
440
441 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
442                                    struct smbcli_state **c, 
443                                    struct torture_context *tctx,
444                                    const char *hostname, 
445                                    const char *sharename,
446                                    struct tevent_context *ev)
447 {
448         NTSTATUS status;
449
450         struct smbcli_options options;
451         struct smbcli_session_options session_options;
452
453         lpcfg_smbcli_options(tctx->lp_ctx, &options);
454         lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
455
456         options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true);
457         options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true);
458
459         status = smbcli_full_connection(mem_ctx, c, hostname, 
460                                         lpcfg_smb_ports(tctx->lp_ctx),
461                                         sharename, NULL,
462                                         lpcfg_socket_options(tctx->lp_ctx),
463                                         samba_cmdline_get_creds(),
464                                         lpcfg_resolve_context(tctx->lp_ctx),
465                                         ev, &options, &session_options,
466                                         lpcfg_gensec_settings(tctx, tctx->lp_ctx));
467         if (!NT_STATUS_IS_OK(status)) {
468                 printf("Failed to open connection - %s\n", nt_errstr(status));
469                 return false;
470         }
471
472         return true;
473 }
474
475 _PUBLIC_ bool torture_get_conn_index(int conn_index,
476                                      TALLOC_CTX *mem_ctx,
477                                      struct torture_context *tctx,
478                                      char **host, char **share)
479 {
480         char **unc_list = NULL;
481         int num_unc_names = 0;
482         const char *p;
483
484         (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
485         (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
486         
487         p = torture_setting_string(tctx, "unclist", NULL);
488         if (!p) {
489                 return true;
490         }
491
492         unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
493         if (!unc_list || num_unc_names <= 0) {
494                 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
495                 return false;
496         }
497
498         p = unc_list[conn_index % num_unc_names];
499         if (p[0] != '/' && p[0] != '\\') {
500                 /* allow UNC lists of hosts */
501                 (*host) = talloc_strdup(mem_ctx, p);
502         } else if (!smbcli_parse_unc(p, mem_ctx, host, share)) {
503                 DEBUG(0, ("Failed to parse UNC name %s\n",
504                           unc_list[conn_index % num_unc_names]));
505                 return false;
506         }
507
508         talloc_free(unc_list);
509         return true;
510 }
511
512
513
514 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
515                                          int conn_index,
516                                          struct torture_context *tctx,
517                                          struct tevent_context *ev)
518 {
519         char *host, *share;
520         bool ret;
521
522         if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
523                 return false;
524         }
525
526         ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
527         talloc_free(host);
528         talloc_free(share);
529
530         return ret;
531 }
532
533 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
534 {
535         return torture_open_connection_ev(c, conn_index, tctx, tctx->ev);
536 }
537
538
539
540 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
541 {
542         bool ret = true;
543         if (!c) return true;
544         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
545                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
546                 ret = false;
547         }
548         talloc_free(c);
549         return ret;
550 }
551
552
553 /* check if the server produced the expected error code */
554 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
555                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
556 {
557         NTSTATUS status;
558         
559         status = smbcli_nt_error(c->tree);
560         if (NT_STATUS_IS_DOS(status)) {
561                 int classnum, num;
562                 classnum = NT_STATUS_DOS_CLASS(status);
563                 num = NT_STATUS_DOS_CODE(status);
564                 if (eclass != classnum || ecode != num) {
565                         printf("unexpected error code %s\n", nt_errstr(status));
566                         printf(" expected %s or %s (at %s)\n", 
567                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
568                                nt_errstr(nterr), location);
569                         return false;
570                 }
571         } else {
572                 if (!NT_STATUS_EQUAL(nterr, status)) {
573                         printf("unexpected error code %s\n", nt_errstr(status));
574                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
575                         return false;
576                 }
577         }
578
579         return true;
580 }
581
582 static struct smbcli_state *current_cli;
583 static int procnum; /* records process count number when forking */
584
585 static void sigcont(int sig)
586 {
587 }
588
589 struct child_status {
590         pid_t pid;
591         bool start;
592         enum torture_result result;
593         char reason[1024];
594 };
595
596 double torture_create_procs(struct torture_context *tctx,
597         bool (*fn)(struct torture_context *, struct smbcli_state *, int),
598         bool *result)
599 {
600         int status;
601         size_t i;
602         struct child_status *child_status;
603         size_t synccount;
604         size_t tries = 8;
605         size_t torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
606         double start_time_limit = 10 + (torture_nprocs * 1.5);
607         struct timeval tv;
608
609         *result = true;
610
611         synccount = 0;
612
613         signal(SIGCONT, sigcont);
614
615         child_status = (struct child_status *)anonymous_shared_allocate(
616                                 sizeof(struct child_status)*torture_nprocs);
617         if (child_status == NULL) {
618                 printf("Failed to setup shared memory\n");
619                 return -1;
620         }
621
622         for (i = 0; i < torture_nprocs; i++) {
623                 ZERO_STRUCT(child_status[i]);
624         }
625
626         tv = timeval_current();
627
628         for (i=0;i<torture_nprocs;i++) {
629                 procnum = i;
630                 if (fork() == 0) {
631                         char *myname;
632                         bool ok;
633
634                         pid_t mypid = getpid();
635                         srandom(((int)mypid) ^ ((int)time(NULL)));
636
637                         if (asprintf(&myname, "CLIENT%zu", i) == -1) {
638                                 printf("asprintf failed\n");
639                                 return -1;
640                         }
641                         lpcfg_set_cmdline(tctx->lp_ctx, "netbios name", myname);
642                         free(myname);
643
644
645                         while (1) {
646                                 if (torture_open_connection(&current_cli, tctx, i)) {
647                                         break;
648                                 }
649                                 if (tries-- == 0) {
650                                         printf("pid %d failed to start\n", (int)getpid());
651                                         _exit(1);
652                                 }
653                                 smb_msleep(100);        
654                         }
655
656                         child_status[i].pid = getpid();
657
658                         pause();
659
660                         if (!child_status[i].start) {
661                                 child_status[i].result = TORTURE_ERROR;
662                                 printf("Child %zu failed to start!\n", i);
663                                 _exit(1);
664                         }
665
666                         ok = fn(tctx, current_cli, i);
667                         if (!ok) {
668                                 if (tctx->last_result == TORTURE_OK) {
669                                         torture_result(tctx, TORTURE_ERROR,
670                                                 "unknown error: missing "
671                                                 "torture_result call?\n");
672                                 }
673
674                                 child_status[i].result = tctx->last_result;
675
676                                 if (strlen(tctx->last_reason) > 1023) {
677                                         /* note: reason already contains \n */
678                                         torture_comment(tctx,
679                                                 "child %zu (pid %u) failed: %s",
680                                                 i,
681                                                 (unsigned)child_status[i].pid,
682                                                 tctx->last_reason);
683                                 }
684
685                                 snprintf(child_status[i].reason,
686                                          1024, "child %zu (pid %u) failed: %s",
687                                          i, (unsigned)child_status[i].pid,
688                                          tctx->last_reason);
689                                 /* ensure proper "\n\0" termination: */
690                                 if (child_status[i].reason[1022] != '\0') {
691                                         child_status[i].reason[1022] = '\n';
692                                         child_status[i].reason[1023] = '\0';
693                                 }
694                         }
695                         _exit(0);
696                 }
697         }
698
699         do {
700                 synccount = 0;
701                 for (i=0;i<torture_nprocs;i++) {
702                         if (child_status[i].pid != 0) {
703                                 synccount++;
704                         }
705                 }
706                 if (synccount == torture_nprocs) {
707                         break;
708                 }
709                 smb_msleep(100);
710         } while (timeval_elapsed(&tv) < start_time_limit);
711
712         if (synccount != torture_nprocs) {
713                 printf("FAILED TO START %zu CLIENTS (started %zu)\n", torture_nprocs, synccount);
714
715                 /* cleanup child processes */
716                 for (i = 0; i < torture_nprocs; i++) {
717                         if (child_status[i].pid != 0) {
718                                 kill(child_status[i].pid, SIGTERM);
719                         }
720                 }
721
722                 *result = false;
723                 return timeval_elapsed(&tv);
724         }
725
726         printf("Starting %zu clients\n", torture_nprocs);
727
728         /* start the client load */
729         tv = timeval_current();
730         for (i=0;i<torture_nprocs;i++) {
731                 child_status[i].start = true;
732         }
733
734         printf("%zu clients started\n", torture_nprocs);
735
736         kill(0, SIGCONT);
737
738         for (i=0;i<torture_nprocs;i++) {
739                 int ret;
740                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
741                 if (ret == -1 || WEXITSTATUS(status) != 0) {
742                         *result = false;
743                 }
744         }
745
746         printf("\n");
747
748         for (i=0;i<torture_nprocs;i++) {
749                 if (child_status[i].result != TORTURE_OK) {
750                         *result = false;
751                         torture_result(tctx, child_status[i].result,
752                                        "%s", child_status[i].reason);
753                 }
754         }
755
756         return timeval_elapsed(&tv);
757 }
758
759 static bool wrap_smb_multi_test(struct torture_context *torture,
760                                                                 struct torture_tcase *tcase,
761                                                                 struct torture_test *test)
762 {
763         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
764         bool result;
765
766         torture_create_procs(torture, fn, &result);
767
768         return result;
769 }
770
771 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
772                                                                         struct torture_suite *suite,
773                                                                         const char *name,
774                                                                         bool (*run) (struct torture_context *,
775                                                                                                  struct smbcli_state *,
776                                                                                                 int i))
777 {
778         struct torture_test *test; 
779         struct torture_tcase *tcase;
780         
781         tcase = torture_suite_add_tcase(suite, name);
782
783         test = talloc(tcase, struct torture_test);
784
785         test->name = talloc_strdup(test, name);
786         test->description = NULL;
787         test->run = wrap_smb_multi_test;
788         test->fn = run;
789         test->dangerous = false;
790
791         DLIST_ADD_END(tcase->tests, test);
792
793         return test;
794
795 }
796
797 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
798                                                                         struct torture_tcase *tcase,
799                                                                         struct torture_test *test)
800 {
801         bool (*fn) (struct torture_context *, struct smbcli_state *,
802                                 struct smbcli_state *);
803         bool ret = true;
804
805         struct smbcli_state *cli1 = NULL, *cli2 = NULL;
806
807         torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection");
808         torture_assert_goto(torture_ctx, torture_open_connection(&cli2, torture_ctx, 1), ret, fail, "Failed to open connection");
809
810         fn = test->fn;
811
812         ret = fn(torture_ctx, cli1, cli2);
813 fail:
814         talloc_free(cli1);
815         talloc_free(cli2);
816
817         return ret;
818 }
819
820
821
822 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
823                                                                         struct torture_suite *suite,
824                                                                         const char *name,
825                                                                         bool (*run) (struct torture_context *,
826                                                                                                 struct smbcli_state *,
827                                                                                                 struct smbcli_state *))
828 {
829         struct torture_test *test; 
830         struct torture_tcase *tcase;
831         
832         tcase = torture_suite_add_tcase(suite, name);
833
834         test = talloc(tcase, struct torture_test);
835
836         test->name = talloc_strdup(test, name);
837         test->description = NULL;
838         test->run = wrap_simple_2smb_test;
839         test->fn = run;
840         test->dangerous = false;
841
842         DLIST_ADD_END(tcase->tests, test);
843
844         return test;
845
846 }
847
848 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
849                                                                         struct torture_tcase *tcase,
850                                                                         struct torture_test *test)
851 {
852         bool (*fn) (struct torture_context *, struct smbcli_state *);
853         bool ret = true;
854
855         struct smbcli_state *cli1 = NULL;
856
857         torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection");
858
859         fn = test->fn;
860
861         ret = fn(torture_ctx, cli1);
862 fail:
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);
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 = TCONX_FLAG_EXTENDED_RESPONSE;
914         tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
915
916         /* Ignore share mode security here */
917         tcon.tconx.in.password = data_blob(NULL, 0);
918         tcon.tconx.in.path = sharename;
919         tcon.tconx.in.device = "?????";
920
921         status = smb_raw_tcon(result, tmp_ctx, &tcon);
922         if (!NT_STATUS_IS_OK(status)) {
923                 talloc_free(tmp_ctx);
924                 return status;
925         }
926
927         result->tid = tcon.tconx.out.tid;
928
929         if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
930                 smb1cli_session_protect_session_key(result->session->smbXcli);
931         }
932
933         *res = talloc_steal(mem_ctx, result);
934         talloc_free(tmp_ctx);
935         return NT_STATUS_OK;
936 }
937
938 /* 
939    a wrapper around smblsa_sid_check_privilege, that tries to take
940    account of the fact that the lsa privileges calls don't expand
941    group memberships, using an explicit check for administrator. There
942    must be a better way ...
943  */
944 NTSTATUS torture_check_privilege(struct smbcli_state *cli, 
945                                  const char *sid_str,
946                                  const char *privilege)
947 {
948         struct dom_sid *sid;
949         TALLOC_CTX *tmp_ctx = talloc_new(cli);
950         uint32_t rid;
951         NTSTATUS status;
952
953         sid = dom_sid_parse_talloc(tmp_ctx, sid_str);
954         if (sid == NULL) {
955                 talloc_free(tmp_ctx);
956                 return NT_STATUS_INVALID_SID;
957         }
958
959         status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
960         if (!NT_STATUS_IS_OK(status)) {
961                 TALLOC_FREE(tmp_ctx);
962                 return status;
963         }
964
965         if (rid == DOMAIN_RID_ADMINISTRATOR) {
966                 /* assume the administrator has them all */
967                 return NT_STATUS_OK;
968         }
969
970         talloc_free(tmp_ctx);
971
972         return smblsa_sid_check_privilege(cli, sid_str, privilege);
973 }
974
975 /*
976  * Use this to pass a 2nd user:
977  *
978  * --option='torture:user2name=user2'
979  * --option='torture:user2domain=domain2'
980  * --option='torture:user2password=password2'
981  */
982 struct cli_credentials *torture_user2_credentials(struct torture_context *tctx,
983                                                   TALLOC_CTX *mem_ctx)
984 {
985         struct cli_credentials *credentials1 = samba_cmdline_get_creds();
986         const char *user1domain = cli_credentials_get_domain(credentials1);
987         const char *user2name = torture_setting_string(tctx, "user2name", NULL);
988         const char *user2domain = torture_setting_string(tctx, "user2domain", user1domain);
989         const char *user2password = torture_setting_string(tctx, "user2password", NULL);
990         struct cli_credentials *credentials2 = NULL;
991
992         credentials2 = cli_credentials_shallow_copy(mem_ctx, credentials1);
993         if (credentials2 == NULL) {
994                 torture_comment(tctx,
995                                 "%s: cli_credentials_shallow_copy() failed\n",
996                                 __func__);
997                 return NULL;
998         }
999         if (user2name != NULL) {
1000                 torture_comment(tctx,
1001                                 "Using "
1002                                 "'torture:user2name'='%s' "
1003                                 "'torture:user2domain'='%s' "
1004                                 "'torture:user2password'='REDACTED'",
1005                                 user2name,
1006                                 user2domain);
1007                 cli_credentials_set_username(credentials2, user2name, CRED_SPECIFIED);
1008                 cli_credentials_set_domain(credentials2, user2domain, CRED_SPECIFIED);
1009                 cli_credentials_set_password(credentials2, user2password, CRED_SPECIFIED);
1010         } else {
1011                 torture_comment(tctx,
1012                                 "Fallback to anonymous for "
1013                                 "'torture:user2name'=NULL "
1014                                 "'torture:user2domain'='%s' "
1015                                 "'torture:user2password'='REDACTED'",
1016                                 user2domain);
1017                 cli_credentials_set_anonymous(credentials2);
1018         }
1019
1020         return credentials2;
1021 }