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 41static 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/**************************************************************************** 103run a specified test or "ALL" 104****************************************************************************/ 105static 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 128static 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 154static 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 194static 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 312struct timeval last_suite_started; 313 314static 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 321static 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 329static 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 349static void simple_comment(struct torture_context *test, 350 const char *comment) 351{ 352 printf("%s", comment); 353} 354 355static void simple_warning(struct torture_context *test, 356 const char *comment) 357{ 358 fprintf(stderr, "WARNING: %s\n", comment); 359} 360 361const 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 370static 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****************************************************************************/ 419int 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, 440 OPT_EXTRA_USER,}; 441 442 struct poptOption long_options[] = { 443 POPT_AUTOHELP 444 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL }, 445 {"smb-ports", 'p', POPT_ARG_STRING, NULL, OPT_SMB_PORTS, "SMB ports", NULL}, 446 {"basedir", 0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" }, 447 {"seed", 0, POPT_ARG_INT, &torture_seed, 0, "Seed to use for randomizer", NULL}, 448 {"num-progs", 0, POPT_ARG_INT, NULL, OPT_NUMPROGS, "num progs", NULL}, 449 {"num-ops", 0, POPT_ARG_INT, &torture_numops, 0, "num ops", NULL}, 450 {"entries", 0, POPT_ARG_INT, &torture_entries, 0, "entries", NULL}, 451 {"loadfile", 0, POPT_ARG_STRING, NULL, OPT_LOADFILE, "NBench load file to use", NULL}, 452 {"list", 0, POPT_ARG_NONE, &list_tests, 0, "List available tests and exit", NULL }, 453 {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, 454 {"timelimit", 't', POPT_ARG_INT, NULL, OPT_TIMELIMIT, "Set time limit (in seconds)", NULL}, 455 {"failures", 'f', POPT_ARG_INT, &torture_failures, 0, "failures", NULL}, 456 {"parse-dns", 'D', POPT_ARG_STRING, NULL, OPT_DNS, "parse-dns", NULL}, 457 {"dangerous", 'X', POPT_ARG_NONE, NULL, OPT_DANGEROUS, 458 "run dangerous tests (eg. wiping out password database)", NULL}, 459 {"load-module", 0, POPT_ARG_STRING, &extra_module, 0, "load tests from DSO file", "SOFILE"}, 460 {"shell", 0, POPT_ARG_NONE, &shell, true, "Run shell", NULL}, 461 {"target", 'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL}, 462 {"async", 'a', POPT_ARG_NONE, NULL, OPT_ASYNC, 463 "run async tests", NULL}, 464 {"num-async", 0, POPT_ARG_INT, &torture_numasync, 0, 465 "number of simultaneous async requests", NULL}, 466 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 467 "set maximum time for smbtorture to live", "seconds"}, 468 {"extra-user", 0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER, 469 "extra user credentials", NULL}, 470 POPT_COMMON_SAMBA 471 POPT_COMMON_CONNECTION 472 POPT_COMMON_CREDENTIALS 473 POPT_COMMON_VERSION 474 { NULL } 475 }; 476 477 setlinebuf(stdout); 478 479 /* we are never interested in SIGPIPE */ 480 BlockSignals(true, SIGPIPE); 481 482 pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 483 POPT_CONTEXT_KEEP_FIRST); 484 485 poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ..."); 486 487 while((opt = poptGetNextOpt(pc)) != -1) { 488 switch (opt) { 489 case OPT_LOADFILE: 490 lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc)); 491 break; 492 case OPT_UNCLIST: 493 lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc)); 494 break; 495 case OPT_TIMELIMIT: 496 lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc)); 497 break; 498 case OPT_NUMPROGS: 499 lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc)); 500 break; 501 case OPT_DNS: 502 parse_dns(cmdline_lp_ctx, poptGetOptArg(pc)); 503 break; 504 case OPT_DANGEROUS: 505 lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes"); 506 break; 507 case OPT_ASYNC: 508 lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes"); 509 break; 510 case OPT_SMB_PORTS: 511 lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc)); 512 break; 513 case OPT_EXTRA_USER: 514 { 515 char *option = talloc_asprintf(NULL, "torture:extra_user%u", 516 ++num_extra_users); 517 char *value = poptGetOptArg(pc); 518 lp_set_cmdline(cmdline_lp_ctx, option, value); 519 talloc_free(option); 520 } 521 break; 522 default: 523 printf("bad command line option\n"); 524 exit(1); 525 } 526 } 527 528 if (strcmp(target, "samba3") == 0) { 529 lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true"); 530 } else if (strcmp(target, "samba4") == 0) { 531 lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true"); 532 } else if (strcmp(target, "w2k8") == 0) { 533 lp_set_cmdline(cmdline_lp_ctx, "torture:w2k8", "true"); 534 } else if (strcmp(target, "win7") == 0) { 535 lp_set_cmdline(cmdline_lp_ctx, "torture:win7", "true"); 536 } else if (strcmp(target, "onefs") == 0) { 537 lp_set_cmdline(cmdline_lp_ctx, "torture:sacl_support", "false"); 538 } 539 540 if (max_runtime) { 541 /* this will only work if nobody else uses alarm(), 542 which means it won't work for some tests, but we 543 can't use the event context method we use for smbd 544 as so many tests create their own event 545 context. This will at least catch most cases. */ 546 signal(SIGALRM, max_runtime_handler); 547 alarm(max_runtime); 548 } 549 550 if (extra_module != NULL) { 551 init_module_fn fn = load_module(talloc_autofree_context(), poptGetOptArg(pc)); 552 553 if (fn == NULL) 554 d_printf("Unable to load module from %s\n", poptGetOptArg(pc)); 555 else { 556 status = fn(); 557 if (NT_STATUS_IS_ERR(status)) { 558 d_printf("Error initializing module %s: %s\n", 559 poptGetOptArg(pc), nt_errstr(status)); 560 } 561 } 562 } else { 563 torture_init(); 564 } 565 566 if (list_tests) { 567 print_test_list(); 568 return 0; 569 } 570 571 if (torture_seed == 0) { 572 torture_seed = time(NULL); 573 } 574 printf("Using seed %d\n", torture_seed); 575 srandom(torture_seed); 576 577 argv_new = discard_const_p(char *, poptGetArgs(pc)); 578 579 argc_new = argc; 580 for (i=0; i<argc; i++) { 581 if (argv_new[i] == NULL) { 582 argc_new = i; 583 break; 584 } 585 } 586 587 if (!(argc_new >= 3 || (shell && argc_new >= 2))) { 588 usage(pc); 589 exit(1); 590 } 591 592 if (!parse_target(cmdline_lp_ctx, argv_new[1])) { 593 usage(pc); 594 exit(1); 595 } 596 597 if (!strcmp(ui_ops_name, "simple")) { 598 ui_ops = &std_ui_ops; 599 } else if (!strcmp(ui_ops_name, "subunit")) { 600 ui_ops = &torture_subunit_ui_ops; 601 } else { 602 printf("Unknown output format '%s'\n", ui_ops_name); 603 exit(1); 604 } 605 606 results = torture_results_init(talloc_autofree_context(), ui_ops); 607 608 torture = torture_context_init(s4_event_context_init(NULL), results); 609 if (basedir != NULL) { 610 if (basedir[0] != '/') { 611 fprintf(stderr, "Please specify an absolute path to --basedir\n"); 612 return 1; 613 } 614 torture->outputdir = basedir; 615 } else { 616 char *pwd = talloc_size(torture, PATH_MAX); 617 if (!getcwd(pwd, PATH_MAX)) { 618 fprintf(stderr, "Unable to determine current working directory\n"); 619 return 1; 620 } 621 torture->outputdir = pwd; 622 } 623 624 torture->lp_ctx = cmdline_lp_ctx; 625 626 gensec_init(cmdline_lp_ctx); 627 628 if (argc_new == 0) { 629 printf("You must specify a test to run, or 'ALL'\n"); 630 } else if (shell) { 631 run_shell(torture); 632 } else { 633 for (i=2;i<argc_new;i++) { 634 if (!run_test(torture, argv_new[i])) { 635 correct = false; 636 } 637 } 638 } 639 640 if (torture->results->returncode && correct) { 641 return(0); 642 } else { 643 return(1); 644 } 645} 646