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