RAW-LOCK: fix the run against w2k3, w2k8, win7rc and samba3/4
[ira/wip.git] / source4 / torture / smbtorture.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006-2008
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 "system/time.h"
24 #include "system/wait.h"
25 #include "system/filesys.h"
26 #include "system/readline.h"
27 #include "lib/smbreadline/smbreadline.h"
28 #include "libcli/libcli.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/events/events.h"
31 #include "dynconfig/dynconfig.h"
32
33 #include "torture/smbtorture.h"
34 #include "../lib/util/dlinklist.h"
35 #include "librpc/rpc/dcerpc.h"
36 #include "auth/gensec/gensec.h"
37 #include "param/param.h"
38
39 #include "auth/credentials/credentials.h"
40
41 static bool run_matching(struct torture_context *torture,
42                                                  const char *prefix, 
43                                                  const char *expr,
44                                                  struct torture_suite *suite,
45                                                  bool *matched)
46 {
47         bool ret = true;
48
49         if (suite == NULL) {
50                 struct torture_suite *o;
51
52                 for (o = (torture_root == NULL?NULL:torture_root->children); o; o = o->next) {
53                         if (gen_fnmatch(expr, o->name) == 0) {
54                                 *matched = true;
55                                 reload_charcnv(torture->lp_ctx);
56                                 ret &= torture_run_suite(torture, o);
57                                 continue;
58                         }
59
60                         ret &= run_matching(torture, o->name, expr, o, matched);
61                 }
62         } else {
63                 char *name;
64                 struct torture_suite *c;
65                 struct torture_tcase *t;
66
67                 for (c = suite->children; c; c = c->next) {
68                         asprintf(&name, "%s-%s", prefix, c->name);
69
70                         if (gen_fnmatch(expr, name) == 0) {
71                                 *matched = true;
72                                 reload_charcnv(torture->lp_ctx);
73                                 torture->active_testname = talloc_strdup(torture, prefix);
74                                 ret &= torture_run_suite(torture, c);
75                                 free(name);
76                                 continue;
77                         }
78                         
79                         ret &= run_matching(torture, name, expr, c, matched);
80
81                         free(name);
82                 }
83
84                 for (t = suite->testcases; t; t = t->next) {
85                         asprintf(&name, "%s-%s", prefix, t->name);
86                         if (gen_fnmatch(expr, name) == 0) {
87                                 *matched = true;
88                                 reload_charcnv(torture->lp_ctx);
89                                 torture->active_testname = talloc_strdup(torture, prefix);
90                                 ret &= torture_run_tcase(torture, t);
91                                 talloc_free(torture->active_testname);
92                         }
93                         free(name);
94                 }
95         }
96
97         return ret;
98 }
99
100 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
101
102 /****************************************************************************
103 run a specified test or "ALL"
104 ****************************************************************************/
105 static bool run_test(struct torture_context *torture, const char *name)
106 {
107         bool ret = true;
108         bool matched = false;
109         struct torture_suite *o;
110
111         if (strequal(name, "ALL")) {
112                 for (o = torture_root->children; o; o = o->next) {
113                         ret &= torture_run_suite(torture, o);
114                 }
115                 return ret;
116         }
117
118         ret = run_matching(torture, NULL, name, NULL, &matched);
119
120         if (!matched) {
121                 printf("Unknown torture operation '%s'\n", name);
122                 return false;
123         }
124
125         return ret;
126 }
127
128 static bool parse_target(struct loadparm_context *lp_ctx, const char *target)
129 {
130         char *host = NULL, *share = NULL;
131         struct dcerpc_binding *binding_struct;
132         NTSTATUS status;
133
134         /* see if its a RPC transport specifier */
135         if (!smbcli_parse_unc(target, NULL, &host, &share)) {
136                 status = dcerpc_parse_binding(talloc_autofree_context(), target, &binding_struct);
137                 if (NT_STATUS_IS_ERR(status)) {
138                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", target);
139                         return false;
140                 }
141                 lp_set_cmdline(lp_ctx, "torture:host", binding_struct->host);
142                 if (lp_parm_string(lp_ctx, NULL, "torture", "share") == NULL)
143                         lp_set_cmdline(lp_ctx, "torture:share", "IPC$");
144                 lp_set_cmdline(lp_ctx, "torture:binding", target);
145         } else {
146                 lp_set_cmdline(lp_ctx, "torture:host", host);
147                 lp_set_cmdline(lp_ctx, "torture:share", share);
148                 lp_set_cmdline(lp_ctx, "torture:binding", host);
149         }
150
151         return true;
152 }
153
154 static void parse_dns(struct loadparm_context *lp_ctx, const char *dns)
155 {
156         char *userdn, *basedn, *secret;
157         char *p, *d;
158
159         /* retrievieng the userdn */
160         p = strchr_m(dns, '#');
161         if (!p) {
162                 lp_set_cmdline(lp_ctx, "torture:ldap_userdn", "");
163                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
164                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
165                 return;
166         }
167         userdn = strndup(dns, p - dns);
168         lp_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn);
169
170         /* retrieve the basedn */
171         d = p + 1;
172         p = strchr_m(d, '#');
173         if (!p) {
174                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
175                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
176                 return;
177         }
178         basedn = strndup(d, p - d);
179         lp_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn);
180
181         /* retrieve the secret */
182         p = p + 1;
183         if (!p) {
184                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
185                 return;
186         }
187         secret = strdup(p);
188         lp_set_cmdline(lp_ctx, "torture:ldap_secret", secret);
189
190         printf ("%s - %s - %s\n", userdn, basedn, secret);
191
192 }
193
194 static void print_test_list(void)
195 {
196         struct torture_suite *o;
197         struct torture_suite *s;
198         struct torture_tcase *t;
199
200         if (torture_root == NULL)
201                 return;
202
203         for (o = torture_root->children; o; o = o->next) {
204                 for (s = o->children; s; s = s->next) {
205                         printf("%s-%s\n", o->name, s->name);
206                 }
207
208                 for (t = o->testcases; t; t = t->next) {
209                         printf("%s-%s\n", o->name, t->name);
210                 }
211         }
212 }
213
214 _NORETURN_ static void usage(poptContext pc)
215 {
216         struct torture_suite *o;
217         struct torture_suite *s;
218         struct torture_tcase *t;
219         int i;
220
221         poptPrintUsage(pc, stdout, 0);
222         printf("\n");
223
224         printf("The binding format is:\n\n");
225
226         printf("  TRANSPORT:host[flags]\n\n");
227
228         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
229         printf("  or ncalrpc for local connections.\n\n");
230
231         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
232         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
233         printf("  string.\n\n");
234
235         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
236         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
237         printf("  will be auto-determined.\n\n");
238
239         printf("  other recognised flags are:\n\n");
240
241         printf("    sign : enable ntlmssp signing\n");
242         printf("    seal : enable ntlmssp sealing\n");
243         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
244         printf("    validate: enable the NDR validator\n");
245         printf("    print: enable debugging of the packets\n");
246         printf("    bigendian: use bigendian RPC\n");
247         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
248
249         printf("  For example, these all connect to the samr pipe:\n\n");
250
251         printf("    ncacn_np:myserver\n");
252         printf("    ncacn_np:myserver[samr]\n");
253         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
254         printf("    ncacn_np:myserver[/pipe/samr]\n");
255         printf("    ncacn_np:myserver[samr,sign,print]\n");
256         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
257         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
258         printf("    ncacn_np:\n");
259         printf("    ncacn_np:[/pipe/samr]\n\n");
260
261         printf("    ncacn_ip_tcp:myserver\n");
262         printf("    ncacn_ip_tcp:myserver[1024]\n");
263         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
264
265         printf("    ncalrpc:\n\n");
266
267         printf("The UNC format is:\n\n");
268
269         printf("  //server/share\n\n");
270
271         printf("Tests are:");
272
273         if (torture_root == NULL) {
274             printf("NO TESTS LOADED\n");
275             exit(1);
276         }
277
278         for (o = torture_root->children; o; o = o->next) {
279                 printf("\n%s (%s):\n  ", o->description, o->name);
280
281                 i = 0;
282                 for (s = o->children; s; s = s->next) {
283                         if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
284                                 printf("\n  ");
285                                 i = 0;
286                         }
287                         i+=printf("%s-%s ", o->name, s->name);
288                 }
289
290                 for (t = o->testcases; t; t = t->next) {
291                         if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
292                                 printf("\n  ");
293                                 i = 0;
294                         }
295                         i+=printf("%s-%s ", o->name, t->name);
296                 }
297
298                 if (i) printf("\n");
299         }
300
301         printf("\nThe default test is ALL.\n");
302
303         exit(1);
304 }
305
306 _NORETURN_ static void max_runtime_handler(int sig)
307 {
308         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
309         exit(1);
310 }
311
312 struct timeval last_suite_started;
313
314 static void simple_suite_start(struct torture_context *ctx,
315                                struct torture_suite *suite)
316 {
317         last_suite_started = timeval_current();
318         printf("Running %s\n", suite->name);
319 }
320
321 static void simple_suite_finish(struct torture_context *ctx,
322                                 struct torture_suite *suite)
323 {
324
325         printf("%s took %g secs\n\n", suite->name, 
326                    timeval_elapsed(&last_suite_started));
327 }
328
329 static void simple_test_result(struct torture_context *context, 
330                                enum torture_result res, const char *reason)
331 {
332         switch (res) {
333         case TORTURE_OK:
334                 if (reason)
335                         printf("OK: %s\n", reason);
336                 break;
337         case TORTURE_FAIL:
338                 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
339                 break;
340         case TORTURE_ERROR:
341                 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason); 
342                 break;
343         case TORTURE_SKIP:
344                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
345                 break;
346         }
347 }
348
349 static void simple_comment(struct torture_context *test, 
350                            const char *comment)
351 {
352         printf("%s", comment);
353 }
354
355 static void simple_warning(struct torture_context *test, 
356                            const char *comment)
357 {
358         fprintf(stderr, "WARNING: %s\n", comment);
359 }
360
361 const static struct torture_ui_ops std_ui_ops = {
362         .comment = simple_comment,
363         .warning = simple_warning,
364         .suite_start = simple_suite_start,
365         .suite_finish = simple_suite_finish,
366         .test_result = simple_test_result
367 };
368
369
370 static void run_shell(struct torture_context *tctx)
371 {
372         char *cline;
373         int argc;
374         const char **argv;
375         int ret;
376
377         while (1) {
378                 cline = smb_readline("torture> ", NULL, NULL);
379
380                 if (cline == NULL)
381                         return;
382         
383                 ret = poptParseArgvString(cline, &argc, &argv);
384                 if (ret != 0) {
385                         fprintf(stderr, "Error parsing line\n");
386                         continue;
387                 }
388
389                 if (!strcmp(argv[0], "quit")) {
390                         return;
391                 } else if (!strcmp(argv[0], "set")) {
392                         if (argc < 3) {
393                                 fprintf(stderr, "Usage: set <variable> <value>\n");
394                         } else {
395                                 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
396                                 lp_set_cmdline(tctx->lp_ctx, name, argv[2]);
397                                 talloc_free(name);
398                         }
399                 } else if (!strcmp(argv[0], "help")) {
400                         fprintf(stderr, "Available commands:\n"
401                                                         " help - This help command\n"
402                                                         " run - Run test\n"
403                                                         " set - Change variables\n"
404                                                         "\n");
405                 } else if (!strcmp(argv[0], "run")) {
406                         if (argc < 2) {
407                                 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
408                         } else {
409                                 run_test(tctx, argv[1]);
410                         }
411                 }
412                 free(cline);
413         }
414 }
415
416 /****************************************************************************
417   main program
418 ****************************************************************************/
419 int main(int argc,char *argv[])
420 {
421         int opt, i;
422         bool correct = true;
423         int max_runtime=0;
424         int argc_new;
425         struct torture_context *torture;
426         struct torture_results *results;
427         const struct torture_ui_ops *ui_ops;
428         char **argv_new;
429         poptContext pc;
430         static const char *target = "other";
431         NTSTATUS status;
432         int shell = false;
433         static const char *ui_ops_name = "subunit";
434         const char *basedir = NULL;
435         const char *extra_module = NULL;
436         static int list_tests = 0;
437         int num_extra_users = 0;
438         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
439               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS,OPT_EXTRA_USER};
440         
441         struct poptOption long_options[] = {
442                 POPT_AUTOHELP
443                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL },
444                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
445                 {"basedir",       0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" },
446                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "Seed to use for randomizer",   NULL},
447                 {"num-progs",     0, POPT_ARG_INT,  NULL,       OPT_NUMPROGS,   "num progs",    NULL},
448                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
449                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
450                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "NBench load file to use",      NULL},
451                 {"list",          0, POPT_ARG_NONE, &list_tests, 0, "List available tests and exit", NULL },
452                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
453                 {"timelimit",   't', POPT_ARG_INT,      NULL,   OPT_TIMELIMIT,  "Set time limit (in seconds)",  NULL},
454                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
455                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
456                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
457                  "run dangerous tests (eg. wiping out password database)", NULL},
458                 {"load-module",  0,  POPT_ARG_STRING, &extra_module,     0, "load tests from DSO file",    "SOFILE"},
459                 {"shell",               0, POPT_ARG_NONE, &shell, true, "Run shell", NULL},
460                 {"target",              'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
461                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
462                  "run async tests", NULL},
463                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
464                  "number of simultaneous async requests", NULL},
465                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
466                  "set maximum time for smbtorture to live", "seconds"},
467                 {"extra-user",   0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER,
468                  "extra user credentials", NULL},
469                 POPT_COMMON_SAMBA
470                 POPT_COMMON_CONNECTION
471                 POPT_COMMON_CREDENTIALS
472                 POPT_COMMON_VERSION
473                 { NULL }
474         };
475
476         setlinebuf(stdout);
477
478         /* we are never interested in SIGPIPE */
479         BlockSignals(true, SIGPIPE);
480
481         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
482                             POPT_CONTEXT_KEEP_FIRST);
483
484         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
485
486         while((opt = poptGetNextOpt(pc)) != -1) {
487                 switch (opt) {
488                 case OPT_LOADFILE:
489                         lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc));
490                         break;
491                 case OPT_UNCLIST:
492                         lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
493                         break;
494                 case OPT_TIMELIMIT:
495                         lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc));
496                         break;
497                 case OPT_NUMPROGS:
498                         lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc));
499                         break;
500                 case OPT_DNS:
501                         parse_dns(cmdline_lp_ctx, poptGetOptArg(pc));
502                         break;
503                 case OPT_DANGEROUS:
504                         lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes");
505                         break;
506                 case OPT_ASYNC:
507                         lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes");
508                         break;
509                 case OPT_SMB_PORTS:
510                         lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc));
511                         break;
512                 case OPT_EXTRA_USER:
513                         {
514                                 char *option = talloc_asprintf(NULL, "torture:extra_user%u",
515                                                                ++num_extra_users);
516                                 char *value = poptGetOptArg(pc);
517                                 lp_set_cmdline(cmdline_lp_ctx, option, value);
518                                 talloc_free(option);
519                         }
520                         break;
521                 }
522         }
523
524         if (strcmp(target, "samba3") == 0) {
525                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true");
526         } else if (strcmp(target, "samba4") == 0) {
527                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true");
528         } else if (strcmp(target, "w2k8") == 0) {
529                 lp_set_cmdline(cmdline_lp_ctx, "torture:w2k8", "true");
530         } else if (strcmp(target, "win7") == 0) {
531                 lp_set_cmdline(cmdline_lp_ctx, "torture:win7", "true");
532         }
533
534         if (max_runtime) {
535                 /* this will only work if nobody else uses alarm(),
536                    which means it won't work for some tests, but we
537                    can't use the event context method we use for smbd
538                    as so many tests create their own event
539                    context. This will at least catch most cases. */
540                 signal(SIGALRM, max_runtime_handler);
541                 alarm(max_runtime);
542         }
543
544         if (extra_module != NULL) {
545             init_module_fn fn = load_module(talloc_autofree_context(), poptGetOptArg(pc));
546
547             if (fn == NULL) 
548                 d_printf("Unable to load module from %s\n", poptGetOptArg(pc));
549             else {
550                 status = fn();
551                 if (NT_STATUS_IS_ERR(status)) {
552                     d_printf("Error initializing module %s: %s\n", 
553                              poptGetOptArg(pc), nt_errstr(status));
554                 }
555             }
556         } else { 
557                 torture_init();
558         }
559
560         if (list_tests) {
561                 print_test_list();
562                 return 0;
563         }
564
565         if (torture_seed == 0) {
566                 torture_seed = time(NULL);
567         } 
568         printf("Using seed %d\n", torture_seed);
569         srandom(torture_seed);
570
571         argv_new = discard_const_p(char *, poptGetArgs(pc));
572
573         argc_new = argc;
574         for (i=0; i<argc; i++) {
575                 if (argv_new[i] == NULL) {
576                         argc_new = i;
577                         break;
578                 }
579         }
580
581         if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
582                 usage(pc);
583                 exit(1);
584         }
585
586         if (!parse_target(cmdline_lp_ctx, argv_new[1])) {
587                 usage(pc);
588                 exit(1);
589         }
590
591         if (!strcmp(ui_ops_name, "simple")) {
592                 ui_ops = &std_ui_ops;
593         } else if (!strcmp(ui_ops_name, "subunit")) {
594                 ui_ops = &torture_subunit_ui_ops;
595         } else {
596                 printf("Unknown output format '%s'\n", ui_ops_name);
597                 exit(1);
598         }
599
600         results = torture_results_init(talloc_autofree_context(), ui_ops);
601
602         torture = torture_context_init(s4_event_context_init(NULL), results);
603         if (basedir != NULL) {
604                 if (basedir[0] != '/') {
605                         fprintf(stderr, "Please specify an absolute path to --basedir\n");
606                         return 1;
607                 }
608                 torture->outputdir = basedir;
609         } else {
610                 char *pwd = talloc_size(torture, PATH_MAX);
611                 if (!getcwd(pwd, PATH_MAX)) {
612                         fprintf(stderr, "Unable to determine current working directory\n");
613                         return 1;
614                 }
615                 torture->outputdir = pwd;
616         }
617
618         torture->lp_ctx = cmdline_lp_ctx;
619
620         gensec_init(cmdline_lp_ctx);
621
622         if (argc_new == 0) {
623                 printf("You must specify a test to run, or 'ALL'\n");
624         } else if (shell) {
625                 run_shell(torture);
626         } else {
627                 for (i=2;i<argc_new;i++) {
628                         if (!run_test(torture, argv_new[i])) {
629                                 correct = false;
630                         }
631                 }
632         }
633
634         if (torture->results->returncode && correct) {
635                 return(0);
636         } else {
637                 return(1);
638         }
639 }