/* Unix SMB/CIFS implementation. SMB torture tester Copyright (C) Guenther Deschner 2010 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 . */ #include "includes.h" #include "system/dir.h" #include "torture/smbtorture.h" #include "auth/credentials/credentials.h" #include "lib/cmdline/cmdline.h" #include #include "torture/libsmbclient/proto.h" #include "lib/param/loadparm.h" #include "lib/param/param_global.h" #include "libcli/smb/smb_constants.h" #include "dynconfig.h" #include "lib/util/time.h" /* test string to compare with when debug_callback is called */ #define TEST_STRING "smbc_setLogCallback test" /* Dummy log callback function */ static void debug_callback(void *private_ptr, int level, const char *msg) { bool *found = private_ptr; if (strstr(msg, TEST_STRING) != NULL) { *found = true; } return; } static void auth_callback(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen) { const char *workgroup = cli_credentials_get_domain(samba_cmdline_get_creds()); const char *username = cli_credentials_get_username(samba_cmdline_get_creds()); const char *password = cli_credentials_get_password(samba_cmdline_get_creds()); ssize_t ret; if (workgroup != NULL) { ret = strlcpy(wg, workgroup, wglen); if (ret >= wglen) { abort(); } } if (username != NULL) { ret = strlcpy(un, username, unlen); if (ret >= unlen) { abort(); } } if (password != NULL) { ret = strlcpy(pw, password, pwlen); if (ret >= pwlen) { abort(); } } }; bool torture_libsmbclient_init_context(struct torture_context *tctx, SMBCCTX **ctx_p) { const char *workgroup = cli_credentials_get_domain(samba_cmdline_get_creds()); const char *username = cli_credentials_get_username(samba_cmdline_get_creds()); const char *client_proto = torture_setting_string(tctx, "clientprotocol", NULL); SMBCCTX *ctx = NULL; SMBCCTX *p = NULL; bool ok = true; int dbglevel = DEBUGLEVEL; ctx = smbc_new_context(); torture_assert_not_null_goto(tctx, ctx, ok, out, "Failed to create new context"); p = smbc_init_context(ctx); torture_assert_not_null_goto(tctx, p, ok, out, "Failed to initialize context"); smbc_setDebug(ctx, dbglevel); smbc_setOptionDebugToStderr(ctx, 1); if (workgroup != NULL) { smbc_setWorkgroup(ctx, workgroup); } if (username != NULL) { smbc_setUser(ctx, username); } smbc_setFunctionAuthData(ctx, auth_callback); if (client_proto != NULL) { smbc_setOptionProtocols(ctx, client_proto, client_proto); } *ctx_p = ctx; out: if (!ok) { smbc_free_context(ctx, 1); } return ok; } static bool torture_libsmbclient_version(struct torture_context *tctx) { torture_comment(tctx, "Testing smbc_version\n"); torture_assert(tctx, smbc_version(), "failed to get version"); return true; } static bool torture_libsmbclient_initialize(struct torture_context *tctx) { SMBCCTX *ctx; bool ret = false; torture_comment(tctx, "Testing smbc_new_context\n"); ctx = smbc_new_context(); torture_assert(tctx, ctx, "failed to get new context"); torture_comment(tctx, "Testing smbc_init_context\n"); torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); smbc_setLogCallback(ctx, &ret, debug_callback); DEBUG(0, (TEST_STRING"\n")); torture_assert(tctx, ret, "Failed debug_callback not called"); ret = false; smbc_setLogCallback(ctx, NULL, NULL); DEBUG(0, (TEST_STRING"\n")); torture_assert(tctx, !ret, "Failed debug_callback called"); smbc_free_context(ctx, 1); return true; } static bool torture_libsmbclient_setConfiguration(struct torture_context *tctx) { SMBCCTX *ctx; struct loadparm_global *global_config = NULL; const char *new_smb_conf = torture_setting_string(tctx, "replace_smbconf", ""); ctx = smbc_new_context(); torture_assert_not_null(tctx, ctx, "failed to get new context"); torture_assert_not_null( tctx, smbc_init_context(ctx), "failed to init context"); torture_comment(tctx, "Testing smbc_setConfiguration - new file %s\n", new_smb_conf); global_config = get_globals(); torture_assert(tctx, global_config, "Global Config is NULL"); /* check configuration before smbc_setConfiguration call */ torture_comment(tctx, "'workgroup' before setConfiguration %s\n", global_config->workgroup); torture_comment(tctx, "'client min protocol' before " "setConfiguration %d\n", global_config->client_min_protocol); torture_comment(tctx, "'client max protocol' before " "setConfiguration %d\n", global_config->_client_max_protocol); torture_comment(tctx, "'client signing' before setConfiguration %d\n", global_config->client_signing); torture_comment(tctx, "'deadtime' before setConfiguration %d\n", global_config->deadtime); torture_assert_int_equal(tctx, smbc_setConfiguration(ctx, new_smb_conf), 0, "setConfiguration conf file not found"); /* verify configuration */ torture_assert_str_equal(tctx, global_config->workgroup, "NEW_WORKGROUP", "smbc_setConfiguration failed, " "'workgroup' not updated"); torture_assert_int_equal(tctx, global_config->client_min_protocol, PROTOCOL_NT1, "smbc_setConfiguration failed, 'client min protocol' " "not updated"); torture_assert_int_equal(tctx, global_config->_client_max_protocol, PROTOCOL_SMB3_00, "smbc_setConfiguration failed, 'client max protocol' " "not updated"); torture_assert_int_equal(tctx, global_config->client_signing, 1, "smbc_setConfiguration failed, 'client signing' " "not updated"); torture_assert_int_equal(tctx, global_config->deadtime, 5, "smbc_setConfiguration failed, 'deadtime' not updated"); /* Restore configuration to default */ smbc_setConfiguration(ctx, get_dyn_CONFIGFILE()); smbc_free_context(ctx, 1); return true; } static bool test_opendir(struct torture_context *tctx, SMBCCTX *ctx, const char *fname, bool expect_success) { int handle, ret; torture_comment(tctx, "Testing smbc_opendir(%s)\n", fname); handle = smbc_opendir(fname); if (!expect_success) { return true; } if (handle < 0) { torture_fail(tctx, talloc_asprintf(tctx, "failed to obain file handle for '%s'", fname)); } ret = smbc_closedir(handle); torture_assert_int_equal(tctx, ret, 0, talloc_asprintf(tctx, "failed to close file handle for '%s'", fname)); return true; } static bool torture_libsmbclient_opendir(struct torture_context *tctx) { size_t i; SMBCCTX *ctx; bool ret = true; const char *bad_urls[] = { "", NULL, "smb", "smb:", "smb:/", "smb:///", "bms://", ":", ":/", "://", ":///", "/", "//", "///" }; const char *good_urls[] = { "smb://", "smb://WORKGROUP", "smb://WORKGROUP/" }; torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); smbc_set_context(ctx); for (i=0; i < ARRAY_SIZE(bad_urls); i++) { ret &= test_opendir(tctx, ctx, bad_urls[i], false); } for (i=0; i < ARRAY_SIZE(good_urls); i++) { ret &= test_opendir(tctx, ctx, good_urls[i], true); } smbc_free_context(ctx, 1); return ret; } static bool torture_libsmbclient_readdirplus(struct torture_context *tctx) { SMBCCTX *ctx; int ret = -1; int dhandle = -1; int fhandle = -1; bool found = false; const char *filename = NULL; const char *smburl = torture_setting_string(tctx, "smburl", NULL); if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server/share missing\n"); } torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); smbc_set_context(ctx); filename = talloc_asprintf(tctx, "%s/test_readdirplus.txt", smburl); if (filename == NULL) { torture_fail(tctx, "talloc fail\n"); } /* Ensure the file doesn't exist. */ smbc_unlink(filename); /* Create it. */ fhandle = smbc_creat(filename, 0666); if (fhandle < 0) { torture_fail(tctx, talloc_asprintf(tctx, "failed to create file '%s': %s", filename, strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal(tctx, ret, 0, talloc_asprintf(tctx, "failed to close handle for '%s'", filename)); dhandle = smbc_opendir(smburl); if (dhandle < 0) { int saved_errno = errno; smbc_unlink(filename); torture_fail(tctx, talloc_asprintf(tctx, "failed to obtain " "directory handle for '%s' : %s", smburl, strerror(saved_errno))); } /* Readdirplus to ensure we see the new file. */ for (;;) { const struct libsmb_file_info *exstat = smbc_readdirplus(dhandle); if (exstat == NULL) { break; } if (strcmp(exstat->name, "test_readdirplus.txt") == 0) { found = true; break; } } /* Remove it again. */ smbc_unlink(filename); ret = smbc_closedir(dhandle); torture_assert_int_equal(tctx, ret, 0, talloc_asprintf(tctx, "failed to close directory handle for '%s'", smburl)); smbc_free_context(ctx, 1); if (!found) { torture_fail(tctx, talloc_asprintf(tctx, "failed to find file '%s'", filename)); } return true; } static bool torture_libsmbclient_readdirplus_seek(struct torture_context *tctx) { SMBCCTX *ctx; int ret = -1; int dhandle = -1; int fhandle = -1; const char *dname = NULL; const char *full_filename[100] = {0}; const char *filename[100] = {0}; const struct libsmb_file_info *direntries[102] = {0}; unsigned int i = 0; const char *smburl = torture_setting_string(tctx, "smburl", NULL); bool success = false; off_t telldir_50 = (off_t)-1; off_t telldir_20 = (off_t)-1; size_t getdentries_size = 0; struct smbc_dirent *getdentries = NULL; struct smbc_dirent *dirent_20 = NULL; const struct libsmb_file_info *direntries_20 = NULL; const struct libsmb_file_info *direntriesplus_20 = NULL; const char *plus2_stat_path = NULL; struct stat st = {0}; struct stat st2 = {0}; torture_assert_not_null( tctx, smburl, "option --option=torture:smburl=" "smb://user:password@server/share missing\n"); DEBUG(0,("torture_libsmbclient_readdirplus_seek start\n")); torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); smbc_set_context(ctx); dname = talloc_asprintf(tctx, "%s/rd_seek", smburl); torture_assert_not_null_goto( tctx, dname, success, done, "talloc fail\n"); /* Ensure the files don't exist. */ for (i = 0; i < 100; i++) { filename[i] = talloc_asprintf(tctx, "test_readdirplus_%u.txt", i); torture_assert_not_null_goto( tctx, filename[i], success, done, "talloc fail"); full_filename[i] = talloc_asprintf(tctx, "%s/%s", dname, filename[i]); torture_assert_not_null_goto( tctx, full_filename[i], success, done, "talloc fail"); (void)smbc_unlink(full_filename[i]); } /* Ensure the directory doesn't exist. */ (void)smbc_rmdir(dname); /* Create containing directory. */ ret = smbc_mkdir(dname, 0777); torture_assert_goto( tctx, ret == 0, success, done, talloc_asprintf(tctx, "failed to create directory '%s': %s", dname, strerror(errno))); DEBUG(0,("torture_libsmbclient_readdirplus_seek create\n")); /* Create them. */ for (i = 0; i < 100; i++) { fhandle = smbc_creat(full_filename[i], 0666); if (fhandle < 0) { torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to create file '%s': %s", full_filename[i], strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to close handle for '%s'", full_filename[i])); } DEBUG(0,("torture_libsmbclient_readdirplus_seek enum\n")); /* Now enumerate the directory. */ dhandle = smbc_opendir(dname); torture_assert_goto( tctx, dhandle >= 0, success, done, talloc_asprintf(tctx, "failed to obtain " "directory handle for '%s' : %s", dname, strerror(errno))); /* Read all the files. 100 we created plus . and .. */ for (i = 0; i < 102; i++) { bool found = false; unsigned int j; direntries[i] = smbc_readdirplus(dhandle); if (direntries[i] == NULL) { break; } /* Store at offset 50. */ if (i == 50) { telldir_50 = smbc_telldir(dhandle); torture_assert_goto( tctx, telldir_50 != (off_t)-1, success, done, talloc_asprintf(tctx, "telldir failed file %s\n", direntries[i]->name)); } if (ISDOT(direntries[i]->name)) { continue; } if (ISDOTDOT(direntries[i]->name)) { continue; } /* Ensure all our files exist. */ for (j = 0; j < 100; j++) { if (strcmp(direntries[i]->name, filename[j]) == 0) { found = true; } } torture_assert_goto( tctx, found, success, done, talloc_asprintf(tctx, "failed to find file %s\n", direntries[i]->name)); } /* * We're seeking on in-memory lists here, so * whilst the handle is open we really should * get the same files back in the same order. */ ret = smbc_lseekdir(dhandle, telldir_50); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to seek (50) directory handle for '%s'", dname)); DEBUG(0,("torture_libsmbclient_readdirplus_seek seek\n")); for (i = 51; i < 102; i++) { const struct libsmb_file_info *entry = smbc_readdirplus(dhandle); torture_assert_goto( tctx, entry == direntries[i], success, done, talloc_asprintf(tctx, "after seek - failed to find " "file %s - got %s\n", direntries[i]->name, entry->name)); } /* Seek back to the start. */ ret = smbc_lseekdir(dhandle, 0); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to seek directory handle to start for '%s'", dname)); /* * Mix getdents/readdir/readdirplus with lseek to ensure * we get the same result. */ /* Allocate the space for 20 entries. * Tricky as we need to allocate 20 struct smbc_dirent's + space * for the name lengths. */ getdentries_size = 20 * (sizeof(struct smbc_dirent) + strlen("test_readdirplus_1000.txt") + 1); getdentries = (struct smbc_dirent *)talloc_array_size(tctx, getdentries_size, 1); torture_assert_not_null_goto( tctx, getdentries, success, done, "talloc fail"); ret = smbc_getdents(dhandle, getdentries, getdentries_size); torture_assert_goto(tctx, (ret != -1), success, done, talloc_asprintf(tctx, "smbd_getdents(1) for '%s' failed\n", dname)); telldir_20 = smbc_telldir(dhandle); torture_assert_goto( tctx, telldir_20 != (off_t)-1, success, done, "telldir (20) failed\n"); /* Read another 20. */ ret = smbc_getdents(dhandle, getdentries, getdentries_size); torture_assert_goto(tctx, (ret != -1), success, done, talloc_asprintf(tctx, "smbd_getdents(2) for '%s' failed\n", dname)); /* Seek back to 20. */ ret = smbc_lseekdir(dhandle, telldir_20); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to seek (20) directory handle for '%s'", dname)); /* Read with readdir. */ dirent_20 = smbc_readdir(dhandle); torture_assert_not_null_goto( tctx, dirent_20, success, done, "smbc_readdir (20) failed\n"); /* Ensure the getdents and readdir names are the same. */ ret = strcmp(dirent_20->name, getdentries[0].name); torture_assert_goto( tctx, ret == 0, success, done, talloc_asprintf(tctx, "after seek (20) readdir name mismatch " "file %s - got %s\n", dirent_20->name, getdentries[0].name)); /* Seek back to 20. */ ret = smbc_lseekdir(dhandle, telldir_20); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to seek (20) directory handle for '%s'", dname)); /* Read with readdirplus. */ direntries_20 = smbc_readdirplus(dhandle); torture_assert_not_null_goto( tctx, direntries_20, success, done, "smbc_readdirplus (20) failed\n"); /* Ensure the readdirplus and readdir names are the same. */ ret = strcmp(dirent_20->name, direntries_20->name); torture_assert_goto( tctx, ret == 0, success, done, talloc_asprintf(tctx, "after seek (20) readdirplus name mismatch " "file %s - got %s\n", dirent_20->name, direntries_20->name)); /* Seek back to 20. */ ret = smbc_lseekdir(dhandle, telldir_20); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to seek (20) directory handle for '%s'", dname)); /* Read with readdirplus2. */ direntriesplus_20 = smbc_readdirplus2(dhandle, &st2); torture_assert_not_null_goto( tctx, direntriesplus_20, success, done, "smbc_readdirplus2 (20) failed\n"); /* Ensure the readdirplus2 and readdirplus names are the same. */ ret = strcmp(direntries_20->name, direntriesplus_20->name); torture_assert_goto( tctx, ret == 0, success, done, talloc_asprintf(tctx, "after seek (20) readdirplus2 name mismatch " "file %s - got %s\n", dirent_20->name, direntries_20->name)); /* Ensure doing stat gets the same data. */ plus2_stat_path = talloc_asprintf(tctx, "%s/%s", dname, direntriesplus_20->name); torture_assert_not_null_goto( tctx, plus2_stat_path, success, done, "talloc fail\n"); ret = smbc_stat(plus2_stat_path, &st); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to stat file '%s'", plus2_stat_path)); torture_assert_int_equal(tctx, st.st_ino, st2.st_ino, talloc_asprintf(tctx, "file %s mismatched ino value " "stat got %"PRIx64" readdirplus2 got %"PRIx64"" , plus2_stat_path, (uint64_t)st.st_ino, (uint64_t)st2.st_ino)); torture_assert_int_equal(tctx, st.st_dev, st2.st_dev, talloc_asprintf(tctx, "file %s mismatched dev value " "stat got %"PRIx64" readdirplus2 got %"PRIx64"" , plus2_stat_path, (uint64_t)st.st_dev, (uint64_t)st2.st_dev)); ret = smbc_closedir(dhandle); torture_assert_int_equal(tctx, ret, 0, talloc_asprintf(tctx, "failed to close directory handle for '%s'", dname)); dhandle = -1; success = true; done: /* Clean up. */ if (dhandle != -1) { smbc_closedir(dhandle); } for (i = 0; i < 100; i++) { if (full_filename[i] != NULL) { smbc_unlink(full_filename[i]); } } if (dname != NULL) { smbc_rmdir(dname); } smbc_free_context(ctx, 1); return success; } #ifndef SMBC_FILE_MODE #define SMBC_FILE_MODE (S_IFREG | 0444) #endif static bool torture_libsmbclient_readdirplus2(struct torture_context *tctx) { SMBCCTX *ctx = NULL; int dhandle = -1; int fhandle = -1; bool found = false; bool success = false; const char *filename = NULL; struct stat st2 = {0}; struct stat st = {0}; int ret; const char *smburl = torture_setting_string(tctx, "smburl", NULL); if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server/share missing\n"); } torture_assert_goto(tctx, torture_libsmbclient_init_context(tctx, &ctx), success, done, ""); smbc_set_context(ctx); filename = talloc_asprintf(tctx, "%s/test_readdirplus.txt", smburl); if (filename == NULL) { torture_fail_goto(tctx, done, "talloc fail\n"); } /* Ensure the file doesn't exist. */ smbc_unlink(filename); /* Create it. */ fhandle = smbc_creat(filename, 0666); if (fhandle < 0) { torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to create file '%s': %s", filename, strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to close handle for '%s'", filename)); dhandle = smbc_opendir(smburl); if (dhandle < 0) { int saved_errno = errno; smbc_unlink(filename); torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to obtain " "directory handle for '%s' : %s", smburl, strerror(saved_errno))); } /* readdirplus2 to ensure we see the new file. */ for (;;) { const struct libsmb_file_info *exstat = smbc_readdirplus2(dhandle, &st2); if (exstat == NULL) { break; } if (strcmp(exstat->name, "test_readdirplus.txt") == 0) { found = true; break; } } if (!found) { smbc_unlink(filename); torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to find file '%s'", filename)); } /* Ensure mode is as expected. */ /* * New file gets SMBC_FILE_MODE plus * archive bit -> S_IXUSR * !READONLY -> S_IWUSR. */ torture_assert_int_equal_goto(tctx, st2.st_mode, SMBC_FILE_MODE|S_IXUSR|S_IWUSR, success, done, talloc_asprintf(tctx, "file %s st_mode should be 0%o, got 0%o'", filename, SMBC_FILE_MODE|S_IXUSR|S_IWUSR, (unsigned int)st2.st_mode)); /* Ensure smbc_stat() gets the same data. */ ret = smbc_stat(filename, &st); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to stat file '%s'", filename)); torture_assert_int_equal_goto(tctx, st2.st_ino, st.st_ino, success, done, talloc_asprintf(tctx, "filename '%s' ino mismatch. " "From smbc_readdirplus2 = %"PRIx64" " "From smbc_stat = %"PRIx64"", filename, (uint64_t)st2.st_ino, (uint64_t)st.st_ino)); /* Remove it again. */ smbc_unlink(filename); ret = smbc_closedir(dhandle); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to close directory handle for '%s'", filename)); success = true; done: smbc_free_context(ctx, 1); return success; } bool torture_libsmbclient_configuration(struct torture_context *tctx) { SMBCCTX *ctx; bool ok = true; ctx = smbc_new_context(); torture_assert(tctx, ctx, "failed to get new context"); torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); torture_comment(tctx, "Testing smbc_(set|get)Debug\n"); smbc_setDebug(ctx, DEBUGLEVEL); torture_assert_int_equal_goto(tctx, smbc_getDebug(ctx), DEBUGLEVEL, ok, done, "failed to set DEBUGLEVEL"); torture_comment(tctx, "Testing smbc_(set|get)NetbiosName\n"); smbc_setNetbiosName(ctx, discard_const("torture_netbios")); torture_assert_str_equal_goto(tctx, smbc_getNetbiosName(ctx), "torture_netbios", ok, done, "failed to set NetbiosName"); torture_comment(tctx, "Testing smbc_(set|get)Workgroup\n"); smbc_setWorkgroup(ctx, discard_const("torture_workgroup")); torture_assert_str_equal_goto(tctx, smbc_getWorkgroup(ctx), "torture_workgroup", ok, done, "failed to set Workgroup"); torture_comment(tctx, "Testing smbc_(set|get)User\n"); smbc_setUser(ctx, "torture_user"); torture_assert_str_equal_goto(tctx, smbc_getUser(ctx), "torture_user", ok, done, "failed to set User"); torture_comment(tctx, "Testing smbc_(set|get)Timeout\n"); smbc_setTimeout(ctx, 12345); torture_assert_int_equal_goto(tctx, smbc_getTimeout(ctx), 12345, ok, done, "failed to set Timeout"); done: smbc_free_context(ctx, 1); return ok; } bool torture_libsmbclient_options(struct torture_context *tctx) { SMBCCTX *ctx; bool ok = true; ctx = smbc_new_context(); torture_assert(tctx, ctx, "failed to get new context"); torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); torture_comment(tctx, "Testing smbc_(set|get)OptionDebugToStderr\n"); smbc_setOptionDebugToStderr(ctx, true); torture_assert_goto(tctx, smbc_getOptionDebugToStderr(ctx), ok, done, "failed to set OptionDebugToStderr"); torture_comment(tctx, "Testing smbc_(set|get)OptionFullTimeNames\n"); smbc_setOptionFullTimeNames(ctx, true); torture_assert_goto(tctx, smbc_getOptionFullTimeNames(ctx), ok, done, "failed to set OptionFullTimeNames"); torture_comment(tctx, "Testing smbc_(set|get)OptionOpenShareMode\n"); smbc_setOptionOpenShareMode(ctx, SMBC_SHAREMODE_DENY_ALL); torture_assert_int_equal_goto(tctx, smbc_getOptionOpenShareMode(ctx), SMBC_SHAREMODE_DENY_ALL, ok, done, "failed to set OptionOpenShareMode"); torture_comment(tctx, "Testing smbc_(set|get)OptionUserData\n"); smbc_setOptionUserData(ctx, (void *)discard_const("torture_user_data")); torture_assert_str_equal_goto(tctx, (const char*)smbc_getOptionUserData(ctx), "torture_user_data", ok, done, "failed to set OptionUserData"); torture_comment(tctx, "Testing smbc_(set|get)OptionSmbEncryptionLevel\n"); smbc_setOptionSmbEncryptionLevel(ctx, SMBC_ENCRYPTLEVEL_REQUEST); torture_assert_int_equal_goto(tctx, smbc_getOptionSmbEncryptionLevel(ctx), SMBC_ENCRYPTLEVEL_REQUEST, ok, done, "failed to set OptionSmbEncryptionLevel"); torture_comment(tctx, "Testing smbc_(set|get)OptionCaseSensitive\n"); smbc_setOptionCaseSensitive(ctx, false); torture_assert_goto(tctx, !smbc_getOptionCaseSensitive(ctx), ok, done, "failed to set OptionCaseSensitive"); torture_comment(tctx, "Testing smbc_(set|get)OptionBrowseMaxLmbCount\n"); smbc_setOptionBrowseMaxLmbCount(ctx, 2); torture_assert_int_equal_goto(tctx, smbc_getOptionBrowseMaxLmbCount(ctx), 2, ok, done, "failed to set OptionBrowseMaxLmbCount"); torture_comment(tctx, "Testing smbc_(set|get)OptionUrlEncodeReaddirEntries\n"); smbc_setOptionUrlEncodeReaddirEntries(ctx, true); torture_assert_goto(tctx, smbc_getOptionUrlEncodeReaddirEntries(ctx), ok, done, "failed to set OptionUrlEncodeReaddirEntries"); torture_comment(tctx, "Testing smbc_(set|get)OptionOneSharePerServer\n"); smbc_setOptionOneSharePerServer(ctx, true); torture_assert_goto(tctx, smbc_getOptionOneSharePerServer(ctx), ok, done, "failed to set OptionOneSharePerServer"); torture_comment(tctx, "Testing smbc_(set|get)OptionUseKerberos\n"); smbc_setOptionUseKerberos(ctx, false); torture_assert_goto(tctx, !smbc_getOptionUseKerberos(ctx), ok, done, "failed to set OptionUseKerberos"); torture_comment(tctx, "Testing smbc_(set|get)OptionFallbackAfterKerberos\n"); smbc_setOptionFallbackAfterKerberos(ctx, false); torture_assert_goto(tctx, !smbc_getOptionFallbackAfterKerberos(ctx), ok, done, "failed to set OptionFallbackAfterKerberos"); torture_comment(tctx, "Testing smbc_(set|get)OptionNoAutoAnonymousLogin\n"); smbc_setOptionNoAutoAnonymousLogin(ctx, true); torture_assert_goto(tctx, smbc_getOptionNoAutoAnonymousLogin(ctx), ok, done, "failed to set OptionNoAutoAnonymousLogin"); torture_comment(tctx, "Testing smbc_(set|get)OptionUseCCache\n"); smbc_setOptionUseCCache(ctx, true); torture_assert_goto(tctx, smbc_getOptionUseCCache(ctx), ok, done, "failed to set OptionUseCCache"); done: smbc_free_context(ctx, 1); return ok; } static bool torture_libsmbclient_list_shares(struct torture_context *tctx) { const char *smburl = torture_setting_string(tctx, "smburl", NULL); struct smbc_dirent *dirent = NULL; SMBCCTX *ctx = NULL; int dhandle = -1; bool ipc_share_found = false; bool ok = true; if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server missing\n"); } ok = torture_libsmbclient_init_context(tctx, &ctx); torture_assert_goto(tctx, ok, ok, out, "Failed to init context"); smbc_set_context(ctx); torture_comment(tctx, "Listing: %s\n", smburl); dhandle = smbc_opendir(smburl); torture_assert_int_not_equal_goto(tctx, dhandle, -1, ok, out, "Failed to open smburl"); while((dirent = smbc_readdir(dhandle)) != NULL) { torture_comment(tctx, "DIR: %s\n", dirent->name); torture_assert_not_null_goto(tctx, dirent->name, ok, out, "Failed to read name"); if (strequal(dirent->name, "IPC$")) { ipc_share_found = true; } } torture_assert_goto(tctx, ipc_share_found, ok, out, "Failed to list IPC$ share"); out: smbc_closedir(dhandle); return ok; } static bool torture_libsmbclient_utimes(struct torture_context *tctx) { const char *smburl = torture_setting_string(tctx, "smburl", NULL); SMBCCTX *ctx = NULL; struct stat st; int fhandle, ret; struct timeval tbuf[2]; bool ok; if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server missing\n"); } ok = torture_libsmbclient_init_context(tctx, &ctx); torture_assert(tctx, ok, "Failed to init context"); smbc_set_context(ctx); fhandle = smbc_open(smburl, O_RDWR|O_CREAT, 0644); torture_assert_int_not_equal(tctx, fhandle, -1, "smbc_open failed"); ret = smbc_fstat(fhandle, &st); torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed"); tbuf[0] = convert_timespec_to_timeval(get_atimespec(&st)); tbuf[1] = convert_timespec_to_timeval(get_mtimespec(&st)); tbuf[1] = timeval_add(&tbuf[1], 0, 100000); /* 100 msec */ ret = smbc_utimes(smburl, tbuf); torture_assert_int_not_equal(tctx, ret, -1, "smbc_utimes failed"); ret = smbc_fstat(fhandle, &st); torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed"); torture_assert_int_equal( tctx, get_mtimensec(&st) / 1000, tbuf[1].tv_usec, "smbc_utimes did not update msec"); smbc_close(fhandle); smbc_unlink(smburl); return true; } static bool torture_libsmbclient_noanon_list(struct torture_context *tctx) { const char *smburl = torture_setting_string(tctx, "smburl", NULL); struct smbc_dirent *dirent = NULL; SMBCCTX *ctx = NULL; int dhandle = -1; bool ok = true; if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server missing\n"); } ok = torture_libsmbclient_init_context(tctx, &ctx); torture_assert_goto(tctx, ok, ok, out, "Failed to init context"); torture_comment(tctx, "Testing smbc_setOptionNoAutoAnonymousLogin\n"); smbc_setOptionNoAutoAnonymousLogin(ctx, true); smbc_set_context(ctx); torture_comment(tctx, "Listing: %s\n", smburl); dhandle = smbc_opendir(smburl); torture_assert_int_not_equal_goto(tctx, dhandle, -1, ok, out, "Failed to open smburl"); while((dirent = smbc_readdir(dhandle)) != NULL) { torture_comment(tctx, "DIR: %s\n", dirent->name); torture_assert_not_null_goto(tctx, dirent->name, ok, out, "Failed to read name"); } out: smbc_closedir(dhandle); return ok; } static bool torture_libsmbclient_rename(struct torture_context *tctx) { SMBCCTX *ctx = NULL; int fhandle = -1; bool success = false; const char *filename_src = NULL; const char *filename_dst = NULL; int ret; const char *smburl = torture_setting_string(tctx, "smburl", NULL); if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server/share missing\n"); } torture_assert_goto(tctx, torture_libsmbclient_init_context(tctx, &ctx), success, done, ""); smbc_set_context(ctx); filename_src = talloc_asprintf(tctx, "%s/src", smburl); if (filename_src == NULL) { torture_fail_goto(tctx, done, "talloc fail\n"); } filename_dst = talloc_asprintf(tctx, "%s/dst", smburl); if (filename_dst == NULL) { torture_fail_goto(tctx, done, "talloc fail\n"); } /* Ensure the files don't exist. */ smbc_unlink(filename_src); smbc_unlink(filename_dst); /* Create them. */ fhandle = smbc_creat(filename_src, 0666); if (fhandle < 0) { torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to create file '%s': %s", filename_src, strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to close handle for '%s'", filename_src)); fhandle = smbc_creat(filename_dst, 0666); if (fhandle < 0) { torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to create file '%s': %s", filename_dst, strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "failed to close handle for '%s'", filename_dst)); ret = smbc_rename(filename_src, filename_dst); /* * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14938 * gives ret == -1, but errno = 0 for overwrite renames * over SMB2. */ torture_assert_int_equal_goto(tctx, ret, 0, success, done, talloc_asprintf(tctx, "smbc_rename '%s' -> '%s' failed with %s\n", filename_src, filename_dst, strerror(errno))); /* Remove them again. */ smbc_unlink(filename_src); smbc_unlink(filename_dst); success = true; done: smbc_free_context(ctx, 1); return success; } static bool torture_libsmbclient_getatr(struct torture_context *tctx) { const char *smburl = torture_setting_string(tctx, "smburl", NULL); SMBCCTX *ctx = NULL; char *getatr_name = NULL; struct stat st = {0}; bool ok; int ret = 0; int err = 0; if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server missing\n"); } ok = torture_libsmbclient_init_context(tctx, &ctx); torture_assert(tctx, ok, "Failed to init context"); smbc_set_context(ctx); getatr_name = talloc_asprintf(tctx, "%s/noexist", smburl); if (getatr_name == NULL) { torture_result(tctx, TORTURE_FAIL, __location__": %s", "talloc fail\n"); return false; } /* Ensure the file doesn't exist. */ smbc_unlink(getatr_name); /* * smbc_stat() internally uses SMBC_getatr(). * Make sure doing getatr on a non-existent file gives * an error of -1, errno = ENOENT. */ ret = smbc_stat(getatr_name, &st); if (ret == -1) { err = errno; } torture_assert_int_equal(tctx, ret, -1, talloc_asprintf(tctx, "smbc_stat on '%s' should " "get -1, got %d\n", getatr_name, ret)); torture_assert_int_equal(tctx, err, ENOENT, talloc_asprintf(tctx, "smbc_stat on '%s' should " "get errno = ENOENT, got %s\n", getatr_name, strerror(err))); return true; } static bool torture_libsmbclient_getxattr(struct torture_context *tctx) { const char *smburl = torture_setting_string(tctx, "smburl", NULL); int fhandle = -1; SMBCCTX *ctx = NULL; char *getxattr_name = NULL; char value[4096]; bool ok = false; int ret = -1; if (smburl == NULL) { torture_fail(tctx, "option --option=torture:smburl=" "smb://user:password@server missing\n"); } ok = torture_libsmbclient_init_context(tctx, &ctx); torture_assert(tctx, ok, "Failed to init context"); smbc_set_context(ctx); getxattr_name = talloc_asprintf(tctx, "%s/getxattr", smburl); if (getxattr_name == NULL) { torture_result(tctx, TORTURE_FAIL, __location__": %s", "talloc fail\n"); return false; } /* Ensure the file doesn't exist. */ smbc_unlink(getxattr_name); /* Create testfile. */ fhandle = smbc_creat(getxattr_name, 0666); if (fhandle < 0) { torture_fail_goto(tctx, done, talloc_asprintf(tctx, "failed to create file '%s': %s", getxattr_name, strerror(errno))); } ret = smbc_close(fhandle); torture_assert_int_equal_goto(tctx, ret, 0, ok, done, talloc_asprintf(tctx, "failed to close handle for '%s'", getxattr_name)); /* * Ensure getting a non-existent attribute returns -1. */ ret = smbc_getxattr(getxattr_name, "foobar", value, sizeof(value)); torture_assert_int_equal_goto(tctx, ret, -1, ok, done, talloc_asprintf(tctx, "smbc_getxattr(foobar) on '%s' should " "get -1, got %d\n", getxattr_name, ret)); /* * Ensure getting a valid attribute computes its size. */ ret = smbc_getxattr(getxattr_name, "system.*", NULL, 0); torture_assert_goto(tctx, ret >= 0, ok, done, talloc_asprintf(tctx, "smbc_getxattr(foobar, NULL) on '%s' should " "get >=0, got %d\n", getxattr_name, ret)); /* * Ensure getting a valid attribute returns its size. */ ret = smbc_getxattr(getxattr_name, "system.*", value, sizeof(value)); torture_assert_goto(tctx, ret >= 0, ok, done, talloc_asprintf(tctx, "smbc_getxattr(foobar, value) on '%s' should " "get >=0, got %d\n", getxattr_name, ret)); ok = true; done: smbc_unlink(getxattr_name); smbc_free_context(ctx, 1); return ok; } NTSTATUS torture_libsmbclient_init(TALLOC_CTX *ctx) { struct torture_suite *suite; suite = torture_suite_create(ctx, "libsmbclient"); torture_suite_add_simple_test(suite, "version", torture_libsmbclient_version); torture_suite_add_simple_test(suite, "initialize", torture_libsmbclient_initialize); torture_suite_add_simple_test(suite, "configuration", torture_libsmbclient_configuration); torture_suite_add_simple_test(suite, "setConfiguration", torture_libsmbclient_setConfiguration); torture_suite_add_simple_test(suite, "options", torture_libsmbclient_options); torture_suite_add_simple_test(suite, "opendir", torture_libsmbclient_opendir); torture_suite_add_simple_test(suite, "list_shares", torture_libsmbclient_list_shares); torture_suite_add_simple_test(suite, "readdirplus", torture_libsmbclient_readdirplus); torture_suite_add_simple_test(suite, "readdirplus_seek", torture_libsmbclient_readdirplus_seek); torture_suite_add_simple_test(suite, "readdirplus2", torture_libsmbclient_readdirplus2); torture_suite_add_simple_test( suite, "utimes", torture_libsmbclient_utimes); torture_suite_add_simple_test( suite, "noanon_list", torture_libsmbclient_noanon_list); torture_suite_add_simple_test(suite, "rename", torture_libsmbclient_rename); torture_suite_add_simple_test(suite, "getatr", torture_libsmbclient_getatr); torture_suite_add_simple_test(suite, "getxattr", torture_libsmbclient_getxattr); suite->description = talloc_strdup(suite, "libsmbclient interface tests"); torture_register_suite(ctx, suite); return NT_STATUS_OK; }