diff options
Diffstat (limited to 'source4/torture')
-rw-r--r-- | source4/torture/raw/lock.c | 120 | ||||
-rw-r--r-- | source4/torture/raw/open.c | 198 | ||||
-rw-r--r-- | source4/torture/rpc/rpc.c | 1 | ||||
-rw-r--r-- | source4/torture/rpc/samr.c | 605 | ||||
-rw-r--r-- | source4/torture/smbtorture.c | 3 | ||||
-rw-r--r-- | source4/torture/smbtorture.h | 14 |
6 files changed, 854 insertions, 87 deletions
diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c index 7eb461048b..6c86a6f615 100644 --- a/source4/torture/raw/lock.c +++ b/source4/torture/raw/lock.c @@ -80,10 +80,14 @@ #define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \ (torture_setting_bool(_tctx, "invalid_lock_range_support", true)) +#define TARGET_SUPPORTS_SMBEXIT(_tctx) \ + (torture_setting_bool(_tctx, "smbexit_pdu_support", true)) #define TARGET_SUPPORTS_SMBLOCK(_tctx) \ (torture_setting_bool(_tctx, "smblock_pdu_support", true)) #define TARGET_SUPPORTS_OPENX_DENY_DOS(_tctx) \ (torture_setting_bool(_tctx, "openx_deny_dos_support", true)) +#define TARGET_RETURNS_RANGE_NOT_LOCKED(_tctx) \ + (torture_setting_bool(_tctx, "range_not_locked_on_file_close", true)) /* test SMBlock and SMBunlock ops */ @@ -786,7 +790,10 @@ static bool test_async(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); status = smbcli_request_simple_recv(req); - CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, "lock cancel by close was not immediate (%s)\n", __location__)); @@ -816,46 +823,57 @@ static bool test_async(struct torture_context *tctx, tree->tid = tcon.tconx.out.tid; torture_comment(tctx, "testing cancel by exit\n"); - fname = BASEDIR "\\test_exit.txt"; - fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE); - torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, - "Failed to reopen %s - %s\n", - fname, smbcli_errstr(tree))); - - io.lockx.level = RAW_LOCK_LOCKX; - io.lockx.in.file.fnum = fnum; - io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; - io.lockx.in.timeout = 0; - io.lockx.in.ulock_cnt = 0; - io.lockx.in.lock_cnt = 1; - lock[0].pid = session->pid; - lock[0].offset = 100; - lock[0].count = 10; - io.lockx.in.locks = &lock[0]; - status = smb_raw_lock(tree, &io); - CHECK_STATUS(status, NT_STATUS_OK); - - io.lockx.in.ulock_cnt = 0; - io.lockx.in.lock_cnt = 1; - io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; - io.lockx.in.timeout = 0; - status = smb_raw_lock(tree, &io); - CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + if (TARGET_SUPPORTS_SMBEXIT(tctx)) { + fname = BASEDIR "\\test_exit.txt"; + fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); - io.lockx.in.timeout = 10000; - t = time(NULL); - req = smb_raw_lock_send(tree, &io); - torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, - "Failed to setup timed lock (%s)\n", __location__)); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + io.lockx.in.timeout = 10000; + t = time(NULL); + req = smb_raw_lock_send(tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", + __location__)); + + status = smb_raw_exit(session); + CHECK_STATUS(status, NT_STATUS_OK); - status = smb_raw_exit(session); - CHECK_STATUS(status, NT_STATUS_OK); + status = smbcli_request_simple_recv(req); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); - status = smbcli_request_simple_recv(req); - CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); - - torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, - "lock cancel by exit was not immediate (%s)\n", __location__)); + torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by exit was not immediate (%s)\n", + __location__)); + } + else { + torture_comment(tctx, + " skipping test, SMBExit not supported\n"); + } torture_comment(tctx, "testing cancel by ulogoff\n"); fname = BASEDIR "\\test_ulogoff.txt"; @@ -894,15 +912,20 @@ static bool test_async(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); status = smbcli_request_simple_recv(req); - if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) { - torture_result(tctx, TORTURE_FAIL, - "lock not canceled by ulogoff - %s (ignored because of vfs_vifs fails it)\n", - nt_errstr(status)); - smb_tree_disconnect(tree); - smb_raw_exit(session); - goto done; + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) { + if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) { + torture_result(tctx, TORTURE_FAIL, + "lock not canceled by ulogoff - %s " + "(ignored because of vfs_vifs fails it)\n", + nt_errstr(status)); + smb_tree_disconnect(tree); + smb_raw_exit(session); + goto done; + } + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + } else { + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); } - CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, "lock cancel by ulogoff was not immediate (%s)\n", __location__)); @@ -942,7 +965,10 @@ static bool test_async(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); status = smbcli_request_simple_recv(req); - CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, "lock cancel by tdis was not immediate (%s)\n", __location__)); diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c index 8c4311b530..e37fd8e09a 100644 --- a/source4/torture/raw/open.c +++ b/source4/torture/raw/open.c @@ -1879,6 +1879,203 @@ done: return ret; } +/** + * Test what happens when trying to open a file with directory parameters and + * vice-versa. Also test that NTCREATEX_OPTIONS_DIRECTORY is treated as + * mandatory and FILE_ATTRIBUTE_DIRECTORY is advisory for directory + * creation/opening. + */ +static bool test_ntcreatexdir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + const char *dname = BASEDIR "\\torture_ntcreatex_dir"; + NTSTATUS status, expected_status; + bool ret = true; + int i; + uint32_t access_mask = 0; + + struct { + uint32_t open_disp; + uint32_t file_attr; + uint32_t create_options; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OPEN, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_SUPERSEDE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + + }; + + if (!torture_setup_dir(cli, BASEDIR)) { + return false; + } + + /* setup some base params. */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + * Test the validity checking for create dispositions, which is done + * against the requested parameters rather than what's actually on + * disk. + */ + for (i=0; i<ARRAY_SIZE(open_funcs); i++) { + io.ntcreatex.in.open_disposition = open_funcs[i].open_disp; + io.ntcreatex.in.file_attr = open_funcs[i].file_attr; + io.ntcreatex.in.create_options = open_funcs[i].create_options; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].open_disp); + ret = false; + } + /* Close and delete the file. */ + if (NT_STATUS_IS_OK(status)) { + if (open_funcs[i].create_options != 0) { + /* out attrib should be a directory. */ + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_DIRECTORY, "should have " + "created a directory"); + + smbcli_close(cli->tree, + io.ntcreatex.out.file.fnum); + + /* Make sure unlink fails. */ + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_IS_A_DIRECTORY, + "unlink should fail for a directory"); + + status = smbcli_rmdir(cli->tree, fname); + torture_assert_ntstatus_ok(tctx, status, + "rmdir failed"); + } else { + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_ARCHIVE, "should not have " + "created a directory"); + + smbcli_close(cli->tree, + io.ntcreatex.out.file.fnum); + + /* Make sure rmdir fails. */ + status = smbcli_rmdir(cli->tree, fname); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_A_DIRECTORY, + "rmdir should fail for a file"); + + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_ok(tctx, status, + "unlink failed"); + } + } + } + + /* Create a file. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "Failed to create file."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open the file with file_attr_dir and check the error. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "FILE_ATTRIBUTE_DIRECTORY " + "doesn't produce a hard failure."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open file with createx_option_dir and check the error. */ + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_A_DIRECTORY, + "NTCREATEX_OPTIONS_DIRECTORY will a file from being opened."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Delete the file and move onto directory testing. */ + smbcli_unlink(cli->tree, fname); + + /* Now try some tests on a directory. */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "Failed to create dir."); + + /* out attrib should be a directory. */ + torture_assert_int_equal(tctx, io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_DIRECTORY, "should have created a directory"); + + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open it with normal attr and check the error. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "FILE_ATTRIBUTE_NORMAL " + "doesn't produce a hard failure."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open it with file create_options and check the error. */ + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_IS_A_DIRECTORY, + "NTCREATEX_OPTIONS_NON_DIRECTORY_FILE should be returned "); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + +done: + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} /* basic testing of all RAW_OPEN_* calls */ @@ -1902,6 +2099,7 @@ struct torture_suite *torture_raw_open(TALLOC_CTX *mem_ctx) torture_suite_add_1smb_test(suite, "OPENX-OVER-DIR", test_openx_over_dir); torture_suite_add_1smb_test(suite, "OPEN-FOR-DELETE", test_open_for_delete); torture_suite_add_1smb_test(suite, "OPENDISP-DIR", test_ntcreatex_opendisp_dir); + torture_suite_add_1smb_test(suite, "NTCREATEDIR", test_ntcreatexdir); return suite; } diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c index 3362bad605..2ab0d12743 100644 --- a/source4/torture/rpc/rpc.c +++ b/source4/torture/rpc/rpc.c @@ -462,6 +462,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite)); torture_suite_add_suite(suite, torture_rpc_samr_workstation_auth(suite)); torture_suite_add_suite(suite, torture_rpc_samr_passwords_pwdlastset(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_passwords_badpwdcount(suite)); torture_suite_add_suite(suite, torture_rpc_samr_user_privileges(suite)); torture_suite_add_suite(suite, torture_rpc_samr_large_dc(suite)); torture_suite_add_suite(suite, torture_rpc_epmapper(suite)); diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c index b5aa761b79..8b466e8ef0 100644 --- a/source4/torture/rpc/samr.c +++ b/source4/torture/rpc/samr.c @@ -4,7 +4,7 @@ Copyright (C) Andrew Tridgell 2003 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003 - Copyright (C) Guenther Deschner 2008,2009 + Copyright (C) Guenther Deschner 2008-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 @@ -33,6 +33,10 @@ #include "libcli/security/security.h" #include "torture/rpc/rpc.h" #include "param/param.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_proto.h" +#include "../libcli/auth/schannel.h" +#include "auth/gensec/schannel_state.h" #include <unistd.h> @@ -46,6 +50,7 @@ enum torture_samr_choice { TORTURE_SAMR_PASSWORDS, TORTURE_SAMR_PASSWORDS_PWDLASTSET, + TORTURE_SAMR_PASSWORDS_BADPWDCOUNT, TORTURE_SAMR_USER_ATTRIBUTES, TORTURE_SAMR_USER_PRIVILEGES, TORTURE_SAMR_OTHER, @@ -2634,11 +2639,13 @@ static bool test_GetAliasMembership(struct dcerpc_pipe *p, /* only true for w2k8 it seems * win7, xp, w2k3 will return a 0 length array pointer */ - torture_assert(tctx, (rids.ids && !rids.count), - "samr_GetAliasMembership protocol misbehaviour"); + if (rids.ids && (rids.count == 0)) { + torture_fail(tctx, "samr_GetAliasMembership returned 0 count and a rids array"); + } #endif - torture_assert(tctx, (!rids.ids && rids.count), - "samr_GetAliasMembership protocol misbehaviour"); + if (!rids.ids && rids.count) { + torture_fail(tctx, "samr_GetAliasMembership returned non-0 count but no rids"); + } return true; } @@ -2730,17 +2737,24 @@ static bool test_QueryUserInfo_pwdlastset(struct dcerpc_pipe *p, static bool test_SamLogon(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *test_credentials, - NTSTATUS expected_result) + NTSTATUS expected_result, + bool interactive) { NTSTATUS status; struct netr_LogonSamLogonEx r; union netr_LogonLevel logon; union netr_Validation validation; uint8_t authoritative; + struct netr_IdentityInfo identity; struct netr_NetworkInfo ninfo; + struct netr_PasswordInfo pinfo; DATA_BLOB names_blob, chal, lm_resp, nt_resp; int flags = CLI_CRED_NTLM_AUTH; uint32_t samlogon_flags = 0; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator a; + + torture_assert_ntstatus_ok(tctx, dcerpc_schannel_creds(p->conn->security_state.generic_state, tctx, &creds), ""); if (lp_client_lanman_auth(tctx->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; @@ -2751,50 +2765,74 @@ static bool test_SamLogon(struct torture_context *tctx, } cli_credentials_get_ntlm_username_domain(test_credentials, tctx, - &ninfo.identity_info.account_name.string, - &ninfo.identity_info.domain_name.string); + &identity.account_name.string, + &identity.domain_name.string); + + identity.parameter_control = + MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | + MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; + identity.logon_id_low = 0; + identity.logon_id_high = 0; + identity.workstation.string = cli_credentials_get_workstation(test_credentials); + + if (interactive) { + netlogon_creds_client_authenticator(creds, &a); + + if (!E_deshash(cli_credentials_get_password(test_credentials), pinfo.lmpassword.hash)) { + ZERO_STRUCT(pinfo.lmpassword.hash); + } + E_md4hash(cli_credentials_get_password(test_credentials), pinfo.ntpassword.hash); + + if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + netlogon_creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16); + netlogon_creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16); + } else { + netlogon_creds_des_encrypt(creds, &pinfo.lmpassword); + netlogon_creds_des_encrypt(creds, &pinfo.ntpassword); + } - generate_random_buffer(ninfo.challenge, - sizeof(ninfo.challenge)); - chal = data_blob_const(ninfo.challenge, - sizeof(ninfo.challenge)); + pinfo.identity_info = identity; + logon.password = &pinfo; - names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials), - cli_credentials_get_domain(test_credentials)); + r.in.logon_level = NetlogonInteractiveInformation; + } else { + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); - status = cli_credentials_get_ntlm_response(test_credentials, tctx, - &flags, - chal, - names_blob, - &lm_resp, &nt_resp, - NULL, NULL); - torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); + names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials), + cli_credentials_get_domain(test_credentials)); - ninfo.lm.data = lm_resp.data; - ninfo.lm.length = lm_resp.length; + status = cli_credentials_get_ntlm_response(test_credentials, tctx, + &flags, + chal, + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); - ninfo.nt.data = nt_resp.data; - ninfo.nt.length = nt_resp.length; + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; - ninfo.identity_info.parameter_control = - MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | - MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; - ninfo.identity_info.logon_id_low = 0; - ninfo.identity_info.logon_id_high = 0; - ninfo.identity_info.workstation.string = cli_credentials_get_workstation(test_credentials); + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; - logon.network = &ninfo; + ninfo.identity_info = identity; + logon.network = &ninfo; + + r.in.logon_level = NetlogonNetworkInformation; + } r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(test_credentials); - r.in.logon_level = NetlogonNetworkInformation; r.in.logon = &logon; r.in.flags = &samlogon_flags; r.out.flags = &samlogon_flags; r.out.validation = &validation; r.out.authoritative = &authoritative; - torture_comment(tctx, "Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string); + torture_comment(tctx, "Testing LogonSamLogon with name %s\n", identity.account_name.string); r.in.validation_level = 6; @@ -2818,7 +2856,8 @@ static bool test_SamLogon_with_creds(struct torture_context *tctx, struct cli_credentials *machine_creds, const char *acct_name, char *password, - NTSTATUS expected_samlogon_result) + NTSTATUS expected_samlogon_result, + bool interactive) { bool ret = true; struct cli_credentials *test_credentials; @@ -2834,11 +2873,11 @@ static bool test_SamLogon_with_creds(struct torture_context *tctx, cli_credentials_set_password(test_credentials, password, CRED_SPECIFIED); - torture_comment(tctx, "testing samlogon as %s password: %s\n", - acct_name, password); + torture_comment(tctx, "testing samlogon (%s) as %s password: %s\n", + interactive ? "interactive" : "network", acct_name, password); if (!test_SamLogon(tctx, p, test_credentials, - expected_samlogon_result)) { + expected_samlogon_result, interactive)) { torture_warning(tctx, "new password did not work\n"); ret = false; } @@ -2904,7 +2943,8 @@ static bool test_SetPassword_level(struct dcerpc_pipe *p, machine_creds, acct_name, *password, - expected_samlogon_result)) { + expected_samlogon_result, + false)) { ret = false; } @@ -3337,6 +3377,428 @@ static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p, return ret; } +static bool test_QueryUserInfo_badpwdcount(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *badpwdcount) +{ + union samr_UserInfo *info; + struct samr_QueryUserInfo r; + + r.in.user_handle = handle; + r.in.level = 3; + r.out.info = &info; + + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo(p, tctx, &r), + "failed to query userinfo"); + + *badpwdcount = info->info3.bad_password_count; + + torture_comment(tctx, " (bad password count: %d)\n", *badpwdcount); + + return true; +} + +static bool test_reset_badpwdcount(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *user_handle, + uint32_t acct_flags, + char **password) +{ + struct samr_SetUserInfo r; + union samr_UserInfo user_info; + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + + torture_comment(tctx, "Testing SetUserInfo level 16 (enable account)\n"); + + user_info.info16.acct_flags = acct_flags; + user_info.info16.acct_flags &= ~ACB_DISABLED; + + r.in.user_handle = user_handle; + r.in.level = 16; + r.in.info = &user_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo(p, tctx, &r), + "failed to enable user"); + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + + return true; +} + +static bool test_Password_badpwdcount(struct dcerpc_pipe *p, + struct dcerpc_pipe *np, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials, + const char *comment, + bool disable, + bool interactive, + NTSTATUS expected_success_status, + struct samr_DomInfo1 *info1, + struct samr_DomInfo12 *info12) +{ + union samr_DomainInfo info; + char **passwords; + int i; + uint32_t badpwdcount, tmp; + uint32_t password_history_length = 12; + uint32_t lockout_threshold = 15; + + torture_comment(tctx, "\nTesting bad pwd count with: %s\n", comment); + + torture_assert(tctx, password_history_length < lockout_threshold, + "password history length needs to be smaller than account lockout threshold for this test"); + + + /* set policies */ + + info.info1 = *info1; + + info.info1.password_history_length = password_history_length; + + { + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainPasswordInformation; + r.in.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_SetDomainInfo(p, tctx, &r), + "failed to set domain info level 1"); + } + + info.info12 = *info12; + + info.info12.lockout_threshold = lockout_threshold; + + { + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainLockoutInformation; + r.in.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_SetDomainInfo(p, tctx, &r), + "failed to set domain info level 12"); + } + + /* reset bad pwd count */ + + torture_assert(tctx, + test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), ""); + + + /* enable or disable account */ + { + struct samr_SetUserInfo r; + union samr_UserInfo user_info; + + torture_comment(tctx, "Testing SetUserInfo level 16 (%s account)\n", + disable ? "disable" : "enable"); + + user_info.info16.acct_flags = acct_flags; + if (disable) { + user_info.info16.acct_flags |= ACB_DISABLED; + } else { + user_info.info16.acct_flags &= ~ACB_DISABLED; + } + + r.in.user_handle = user_handle; + r.in.level = 16; + r.in.info = &user_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo(p, tctx, &r), + "failed to enable user"); + } + + + /* setup password history */ + + passwords = talloc_array(tctx, char *, password_history_length); + + for (i=0; i < password_history_length; i++) { + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + passwords[i] = talloc_strdup(tctx, *password); + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + expected_success_status, interactive)) { + torture_fail(tctx, "failed to auth with latest password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(p, tctx, user_handle, &badpwdcount), ""); + + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + + /* test with wrong password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, "random_crap", + NT_STATUS_WRONG_PASSWORD, interactive)) { + torture_fail(tctx, "succeeded to authenticate with wrong password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(p, tctx, user_handle, &badpwdcount), ""); + + torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1"); + + + /* test with latest good password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + passwords[password_history_length-1], + expected_success_status, interactive)) { + torture_fail(tctx, "succeeded to authenticate with wrong password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(p, tctx, user_handle, &badpwdcount), ""); + + if (disable) { + torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1"); + } else { + /* only enabled accounts get the bad pwd count reset upon + * successful logon */ + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + tmp = badpwdcount; + + + /* test password history */ + + for (i=0; i < password_history_length; i++) { + + torture_comment(tctx, "Testing bad password count behavior with " + "password #%d of #%d\n", i, password_history_length); + + /* - network samlogon will succeed auth and not + * increase badpwdcount for 2 last entries + * - interactive samlogon only for the last one */ + + if (i == password_history_length - 1 || + (i == password_history_length - 2 && !interactive)) { + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + expected_success_status, interactive)) { + torture_fail(tctx, talloc_asprintf(tctx, "succeeded to authenticate with old password (#%d of #%d in history)", i, password_history_length)); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(p, tctx, user_handle, &badpwdcount), ""); + + if (disable) { + /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE* for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount"); + } else { + /* torture_comment(tctx, "expecting bad pwd count to be 0 for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + tmp = badpwdcount; + + continue; + } + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + NT_STATUS_WRONG_PASSWORD, interactive)) { + torture_fail(tctx, talloc_asprintf(tctx, "succeeded to authenticate with old password (#%d of #%d in history)", i, password_history_length)); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(p, tctx, user_handle, &badpwdcount), ""); + + /* - network samlogon will fail auth but not increase + * badpwdcount for 3rd last entry + * - interactive samlogon for 3rd and 2nd last entry */ + + if (i == password_history_length - 3 || + (i == password_history_length - 2 && interactive)) { + /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE * by one for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount"); + } else { + /* torture_comment(tctx, "expecting bad pwd count to increase by one for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp + 1, "unexpected badpwdcount"); + } + + tmp = badpwdcount; + } + + return true; +} + +static bool test_Password_badpwdcount_wrap(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials) +{ + union samr_DomainInfo *q_info, s_info; + struct samr_DomInfo1 info1, _info1; + struct samr_DomInfo12 info12, _info12; + bool ret = true; + struct dcerpc_binding *b; + struct dcerpc_pipe *np; + int i; + + struct { + const char *comment; + bool disabled; + bool interactive; + NTSTATUS expected_success_status; + } creds[] = { + { + .comment = "network logon (disabled account)", + .disabled = true, + .interactive = false, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "network logon (enabled account)", + .disabled = false, + .interactive = false, + .expected_success_status= NT_STATUS_OK + }, + { + .comment = "interactive logon (disabled account)", + .disabled = true, + .interactive = true, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "interactive logon (enabled account)", + .disabled = false, + .interactive = true, + .expected_success_status= NT_STATUS_OK + }, + }; + + /* setup netlogon schannel pipe */ + + torture_assert_ntstatus_ok(tctx, torture_rpc_binding(tctx, &b), "failed to obtain rpc binding"); + + b->flags &= ~DCERPC_AUTH_OPTIONS; + b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128; + + torture_assert_ntstatus_ok(tctx, dcerpc_pipe_connect_b(tctx, &np, b, &ndr_table_netlogon, + machine_credentials, tctx->ev, tctx->lp_ctx), + "failed to connect to NETLOGON pipe"); + + /* backup old policies */ + + { + struct samr_QueryDomainInfo2 r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainPasswordInformation; + r.out.info = &q_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryDomainInfo2(p, tctx, &r), + "failed to query domain info level 1"); + + info1 = q_info->info1; + } + + { + struct samr_QueryDomainInfo2 r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainLockoutInformation; + r.out.info = &q_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryDomainInfo2(p, tctx, &r), + "failed to query domain info level 12"); + + info12 = q_info->info12; + } + + _info1 = info1; + _info12 = info12; + + /* run tests */ + + for (i=0; i < ARRAY_SIZE(creds); i++) { + + /* skip trust tests for now */ + if (acct_flags & ACB_WSTRUST || + acct_flags & ACB_SVRTRUST || + acct_flags & ACB_DOMTRUST) { + continue; + } + + ret &= test_Password_badpwdcount(p, np, tctx, acct_flags, acct_name, + domain_handle, user_handle, password, + machine_credentials, + creds[i].comment, + creds[i].disabled, + creds[i].interactive, + creds[i].expected_success_status, + &_info1, &_info12); + if (!ret) { + torture_warning(tctx, "TEST #%d (%s) failed\n", i, creds[i].comment); + } else { + torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment); + } + } + + /* restore policies */ + + s_info.info1 = info1; + + { + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainPasswordInformation; + r.in.info = &s_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_SetDomainInfo(p, tctx, &r), + "failed to set domain info level 1"); + } + + s_info.info12 = info12; + + { + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = DomainLockoutInformation; + r.in.info = &s_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_SetDomainInfo(p, tctx, &r), + "failed to set domain info level 12"); + } + + return ret; +} + static bool test_DeleteUser_with_privs(struct dcerpc_pipe *p, struct dcerpc_pipe *lp, struct torture_context *tctx, @@ -3836,6 +4298,25 @@ static bool test_user_ops(struct dcerpc_pipe *p, break; + case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: + + /* test bad pwd count change behaviour */ + if (!test_Password_badpwdcount_wrap(p, tctx, base_acct_flags, + base_acct_name, + domain_handle, + user_handle, &password, + machine_credentials)) { + ret = false; + } + + if (ret == true) { + torture_comment(tctx, "badPwdCount test succeeded\n"); + } else { + torture_warning(tctx, "badPwdCount test failed\n"); + } + + break; + case TORTURE_SAMR_USER_PRIVILEGES: { struct dcerpc_pipe *lp; @@ -6527,12 +7008,13 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, } break; case TORTURE_SAMR_PASSWORDS_PWDLASTSET: + case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, ctx->machine_credentials); } ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, ctx->machine_credentials, true); if (!ret) { - torture_warning(tctx, "Testing PASSWORDS PWDLASTSET on domain %s failed!\n", dom_sid_string(tctx, sid)); + torture_warning(tctx, "Testing PASSWORDS PWDLASTSET or BADPWDCOUNT on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_MANY_ACCOUNTS: @@ -7123,3 +7605,46 @@ struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx) return suite; } + +static bool torture_rpc_samr_badpwdcount(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_PASSWORDS_BADPWDCOUNT; + ctx->machine_credentials = machine_credentials; + + ret &= test_Connect(p, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(p, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_passwords_badpwdcount(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR-PASSWORDS-BADPWDCOUNT"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME_PWD); + + torture_rpc_tcase_add_test_creds(tcase, "badPwdCount", + torture_rpc_samr_badpwdcount); + + return suite; +} diff --git a/source4/torture/smbtorture.c b/source4/torture/smbtorture.c index 8e0a25b032..53e860a144 100644 --- a/source4/torture/smbtorture.c +++ b/source4/torture/smbtorture.c @@ -550,8 +550,11 @@ int main(int argc,char *argv[]) lp_set_cmdline(cmdline_lp_ctx, "torture:onefs", "true"); lp_set_cmdline(cmdline_lp_ctx, "torture:openx_deny_dos_support", "false"); + lp_set_cmdline(cmdline_lp_ctx, "torture:range_not_locked_on_file_close", "false"); lp_set_cmdline(cmdline_lp_ctx, "torture:sacl_support", "false"); lp_set_cmdline(cmdline_lp_ctx, "torture:ea_support", "false"); + lp_set_cmdline(cmdline_lp_ctx, "torture:smbexit_pdu_support", + "false"); lp_set_cmdline(cmdline_lp_ctx, "torture:smblock_pdu_support", "false"); lp_set_cmdline(cmdline_lp_ctx, "torture:2_step_break_to_none", diff --git a/source4/torture/smbtorture.h b/source4/torture/smbtorture.h index 38969f1bcc..5b12f4e3f5 100644 --- a/source4/torture/smbtorture.h +++ b/source4/torture/smbtorture.h @@ -74,6 +74,15 @@ bool torture_register_suite(struct torture_suite *suite); * This parameter specifies whether the server supports the DENY_DOS open mode * of the SMBOpenX PDU. */ +/* torture:range_not_locked_on_file_close + * + * When a byte range lock is pending, and the file which is being locked is + * closed, Windows servers return the error NT_STATUS_RANGE_NOT_LOCKED. This + * is strange, as this error is meant to be returned only for unlock requests. + * When true, torture will expect the Windows behavior, otherwise it will + * expect the more logical NT_STATUS_LOCK_NOT_GRANTED. + */ + /* torture:sacl_support * * This parameter specifies whether the server supports the setting and @@ -81,6 +90,10 @@ bool torture_register_suite(struct torture_suite *suite); * supports the use of the SEC_FLAG_SYSTEM_SECURITY bit in the open access * mask.*/ +/* torture:smbexit_pdu_support + * + * This parameter specifies whether the server supports the SMBExit (0x11) PDU. */ + /* torture:smblock_pdu_support * * This parameter specifies whether the server supports the SMBLock (0x0C) PDU. */ @@ -119,4 +132,5 @@ bool torture_register_suite(struct torture_suite *suite); * denied. When true, torture will expect NT_STATUS_OBJECT_NAME_NOT_FOUND * rather than NT_STATUS_ACCESS_DENIED when trying to open one of these files. */ + #endif /* __SMBTORTURE_H__ */ |