diff options
Diffstat (limited to 'source4/torture/util_smb.c')
-rw-r--r-- | source4/torture/util_smb.c | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/source4/torture/util_smb.c b/source4/torture/util_smb.c new file mode 100644 index 0000000..f131329 --- /dev/null +++ b/source4/torture/util_smb.c @@ -0,0 +1,1020 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester utility functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "../libcli/smb/smb_constants.h" +#include "libcli/libcli.h" +#include "system/filesys.h" +#include "system/shmem.h" +#include "system/wait.h" +#include "system/time.h" +#include "torture/torture.h" +#include "../lib/util/dlinklist.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "libcli/security/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/util/clilsa.h" +#include "torture/util.h" +#include "libcli/smb/smbXcli_base.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" + +/** + setup a directory ready for a test +*/ +_PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname) +{ + smb_raw_exit(cli->session); + if (smbcli_deltree(cli->tree, dname) == -1 || + NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) { + printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree)); + return false; + } + return true; +} + +/* + create a directory, returning a handle to it +*/ +NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum) +{ + NTSTATUS status; + union smb_open io; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named_const(tree, 0, "create_directory_handle"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(tree, mem_ctx, &io); + talloc_free(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + *fnum = io.ntcreatex.out.file.fnum; + } + + return status; +} + + +/** + sometimes we need a fairly complex file to work with, so we can test + all possible attributes. +*/ +_PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname) +{ + int fnum; + char buf[7] = "abc"; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smbcli_unlink(cli->tree, fname); + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + if (fnum == -1) return -1; + + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + + if (strchr(fname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + } + + /* make sure all the timestamps aren't the same */ + ZERO_STRUCT(setfile); + setfile.generic.level = RAW_SFILEINFO_BASIC_INFO; + setfile.generic.in.file.fnum = fnum; + + unix_to_nt_time(&setfile.basic_info.in.create_time, + t + 9*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.access_time, + t + 6*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.write_time, + t + 3*30*24*60*60); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO; + fileinfo.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + + +/* + sometimes we need a fairly complex directory to work with, so we can test + all possible attributes. +*/ +int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname) +{ + int fnum; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smbcli_deltree(cli->tree, dname); + fnum = smbcli_nt_create_full(cli->tree, dname, 0, + SEC_RIGHTS_DIR_ALL, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DIRECTORY, 0); + if (fnum == -1) return -1; + + if (strchr(dname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + } + + /* make sure all the timestamps aren't the same */ + ZERO_STRUCT(setfile); + setfile.generic.level = RAW_SFILEINFO_BASIC_INFO; + setfile.generic.in.file.fnum = fnum; + + unix_to_nt_time(&setfile.basic_info.in.create_time, + t + 9*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.access_time, + t + 6*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.write_time, + t + 3*30*24*60*60); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO; + fileinfo.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + +/** + check that a wire string matches the flags specified + not 100% accurate, but close enough for testing +*/ +bool wire_bad_flags(struct smb_wire_string *str, int flags, + struct smbcli_transport *transport) +{ + bool server_unicode; + int len; + if (!str || !str->s) return true; + len = strlen(str->s); + if (flags & STR_TERMINATE) len++; + + server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false; + if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) { + server_unicode = false; + } + + if ((flags & STR_UNICODE) || server_unicode) { + len *= 2; + } else if (flags & STR_TERMINATE_ASCII) { + len++; + } + if (str->private_length != len) { + printf("Expected wire_length %d but got %d for '%s'\n", + len, str->private_length, str->s); + return true; + } + return false; +} + +/* + dump a all_info QFILEINFO structure +*/ +void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo) +{ + d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time)); + d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time)); + d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time)); + d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time)); + d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib); + d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size); + d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size); + d_printf("\tnlink: %u\n", finfo->all_info.out.nlink); + d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending); + d_printf("\tdirectory: %u\n", finfo->all_info.out.directory); + d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size); + d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s); +} + +/* + dump file infor by name +*/ +void torture_all_info(struct smbcli_tree *tree, const char *fname) +{ + TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname); + union smb_fileinfo finfo; + NTSTATUS status; + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s - %s\n", fname, nt_errstr(status)); + return; + } + + d_printf("%s:\n", fname); + dump_all_info(mem_ctx, &finfo); + talloc_free(mem_ctx); +} + + +/* + set a attribute on a file +*/ +bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib) +{ + union smb_setfileinfo sfinfo; + NTSTATUS status; + + ZERO_STRUCT(sfinfo.basic_info.in); + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.basic_info.in.file.path = fname; + sfinfo.basic_info.in.attrib = attrib; + status = smb_raw_setpathinfo(tree, &sfinfo); + return NT_STATUS_IS_OK(status); +} + + +/* + set a file descriptor as sparse +*/ +NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum) +{ + union smb_ioctl nt; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + nt.ntioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_SET_SPARSE; + nt.ntioctl.in.file.fnum = fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = 0; + nt.ntioctl.in.blob = data_blob(NULL, 0); + + status = smb_raw_ioctl(tree, mem_ctx, &nt); + + talloc_free(mem_ctx); + + return status; +} + +/* + check that an EA has the right value +*/ +NTSTATUS torture_check_ea(struct smbcli_state *cli, + const char *fname, const char *eaname, const char *value) +{ + union smb_fileinfo info; + NTSTATUS status; + struct ea_name ea; + TALLOC_CTX *mem_ctx = talloc_new(cli); + + info.ea_list.level = RAW_FILEINFO_EA_LIST; + info.ea_list.in.file.path = fname; + info.ea_list.in.num_names = 1; + info.ea_list.in.ea_names = &ea; + + ea.name.s = eaname; + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &info); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + if (info.ea_list.out.num_eas != 1) { + printf("Expected 1 ea in ea_list\n"); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + + if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) { + printf("Expected ea '%s' not '%s' in ea_list\n", + eaname, info.ea_list.out.eas[0].name.s); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + + if (value == NULL) { + if (info.ea_list.out.eas[0].value.length != 0) { + printf("Expected zero length ea for %s\n", eaname); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + if (strlen(value) == info.ea_list.out.eas[0].value.length && + memcmp(value, info.ea_list.out.eas[0].value.data, + info.ea_list.out.eas[0].value.length) == 0) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + printf("Expected value '%s' not '%*.*s' for ea %s\n", + value, + (int)info.ea_list.out.eas[0].value.length, + (int)info.ea_list.out.eas[0].value.length, + info.ea_list.out.eas[0].value.data, + eaname); + + talloc_free(mem_ctx); + + return NT_STATUS_EA_CORRUPT_ERROR; +} + +_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx, + struct smbcli_state **c, + struct torture_context *tctx, + const char *hostname, + const char *sharename, + struct tevent_context *ev) +{ + NTSTATUS status; + + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true); + options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true); + + status = smbcli_full_connection(mem_ctx, c, hostname, + lpcfg_smb_ports(tctx->lp_ctx), + sharename, NULL, + lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open connection - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +_PUBLIC_ bool torture_get_conn_index(int conn_index, + TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + char **host, char **share) +{ + char **unc_list = NULL; + int num_unc_names = 0; + const char *p; + + (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL)); + (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL)); + + p = torture_setting_string(tctx, "unclist", NULL); + if (!p) { + return true; + } + + unc_list = file_lines_load(p, &num_unc_names, 0, NULL); + if (!unc_list || num_unc_names <= 0) { + DEBUG(0,("Failed to load unc names list from '%s'\n", p)); + return false; + } + + p = unc_list[conn_index % num_unc_names]; + if (p[0] != '/' && p[0] != '\\') { + /* allow UNC lists of hosts */ + (*host) = talloc_strdup(mem_ctx, p); + } else if (!smbcli_parse_unc(p, mem_ctx, host, share)) { + DEBUG(0, ("Failed to parse UNC name %s\n", + unc_list[conn_index % num_unc_names])); + return false; + } + + talloc_free(unc_list); + return true; +} + + + +_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c, + int conn_index, + struct torture_context *tctx, + struct tevent_context *ev) +{ + char *host, *share; + bool ret; + + if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) { + return false; + } + + ret = torture_open_connection_share(NULL, c, tctx, host, share, ev); + talloc_free(host); + talloc_free(share); + + return ret; +} + +_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index) +{ + return torture_open_connection_ev(c, conn_index, tctx, tctx->ev); +} + + + +_PUBLIC_ bool torture_close_connection(struct smbcli_state *c) +{ + bool ret = true; + if (!c) return true; + if (NT_STATUS_IS_ERR(smbcli_tdis(c))) { + printf("tdis failed (%s)\n", smbcli_errstr(c->tree)); + ret = false; + } + talloc_free(c); + return ret; +} + + +/* check if the server produced the expected error code */ +_PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, + uint8_t eclass, uint32_t ecode, NTSTATUS nterr) +{ + NTSTATUS status; + + status = smbcli_nt_error(c->tree); + if (NT_STATUS_IS_DOS(status)) { + int classnum, num; + classnum = NT_STATUS_DOS_CLASS(status); + num = NT_STATUS_DOS_CODE(status); + if (eclass != classnum || ecode != num) { + printf("unexpected error code %s\n", nt_errstr(status)); + printf(" expected %s or %s (at %s)\n", + nt_errstr(NT_STATUS_DOS(eclass, ecode)), + nt_errstr(nterr), location); + return false; + } + } else { + if (!NT_STATUS_EQUAL(nterr, status)) { + printf("unexpected error code %s\n", nt_errstr(status)); + printf(" expected %s (at %s)\n", nt_errstr(nterr), location); + return false; + } + } + + return true; +} + +static struct smbcli_state *current_cli; +static int procnum; /* records process count number when forking */ + +static void sigcont(int sig) +{ +} + +struct child_status { + pid_t pid; + bool start; + enum torture_result result; + char reason[1024]; +}; + +double torture_create_procs(struct torture_context *tctx, + bool (*fn)(struct torture_context *, struct smbcli_state *, int), + bool *result) +{ + int status; + size_t i; + struct child_status *child_status; + size_t synccount; + size_t tries = 8; + size_t torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + double start_time_limit = 10 + (torture_nprocs * 1.5); + struct timeval tv; + + *result = true; + + synccount = 0; + + signal(SIGCONT, sigcont); + + child_status = (struct child_status *)anonymous_shared_allocate( + sizeof(struct child_status)*torture_nprocs); + if (child_status == NULL) { + printf("Failed to setup shared memory\n"); + return -1; + } + + for (i = 0; i < torture_nprocs; i++) { + ZERO_STRUCT(child_status[i]); + } + + tv = timeval_current(); + + for (i=0;i<torture_nprocs;i++) { + procnum = i; + if (fork() == 0) { + char *myname; + bool ok; + + pid_t mypid = getpid(); + srandom(((int)mypid) ^ ((int)time(NULL))); + + if (asprintf(&myname, "CLIENT%zu", i) == -1) { + printf("asprintf failed\n"); + return -1; + } + lpcfg_set_cmdline(tctx->lp_ctx, "netbios name", myname); + free(myname); + + + while (1) { + if (torture_open_connection(¤t_cli, tctx, i)) { + break; + } + if (tries-- == 0) { + printf("pid %d failed to start\n", (int)getpid()); + _exit(1); + } + smb_msleep(100); + } + + child_status[i].pid = getpid(); + + pause(); + + if (!child_status[i].start) { + child_status[i].result = TORTURE_ERROR; + printf("Child %zu failed to start!\n", i); + _exit(1); + } + + ok = fn(tctx, current_cli, i); + if (!ok) { + if (tctx->last_result == TORTURE_OK) { + torture_result(tctx, TORTURE_ERROR, + "unknown error: missing " + "torture_result call?\n"); + } + + child_status[i].result = tctx->last_result; + + if (strlen(tctx->last_reason) > 1023) { + /* note: reason already contains \n */ + torture_comment(tctx, + "child %zu (pid %u) failed: %s", + i, + (unsigned)child_status[i].pid, + tctx->last_reason); + } + + snprintf(child_status[i].reason, + 1024, "child %zu (pid %u) failed: %s", + i, (unsigned)child_status[i].pid, + tctx->last_reason); + /* ensure proper "\n\0" termination: */ + if (child_status[i].reason[1022] != '\0') { + child_status[i].reason[1022] = '\n'; + child_status[i].reason[1023] = '\0'; + } + } + _exit(0); + } + } + + do { + synccount = 0; + for (i=0;i<torture_nprocs;i++) { + if (child_status[i].pid != 0) { + synccount++; + } + } + if (synccount == torture_nprocs) { + break; + } + smb_msleep(100); + } while (timeval_elapsed(&tv) < start_time_limit); + + if (synccount != torture_nprocs) { + printf("FAILED TO START %zu CLIENTS (started %zu)\n", torture_nprocs, synccount); + + /* cleanup child processes */ + for (i = 0; i < torture_nprocs; i++) { + if (child_status[i].pid != 0) { + kill(child_status[i].pid, SIGTERM); + } + } + + *result = false; + return timeval_elapsed(&tv); + } + + printf("Starting %zu clients\n", torture_nprocs); + + /* start the client load */ + tv = timeval_current(); + for (i=0;i<torture_nprocs;i++) { + child_status[i].start = true; + } + + printf("%zu clients started\n", torture_nprocs); + + kill(0, SIGCONT); + + for (i=0;i<torture_nprocs;i++) { + int ret; + while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ; + if (ret == -1 || WEXITSTATUS(status) != 0) { + *result = false; + } + } + + printf("\n"); + + for (i=0;i<torture_nprocs;i++) { + if (child_status[i].result != TORTURE_OK) { + *result = false; + torture_result(tctx, child_status[i].result, + "%s", child_status[i].reason); + } + } + + return timeval_elapsed(&tv); +} + +static bool wrap_smb_multi_test(struct torture_context *torture, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn; + bool result; + + torture_create_procs(torture, fn, &result); + + return result; +} + +_PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + int i)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_smb_multi_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; + +} + +static bool wrap_simple_2smb_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smbcli_state *, + struct smbcli_state *); + bool ret = true; + + struct smbcli_state *cli1 = NULL, *cli2 = NULL; + + torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection"); + torture_assert_goto(torture_ctx, torture_open_connection(&cli2, torture_ctx, 1), ret, fail, "Failed to open connection"); + + fn = test->fn; + + ret = fn(torture_ctx, cli1, cli2); +fail: + talloc_free(cli1); + talloc_free(cli2); + + return ret; +} + + + +_PUBLIC_ struct torture_test *torture_suite_add_2smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + struct smbcli_state *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_2smb_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; + +} + +static bool wrap_simple_1smb_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smbcli_state *); + bool ret = true; + + struct smbcli_state *cli1 = NULL; + + torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection"); + + fn = test->fn; + + ret = fn(torture_ctx, cli1); +fail: + talloc_free(cli1); + + return ret; +} + +_PUBLIC_ struct torture_test *torture_suite_add_1smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, struct smbcli_state *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_1smb_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + + +NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx, + struct smbcli_session *session, + const char *sharename, + struct smbcli_tree **res) +{ + union smb_tcon tcon; + struct smbcli_tree *result; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + + if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) { + return NT_STATUS_NO_MEMORY; + } + + result = smbcli_tree_init(session, tmp_ctx, false); + if (result == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES; + + /* Ignore share mode security here */ + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = "?????"; + + status = smb_raw_tcon(result, tmp_ctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + + result->tid = tcon.tconx.out.tid; + + if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) { + smb1cli_session_protect_session_key(result->session->smbXcli); + } + + *res = talloc_steal(mem_ctx, result); + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* + a wrapper around smblsa_sid_check_privilege, that tries to take + account of the fact that the lsa privileges calls don't expand + group memberships, using an explicit check for administrator. There + must be a better way ... + */ +NTSTATUS torture_check_privilege(struct smbcli_state *cli, + const char *sid_str, + const char *privilege) +{ + struct dom_sid *sid; + TALLOC_CTX *tmp_ctx = talloc_new(cli); + uint32_t rid; + NTSTATUS status; + + sid = dom_sid_parse_talloc(tmp_ctx, sid_str); + if (sid == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_INVALID_SID; + } + + status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(tmp_ctx); + return status; + } + + if (rid == DOMAIN_RID_ADMINISTRATOR) { + /* assume the administrator has them all */ + return NT_STATUS_OK; + } + + talloc_free(tmp_ctx); + + return smblsa_sid_check_privilege(cli, sid_str, privilege); +} + +/* + * Use this to pass a 2nd user: + * + * --option='torture:user2name=user2' + * --option='torture:user2domain=domain2' + * --option='torture:user2password=password2' + */ +struct cli_credentials *torture_user2_credentials(struct torture_context *tctx, + TALLOC_CTX *mem_ctx) +{ + struct cli_credentials *credentials1 = samba_cmdline_get_creds(); + const char *user1domain = cli_credentials_get_domain(credentials1); + const char *user2name = torture_setting_string(tctx, "user2name", NULL); + const char *user2domain = torture_setting_string(tctx, "user2domain", user1domain); + const char *user2password = torture_setting_string(tctx, "user2password", NULL); + struct cli_credentials *credentials2 = NULL; + + credentials2 = cli_credentials_shallow_copy(mem_ctx, credentials1); + if (credentials2 == NULL) { + torture_comment(tctx, + "%s: cli_credentials_shallow_copy() failed\n", + __func__); + return NULL; + } + if (user2name != NULL) { + torture_comment(tctx, + "Using " + "'torture:user2name'='%s' " + "'torture:user2domain'='%s' " + "'torture:user2password'='REDACTED'", + user2name, + user2domain); + cli_credentials_set_username(credentials2, user2name, CRED_SPECIFIED); + cli_credentials_set_domain(credentials2, user2domain, CRED_SPECIFIED); + cli_credentials_set_password(credentials2, user2password, CRED_SPECIFIED); + } else { + torture_comment(tctx, + "Fallback to anonymous for " + "'torture:user2name'=NULL " + "'torture:user2domain'='%s' " + "'torture:user2password'='REDACTED'", + user2domain); + cli_credentials_set_anonymous(credentials2); + } + + return credentials2; +} |