/* Unix SMB/CIFS implementation. test suite for samr rpc operations Copyright (C) Andrew Tridgell 2003 Copyright (C) Andrew Bartlett 2003 Copyright (C) Jelmer Vernooij 2005-2007 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 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 "torture/torture.h" #include #include "system/time.h" #include "system/network.h" #include "librpc/gen_ndr/lsa.h" #include "librpc/gen_ndr/ndr_netlogon.h" #include "librpc/gen_ndr/ndr_netlogon_c.h" #include "librpc/gen_ndr/ndr_samr_c.h" #include "librpc/gen_ndr/ndr_lsa_c.h" #include "lib/crypto/crypto.h" #include "libcli/auth/libcli_auth.h" #include "libcli/security/security.h" #include "torture/rpc/torture_rpc.h" #include "param/param.h" #include "auth/gensec/gensec.h" #include "auth/gensec/gensec_proto.h" #include "../libcli/auth/schannel.h" #include "torture/util.h" #include "source4/librpc/rpc/dcerpc.h" #include "librpc/rpc/dcerpc_samr.h" #include "source3/rpc_client/init_samr.h" #include "lib/crypto/gnutls_helpers.h" #undef strcasecmp #define TEST_ACCOUNT_NAME "samrtorturetest" #define TEST_ACCOUNT_NAME_PWD "samrpwdlastset" #define TEST_ALIASNAME "samrtorturetestalias" #define TEST_GROUPNAME "samrtorturetestgroup" #define TEST_MACHINENAME "samrtestmach$" #define TEST_DOMAINNAME "samrtestdom$" #include #include enum torture_samr_choice { TORTURE_SAMR_PASSWORDS, TORTURE_SAMR_PASSWORDS_PWDLASTSET, TORTURE_SAMR_PASSWORDS_BADPWDCOUNT, TORTURE_SAMR_PASSWORDS_LOCKOUT, TORTURE_SAMR_USER_ATTRIBUTES, TORTURE_SAMR_USER_PRIVILEGES, TORTURE_SAMR_OTHER, TORTURE_SAMR_MANY_ACCOUNTS, TORTURE_SAMR_MANY_GROUPS, TORTURE_SAMR_MANY_ALIASES }; struct torture_samr_context { struct policy_handle handle; struct cli_credentials *machine_credentials; enum torture_samr_choice choice; uint32_t num_objects_large_dc; }; static bool test_QueryUserInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle); static bool test_QueryUserInfo2(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle); static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle); static bool test_ChangePassword(struct dcerpc_pipe *p, struct torture_context *tctx, const char *acct_name, struct policy_handle *domain_handle, char **password); static void init_lsa_String(struct lsa_String *string, const char *s) { string->string = s; } static void init_lsa_StringLarge(struct lsa_StringLarge *string, const char *s) { string->string = s; } static void init_lsa_BinaryString(struct lsa_BinaryString *string, const char *s, uint32_t length) { string->length = length; string->size = length; string->array = (uint16_t *)discard_const(s); } bool test_samr_handle_Close(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_Close r; r.in.handle = handle; r.out.handle = handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Close_r(b, tctx, &r), "Close failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Close failed"); return true; } static bool test_Shutdown(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_Shutdown r; if (!torture_setting_bool(tctx, "dangerous", false)) { torture_skip(tctx, "samr_Shutdown disabled - enable dangerous tests to use\n"); return true; } r.in.connect_handle = handle; torture_comment(tctx, "Testing samr_Shutdown\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_Shutdown_r(b, tctx, &r), "Shutdown failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Shutdown failed"); return true; } static bool test_SetDsrmPassword(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_SetDsrmPassword r; struct lsa_String string; struct samr_Password hash; if (!torture_setting_bool(tctx, "dangerous", false)) { torture_skip(tctx, "samr_SetDsrmPassword disabled - enable dangerous tests to use"); } E_md4hash("TeSTDSRM123", hash.hash); init_lsa_String(&string, "Administrator"); r.in.name = &string; r.in.unknown = 0; r.in.hash = &hash; torture_comment(tctx, "Testing samr_SetDsrmPassword\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDsrmPassword_r(b, tctx, &r), "SetDsrmPassword failed"); torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_SUPPORTED, "SetDsrmPassword failed"); return true; } static bool test_QuerySecurity(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QuerySecurity r; struct samr_SetSecurity s; struct sec_desc_buf *sdbuf = NULL; r.in.handle = handle; r.in.sec_info = 7; r.out.sdbuf = &sdbuf; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r), "QuerySecurity failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed"); torture_assert(tctx, sdbuf != NULL, "sdbuf is NULL"); s.in.handle = handle; s.in.sec_info = 7; s.in.sdbuf = sdbuf; if (torture_setting_bool(tctx, "samba4", false)) { torture_skip(tctx, "skipping SetSecurity test against Samba4\n"); } torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &s), "SetSecurity failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "SetSecurity failed"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r), "QuerySecurity failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed"); return true; } static bool test_SetUserInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle, uint32_t base_acct_flags, const char *base_account_name) { struct samr_SetUserInfo s; struct samr_SetUserInfo2 s2; struct samr_QueryUserInfo q; struct samr_QueryUserInfo q0; union samr_UserInfo u; union samr_UserInfo *info; bool ret = true; const char *test_account_name; uint32_t user_extra_flags = 0; if (!torture_setting_bool(tctx, "samba3", false)) { if (base_acct_flags == ACB_NORMAL) { /* When created, accounts are expired by default */ user_extra_flags = ACB_PW_EXPIRED; } } s.in.user_handle = handle; s.in.info = &u; s2.in.user_handle = handle; s2.in.info = &u; q.in.user_handle = handle; q.out.info = &info; q0 = q; #define TESTCALL(call, r) \ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ ##call## _r(b, tctx, &r),\ #call " failed"); \ if (!NT_STATUS_IS_OK(r.out.result)) { \ torture_result(tctx, TORTURE_FAIL, #call " level %u failed - %s (%s)\n", \ r.in.level, nt_errstr(r.out.result), __location__); \ ret = false; \ break; \ } #define STRING_EQUAL(s1, s2, field) \ torture_assert_str_equal(tctx, s1, s2, "Failed to set " #field) #define MEM_EQUAL(s1, s2, length, field) \ torture_assert_mem_equal(tctx, s1, s2, length, "Failed to set " #field) #define INT_EQUAL(i1, i2, field) \ torture_assert_int_equal(tctx, i1, i2, "Failed to set " #field) #define TEST_USERINFO_STRING(lvl1, field1, lvl2, field2, value, fpval) do { \ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ q.in.level = lvl1; \ TESTCALL(QueryUserInfo, q) \ s.in.level = lvl1; \ s2.in.level = lvl1; \ u = *info; \ if (lvl1 == 21) { \ ZERO_STRUCT(u.info21); \ u.info21.fields_present = fpval; \ } \ init_lsa_String(&u.info ## lvl1.field1, value); \ TESTCALL(SetUserInfo, s) \ TESTCALL(SetUserInfo2, s2) \ init_lsa_String(&u.info ## lvl1.field1, ""); \ TESTCALL(QueryUserInfo, q); \ u = *info; \ STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \ q.in.level = lvl2; \ TESTCALL(QueryUserInfo, q) \ u = *info; \ STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \ } while (0) #define TEST_USERINFO_BINARYSTRING(lvl1, field1, lvl2, field2, value, fpval) do { \ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ q.in.level = lvl1; \ TESTCALL(QueryUserInfo, q) \ s.in.level = lvl1; \ s2.in.level = lvl1; \ u = *info; \ if (lvl1 == 21) { \ ZERO_STRUCT(u.info21); \ u.info21.fields_present = fpval; \ } \ init_lsa_BinaryString(&u.info ## lvl1.field1, value, strlen(value)); \ TESTCALL(SetUserInfo, s) \ TESTCALL(SetUserInfo2, s2) \ init_lsa_BinaryString(&u.info ## lvl1.field1, "", 1); \ TESTCALL(QueryUserInfo, q); \ u = *info; \ MEM_EQUAL(u.info ## lvl1.field1.array, value, strlen(value), field1); \ q.in.level = lvl2; \ TESTCALL(QueryUserInfo, q) \ u = *info; \ MEM_EQUAL(u.info ## lvl2.field2.array, value, strlen(value), field2); \ } while (0) #define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ q.in.level = lvl1; \ TESTCALL(QueryUserInfo, q) \ s.in.level = lvl1; \ s2.in.level = lvl1; \ u = *info; \ if (lvl1 == 21) { \ uint8_t *bits = u.info21.logon_hours.bits; \ ZERO_STRUCT(u.info21); \ if (fpval == SAMR_FIELD_LOGON_HOURS) { \ u.info21.logon_hours.units_per_week = 168; \ u.info21.logon_hours.bits = bits; \ } \ u.info21.fields_present = fpval; \ } \ u.info ## lvl1.field1 = value; \ TESTCALL(SetUserInfo, s) \ TESTCALL(SetUserInfo2, s2) \ u.info ## lvl1.field1 = 0; \ TESTCALL(QueryUserInfo, q); \ u = *info; \ INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \ q.in.level = lvl2; \ TESTCALL(QueryUserInfo, q) \ u = *info; \ INT_EQUAL(u.info ## lvl2.field2, exp_value, field1); \ } while (0) #define TEST_USERINFO_INT(lvl1, field1, lvl2, field2, value, fpval) do { \ TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, value, fpval); \ } while (0) q0.in.level = 12; do { TESTCALL(QueryUserInfo, q0) } while (0); TEST_USERINFO_STRING(2, comment, 1, comment, "xx2-1 comment", 0); TEST_USERINFO_STRING(2, comment, 21, comment, "xx2-21 comment", 0); TEST_USERINFO_STRING(21, comment, 21, comment, "xx21-21 comment", SAMR_FIELD_COMMENT); test_account_name = talloc_asprintf(tctx, "%sxx7-1", base_account_name); TEST_USERINFO_STRING(7, account_name, 1, account_name, test_account_name, 0); test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name); TEST_USERINFO_STRING(7, account_name, 3, account_name, test_account_name, 0); test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name); TEST_USERINFO_STRING(7, account_name, 5, account_name, test_account_name, 0); test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name); TEST_USERINFO_STRING(7, account_name, 6, account_name, test_account_name, 0); test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name); TEST_USERINFO_STRING(7, account_name, 7, account_name, test_account_name, 0); test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name); TEST_USERINFO_STRING(7, account_name, 21, account_name, test_account_name, 0); test_account_name = base_account_name; TEST_USERINFO_STRING(21, account_name, 21, account_name, test_account_name, SAMR_FIELD_ACCOUNT_NAME); TEST_USERINFO_STRING(6, full_name, 1, full_name, "xx6-1 full_name", 0); TEST_USERINFO_STRING(6, full_name, 3, full_name, "xx6-3 full_name", 0); TEST_USERINFO_STRING(6, full_name, 5, full_name, "xx6-5 full_name", 0); TEST_USERINFO_STRING(6, full_name, 6, full_name, "xx6-6 full_name", 0); TEST_USERINFO_STRING(6, full_name, 8, full_name, "xx6-8 full_name", 0); TEST_USERINFO_STRING(6, full_name, 21, full_name, "xx6-21 full_name", 0); TEST_USERINFO_STRING(8, full_name, 21, full_name, "xx8-21 full_name", 0); TEST_USERINFO_STRING(21, full_name, 21, full_name, "xx21-21 full_name", SAMR_FIELD_FULL_NAME); TEST_USERINFO_STRING(6, full_name, 1, full_name, "", 0); TEST_USERINFO_STRING(6, full_name, 3, full_name, "", 0); TEST_USERINFO_STRING(6, full_name, 5, full_name, "", 0); TEST_USERINFO_STRING(6, full_name, 6, full_name, "", 0); TEST_USERINFO_STRING(6, full_name, 8, full_name, "", 0); TEST_USERINFO_STRING(6, full_name, 21, full_name, "", 0); TEST_USERINFO_STRING(8, full_name, 21, full_name, "", 0); TEST_USERINFO_STRING(21, full_name, 21, full_name, "", SAMR_FIELD_FULL_NAME); TEST_USERINFO_STRING(11, logon_script, 3, logon_script, "xx11-3 logon_script", 0); TEST_USERINFO_STRING(11, logon_script, 5, logon_script, "xx11-5 logon_script", 0); TEST_USERINFO_STRING(11, logon_script, 21, logon_script, "xx11-21 logon_script", 0); TEST_USERINFO_STRING(21, logon_script, 21, logon_script, "xx21-21 logon_script", SAMR_FIELD_LOGON_SCRIPT); TEST_USERINFO_STRING(12, profile_path, 3, profile_path, "xx12-3 profile_path", 0); TEST_USERINFO_STRING(12, profile_path, 5, profile_path, "xx12-5 profile_path", 0); TEST_USERINFO_STRING(12, profile_path, 21, profile_path, "xx12-21 profile_path", 0); TEST_USERINFO_STRING(21, profile_path, 21, profile_path, "xx21-21 profile_path", SAMR_FIELD_PROFILE_PATH); TEST_USERINFO_STRING(10, home_directory, 3, home_directory, "xx10-3 home_directory", 0); TEST_USERINFO_STRING(10, home_directory, 5, home_directory, "xx10-5 home_directory", 0); TEST_USERINFO_STRING(10, home_directory, 21, home_directory, "xx10-21 home_directory", 0); TEST_USERINFO_STRING(21, home_directory, 21, home_directory, "xx21-21 home_directory", SAMR_FIELD_HOME_DIRECTORY); TEST_USERINFO_STRING(21, home_directory, 10, home_directory, "xx21-10 home_directory", SAMR_FIELD_HOME_DIRECTORY); TEST_USERINFO_STRING(10, home_drive, 3, home_drive, "xx10-3 home_drive", 0); TEST_USERINFO_STRING(10, home_drive, 5, home_drive, "xx10-5 home_drive", 0); TEST_USERINFO_STRING(10, home_drive, 21, home_drive, "xx10-21 home_drive", 0); TEST_USERINFO_STRING(21, home_drive, 21, home_drive, "xx21-21 home_drive", SAMR_FIELD_HOME_DRIVE); TEST_USERINFO_STRING(21, home_drive, 10, home_drive, "xx21-10 home_drive", SAMR_FIELD_HOME_DRIVE); TEST_USERINFO_STRING(13, description, 1, description, "xx13-1 description", 0); TEST_USERINFO_STRING(13, description, 5, description, "xx13-5 description", 0); TEST_USERINFO_STRING(13, description, 21, description, "xx13-21 description", 0); TEST_USERINFO_STRING(21, description, 21, description, "xx21-21 description", SAMR_FIELD_DESCRIPTION); TEST_USERINFO_STRING(14, workstations, 3, workstations, "14workstation3", 0); TEST_USERINFO_STRING(14, workstations, 5, workstations, "14workstation4", 0); TEST_USERINFO_STRING(14, workstations, 21, workstations, "14workstation21", 0); TEST_USERINFO_STRING(21, workstations, 21, workstations, "21workstation21", SAMR_FIELD_WORKSTATIONS); TEST_USERINFO_STRING(21, workstations, 3, workstations, "21workstation3", SAMR_FIELD_WORKSTATIONS); TEST_USERINFO_STRING(21, workstations, 5, workstations, "21workstation5", SAMR_FIELD_WORKSTATIONS); TEST_USERINFO_STRING(21, workstations, 14, workstations, "21workstation14", SAMR_FIELD_WORKSTATIONS); TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "xx20-21 parameters", 0); TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "xx21-21 parameters", SAMR_FIELD_PARAMETERS); TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "xx21-20 parameters", SAMR_FIELD_PARAMETERS); /* also empty user parameters are allowed */ TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "", 0); TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "", SAMR_FIELD_PARAMETERS); TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "", SAMR_FIELD_PARAMETERS); /* Samba 3 cannot store country_code and code_page atm. - gd */ if (!torture_setting_bool(tctx, "samba3", false)) { TEST_USERINFO_INT(2, country_code, 2, country_code, __LINE__, 0); TEST_USERINFO_INT(2, country_code, 21, country_code, __LINE__, 0); TEST_USERINFO_INT(21, country_code, 21, country_code, __LINE__, SAMR_FIELD_COUNTRY_CODE); TEST_USERINFO_INT(21, country_code, 2, country_code, __LINE__, SAMR_FIELD_COUNTRY_CODE); TEST_USERINFO_INT(2, code_page, 21, code_page, __LINE__, 0); TEST_USERINFO_INT(21, code_page, 21, code_page, __LINE__, SAMR_FIELD_CODE_PAGE); TEST_USERINFO_INT(21, code_page, 2, code_page, __LINE__, SAMR_FIELD_CODE_PAGE); } if (!torture_setting_bool(tctx, "samba3", false)) { TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, __LINE__, 0); TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, __LINE__, 0); TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, __LINE__, SAMR_FIELD_ACCT_EXPIRY); TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, __LINE__, SAMR_FIELD_ACCT_EXPIRY); TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, __LINE__, SAMR_FIELD_ACCT_EXPIRY); } else { /* Samba 3 can only store seconds / time_t in passdb - gd */ NTTIME nt; unix_to_nt_time(&nt, time(NULL) + __LINE__); TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, nt, 0); unix_to_nt_time(&nt, time(NULL) + __LINE__); TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, nt, 0); unix_to_nt_time(&nt, time(NULL) + __LINE__); TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); unix_to_nt_time(&nt, time(NULL) + __LINE__); TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); unix_to_nt_time(&nt, time(NULL) + __LINE__); TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); } TEST_USERINFO_INT(4, logon_hours.bits[3], 3, logon_hours.bits[3], 1, 0); TEST_USERINFO_INT(4, logon_hours.bits[3], 5, logon_hours.bits[3], 2, 0); TEST_USERINFO_INT(4, logon_hours.bits[3], 21, logon_hours.bits[3], 3, 0); TEST_USERINFO_INT(21, logon_hours.bits[3], 21, logon_hours.bits[3], 4, SAMR_FIELD_LOGON_HOURS); TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), 0); TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, (base_acct_flags | ACB_DISABLED), (base_acct_flags | ACB_DISABLED | user_extra_flags), 0); /* Setting PWNOEXP clears the magic ACB_PW_EXPIRED flag */ TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), 0); TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), 0); /* The 'autolock' flag doesn't stick - check this */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_AUTOLOCK), (base_acct_flags | ACB_DISABLED | user_extra_flags), 0); #if 0 /* Removing the 'disabled' flag doesn't stick - check this */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags), (base_acct_flags | ACB_DISABLED | user_extra_flags), 0); #endif /* Samba3 cannot store these atm */ if (!torture_setting_bool(tctx, "samba3", false)) { /* The 'store plaintext' flag does stick */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED), (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED | user_extra_flags), 0); /* The 'use DES' flag does stick */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY), (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY | user_extra_flags), 0); /* The 'don't require kerberos pre-authentication flag does stick */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH), (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH | user_extra_flags), 0); /* The 'no kerberos PAC required' flag sticks */ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD), (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD | user_extra_flags), 0); } TEST_USERINFO_INT_EXP(21, acct_flags, 21, acct_flags, (base_acct_flags | ACB_DISABLED), (base_acct_flags | ACB_DISABLED | user_extra_flags), SAMR_FIELD_ACCT_FLAGS); #if 0 /* these fail with win2003 - it appears you can't set the primary gid? the set succeeds, but the gid isn't changed. Very weird! */ TEST_USERINFO_INT(9, primary_gid, 1, primary_gid, 513); TEST_USERINFO_INT(9, primary_gid, 3, primary_gid, 513); TEST_USERINFO_INT(9, primary_gid, 5, primary_gid, 513); TEST_USERINFO_INT(9, primary_gid, 21, primary_gid, 513); #endif return ret; } /* generate a random password for password change tests */ static char *samr_rand_pass_silent(TALLOC_CTX *mem_ctx, int min_len) { size_t len = MAX(8, min_len); char *s = generate_random_password(mem_ctx, len, len+6); return s; } static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) { char *s = samr_rand_pass_silent(mem_ctx, min_len); printf("Generated password '%s'\n", s); return s; } /* generate a random password for password change tests */ static DATA_BLOB samr_very_rand_pass(TALLOC_CTX *mem_ctx, int len) { int i; DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */); generate_random_buffer(password.data, password.length); for (i=0; i < len; i++) { if (((uint16_t *)password.data)[i] == 0) { ((uint16_t *)password.data)[i] = 1; } } return password; } /* generate a random password for password change tests (fixed length) */ static char *samr_rand_pass_fixed_len(TALLOC_CTX *mem_ctx, int len) { char *s = generate_random_password(mem_ctx, len, len); printf("Generated password '%s'\n", s); return s; } static bool test_SetUserPass(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 24; u.info24.password_expired = 0; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } status = init_samr_CryptPassword(newpass, &session_key, &u.info24.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); torture_comment(tctx, "Testing SetUserInfo level 24 (set password)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_SetUserPass_23(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, uint32_t fields_present, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; struct dcerpc_binding_handle *b = p->binding_handle; char *newpass; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 23; ZERO_STRUCT(u); u.info23.info.fields_present = fields_present; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } status = init_samr_CryptPassword(newpass, &session_key, &u.info23.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); torture_comment(tctx, "Testing SetUserInfo level 23 (set password)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } /* This should break the key nicely */ session_key.data[0]++; status = init_samr_CryptPassword(newpass, &session_key, &u.info23.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); /* Reset the session key */ session_key.data[0]--; torture_comment(tctx, "Testing SetUserInfo level 23 (set password) with wrong password\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } return ret; } static bool test_SetUserPass_32(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, uint32_t fields_present, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; DATA_BLOB session_key; uint8_t salt_data[16]; DATA_BLOB salt = { .data = salt_data, .length = sizeof(salt_data), }; char *newpass = NULL; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; bool ret = true; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 32; ZERO_STRUCT(u); u.info32.info.fields_present = fields_present; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } generate_nonce_buffer(salt.data, salt.length); status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info32.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordAES failed"); torture_comment(tctx, "Testing SetUserInfo level 32 (set password aes)\n"); status = dcerpc_samr_SetUserInfo_r(b, tctx, &s); torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } /* This should break the key nicely */ session_key.data[0]++; status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info32.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); /* Reset the key */ session_key.data[0]--; torture_comment(tctx, "Testing SetUserInfo level 32 (set password aes) with " "wrong session key\n"); status = dcerpc_samr_SetUserInfo_r(b, tctx, &s); torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with " "WRONG_PASSWORD- %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } return ret; } static bool test_SetUserPass_31(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, bool makeshort, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; uint8_t salt_data[16]; DATA_BLOB salt = { .data = salt_data, .length = sizeof(salt_data), }; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } if (makeshort && policy_min_pw_len) { newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1); } else { newpass = samr_rand_pass(tctx, policy_min_pw_len); } s.in.user_handle = handle; s.in.info = &u; s.in.level = 31; ZERO_STRUCT(u); u.info31.password_expired = 0; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } generate_nonce_buffer(salt.data, salt.length); status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info31.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } /* This should break the key nicely */ session_key.data[0]++; status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info31.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); /* Reset the key */ session_key.data[0]--; torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes) with wrong session key\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_SetUserPassEx(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, bool makeshort, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } if (makeshort && policy_min_pw_len) { newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1); } else { newpass = samr_rand_pass(tctx, policy_min_pw_len); } s.in.user_handle = handle; s.in.info = &u; s.in.level = 26; u.info26.password_expired = 0; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info26.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } /* This should break the key nicely */ session_key.data[0]++; status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info26.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); /* Reset the key */ session_key.data[0]--; torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex) with wrong session key\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_SetUserPass_25(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, uint32_t fields_present, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 25; ZERO_STRUCT(u); u.info25.info.fields_present = fields_present; status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info25.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } /* This should break the key nicely */ session_key.data[0]++; status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info25.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); /* Reset the key */ session_key.data[0]--; torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with wrong session key\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } return ret; } static bool test_SetUserPass_18(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; uint8_t lm_hash[16], nt_hash[16]; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 18; ZERO_STRUCT(u); u.info18.nt_pwd_active = true; u.info18.lm_pwd_active = true; E_md4hash(newpass, nt_hash); E_deshash(newpass, lm_hash); status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } { DATA_BLOB in,out; in = data_blob_const(nt_hash, 16); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); memcpy(u.info18.nt_pwd.hash, out.data, out.length); } { DATA_BLOB in,out; in = data_blob_const(lm_hash, 16); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); memcpy(u.info18.lm_pwd.hash, out.data, out.length); } torture_comment(tctx, "Testing SetUserInfo level 18 (set password hash)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_SetUserPass_21(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, uint32_t fields_present, char **password) { NTSTATUS status; struct samr_SetUserInfo s; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; uint8_t lm_hash[16], nt_hash[16]; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); s.in.user_handle = handle; s.in.info = &u; s.in.level = 21; E_md4hash(newpass, nt_hash); E_deshash(newpass, lm_hash); ZERO_STRUCT(u); u.info21.fields_present = fields_present; if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { u.info21.lm_owf_password.length = 16; u.info21.lm_owf_password.size = 16; u.info21.lm_owf_password.array = (uint16_t *)lm_hash; u.info21.lm_password_set = true; } if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { u.info21.nt_owf_password.length = 16; u.info21.nt_owf_password.size = 16; u.info21.nt_owf_password.array = (uint16_t *)nt_hash; u.info21.nt_password_set = true; } status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { DATA_BLOB in,out; in = data_blob_const(u.info21.lm_owf_password.array, u.info21.lm_owf_password.length); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); u.info21.lm_owf_password.array = (uint16_t *)out.data; } if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { DATA_BLOB in,out; in = data_blob_const(u.info21.nt_owf_password.array, u.info21.nt_owf_password.length); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); u.info21.nt_owf_password.array = (uint16_t *)out.data; } torture_comment(tctx, "Testing SetUserInfo level 21 (set password hash)\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } else { *password = newpass; } /* try invalid length */ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { u.info21.nt_owf_password.length++; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } } if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { u.info21.lm_owf_password.length++; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } } return ret; } static bool test_SetUserPass_level_ex(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle, uint16_t level, uint32_t fields_present, char **password, uint8_t password_expired, bool use_setinfo2, bool *matched_expected_error) { NTSTATUS status; NTSTATUS expected_error = NT_STATUS_OK; struct samr_SetUserInfo s; struct samr_SetUserInfo2 s2; union samr_UserInfo u; bool ret = true; DATA_BLOB session_key; uint8_t salt_data[16]; DATA_BLOB salt = { .data = salt_data, .length = sizeof(salt_data), }; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; const char *comment = NULL; uint8_t lm_hash[16], nt_hash[16]; pwp.in.user_handle = handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass_silent(tctx, policy_min_pw_len); if (use_setinfo2) { s2.in.user_handle = handle; s2.in.info = &u; s2.in.level = level; } else { s.in.user_handle = handle; s.in.info = &u; s.in.level = level; } if (fields_present & SAMR_FIELD_COMMENT) { comment = talloc_asprintf(tctx, "comment: %ld\n", (long int) time(NULL)); } ZERO_STRUCT(u); switch (level) { case 18: E_md4hash(newpass, nt_hash); E_deshash(newpass, lm_hash); u.info18.nt_pwd_active = true; u.info18.lm_pwd_active = true; u.info18.password_expired = password_expired; memcpy(u.info18.lm_pwd.hash, lm_hash, 16); memcpy(u.info18.nt_pwd.hash, nt_hash, 16); break; case 21: E_md4hash(newpass, nt_hash); E_deshash(newpass, lm_hash); u.info21.fields_present = fields_present; u.info21.password_expired = password_expired; u.info21.comment.string = comment; if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { u.info21.lm_owf_password.length = 16; u.info21.lm_owf_password.size = 16; u.info21.lm_owf_password.array = (uint16_t *)lm_hash; u.info21.lm_password_set = true; } if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { u.info21.nt_owf_password.length = 16; u.info21.nt_owf_password.size = 16; u.info21.nt_owf_password.array = (uint16_t *)nt_hash; u.info21.nt_password_set = true; } break; case 23: u.info23.info.fields_present = fields_present; u.info23.info.password_expired = password_expired; u.info23.info.comment.string = comment; break; case 24: u.info24.password_expired = password_expired; break; case 25: u.info25.info.fields_present = fields_present; u.info25.info.password_expired = password_expired; u.info25.info.comment.string = comment; break; case 26: u.info26.password_expired = password_expired; break; case 31: u.info31.password_expired = password_expired; break; case 28: u.info25.info.fields_present = fields_present; u.info25.info.password_expired = password_expired; u.info25.info.comment.string = comment; break; } status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } generate_nonce_buffer(salt.data, salt.length); switch (level) { case 18: { DATA_BLOB in,out; in = data_blob_const(u.info18.nt_pwd.hash, 16); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); memcpy(u.info18.nt_pwd.hash, out.data, out.length); } { DATA_BLOB in,out; in = data_blob_const(u.info18.lm_pwd.hash, 16); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); memcpy(u.info18.lm_pwd.hash, out.data, out.length); } break; case 21: if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { DATA_BLOB in,out; in = data_blob_const(u.info21.lm_owf_password.array, u.info21.lm_owf_password.length); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); u.info21.lm_owf_password.array = (uint16_t *)out.data; } if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { DATA_BLOB in,out; in = data_blob_const(u.info21.nt_owf_password.array, u.info21.nt_owf_password.length); out = data_blob_talloc_zero(tctx, 16); sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); u.info21.nt_owf_password.array = (uint16_t *)out.data; } break; case 23: status = init_samr_CryptPassword(newpass, &session_key, &u.info23.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); break; case 24: status = init_samr_CryptPassword(newpass, &session_key, &u.info24.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); break; case 25: status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info25.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); break; case 26: status = init_samr_CryptPasswordEx(newpass, &session_key, &u.info26.password); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPasswordEx failed"); break; case 31: status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info31.password); break; case 32: status = init_samr_CryptPasswordAES(tctx, newpass, &salt, &session_key, &u.info32.password); break; } if (use_setinfo2) { torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo2_r(b, tctx, &s2), "SetUserInfo2 failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s2.out.result)); status = s2.out.result; } else { torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", __location__, __FUNCTION__, newpass, nt_errstr(s.out.result)); status = s.out.result; } if (!NT_STATUS_IS_OK(status)) { if (fields_present == 0) { expected_error = NT_STATUS_INVALID_PARAMETER; } if (fields_present & SAMR_FIELD_LAST_PWD_CHANGE) { expected_error = NT_STATUS_ACCESS_DENIED; } } if (!NT_STATUS_IS_OK(expected_error)) { if (use_setinfo2) { torture_assert_ntstatus_equal(tctx, s2.out.result, expected_error, "SetUserInfo2 failed"); } else { torture_assert_ntstatus_equal(tctx, s.out.result, expected_error, "SetUserInfo failed"); } *matched_expected_error = true; return true; } if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo%s level %u failed - %s\n", use_setinfo2 ? "2":"", level, nt_errstr(status)); ret = false; } else { *password = newpass; } return ret; } static bool test_SetAliasInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_SetAliasInfo r; struct samr_QueryAliasInfo q; union samr_AliasInfo *info; uint16_t levels[] = {2, 3}; int i; bool ret = true; /* Ignoring switch level 1, as that includes the number of members for the alias * and setting this to a wrong value might have negative consequences */ for (i=0;iname,TEST_ALIASNAME); break; case ALIASINFODESCRIPTION: init_lsa_String(&r.in.info->description, "Test Description, should test I18N as well"); break; case ALIASINFOALL: torture_comment(tctx, "ALIASINFOALL ignored\n"); break; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetAliasInfo_r(b, tctx, &r), "SetAliasInfo failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetAliasInfo level %u failed - %s\n", levels[i], nt_errstr(r.out.result)); ret = false; } q.in.alias_handle = handle; q.in.level = levels[i]; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryAliasInfo_r(b, tctx, &q), "QueryAliasInfo failed"); if (!NT_STATUS_IS_OK(q.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryAliasInfo level %u failed - %s\n", levels[i], nt_errstr(q.out.result)); ret = false; } } return ret; } static bool test_GetGroupsForUser(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *user_handle) { struct samr_GetGroupsForUser r; struct samr_RidWithAttributeArray *rids = NULL; torture_comment(tctx, "Testing GetGroupsForUser\n"); r.in.user_handle = user_handle; r.out.rids = &rids; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r), "GetGroupsForUser failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetGroupsForUser failed"); return true; } static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx, struct lsa_String *domain_name) { struct samr_GetDomPwInfo r; struct samr_PwInfo info; struct dcerpc_binding_handle *b = p->binding_handle; r.in.domain_name = domain_name; r.out.info = &info; torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), "GetDomPwInfo failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); r.in.domain_name->string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), "GetDomPwInfo failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); r.in.domain_name->string = "\\\\__NONAME__"; torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), "GetDomPwInfo failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); r.in.domain_name->string = "\\\\Builtin"; torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), "GetDomPwInfo failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); return true; } static bool test_GetUserPwInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_GetUserPwInfo r; struct samr_PwInfo info; torture_comment(tctx, "Testing GetUserPwInfo\n"); r.in.user_handle = handle; r.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &r), "GetUserPwInfo failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetUserPwInfo"); return true; } static NTSTATUS test_LookupName(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *name, uint32_t *rid) { NTSTATUS status; struct samr_LookupNames n; struct lsa_String sname[2]; struct samr_Ids rids, types; init_lsa_String(&sname[0], name); n.in.domain_handle = domain_handle; n.in.num_names = 1; n.in.names = sname; n.out.rids = &rids; n.out.types = &types; status = dcerpc_samr_LookupNames_r(b, tctx, &n); if (!NT_STATUS_IS_OK(status)) { return status; } if (NT_STATUS_IS_OK(n.out.result)) { *rid = n.out.rids->ids[0]; } else { return n.out.result; } init_lsa_String(&sname[1], "xxNONAMExx"); n.in.num_names = 2; status = dcerpc_samr_LookupNames_r(b, tctx, &n); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_EQUAL(n.out.result, STATUS_SOME_UNMAPPED)) { torture_result(tctx, TORTURE_FAIL, "LookupNames[2] failed - %s\n", nt_errstr(n.out.result)); if (NT_STATUS_IS_OK(n.out.result)) { return NT_STATUS_UNSUCCESSFUL; } return n.out.result; } n.in.num_names = 0; status = dcerpc_samr_LookupNames_r(b, tctx, &n); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(n.out.result)) { torture_result(tctx, TORTURE_FAIL, "LookupNames[0] failed - %s\n", nt_errstr(status)); return n.out.result; } init_lsa_String(&sname[0], "xxNONAMExx"); n.in.num_names = 1; status = dcerpc_samr_LookupNames_r(b, tctx, &n); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) { torture_result(tctx, TORTURE_FAIL, "LookupNames[1 bad name] failed - %s\n", nt_errstr(n.out.result)); if (NT_STATUS_IS_OK(n.out.result)) { return NT_STATUS_UNSUCCESSFUL; } return n.out.result; } init_lsa_String(&sname[0], "xxNONAMExx"); init_lsa_String(&sname[1], "xxNONAME2xx"); n.in.num_names = 2; status = dcerpc_samr_LookupNames_r(b, tctx, &n); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) { torture_result(tctx, TORTURE_FAIL, "LookupNames[2 bad names] failed - %s\n", nt_errstr(n.out.result)); if (NT_STATUS_IS_OK(n.out.result)) { return NT_STATUS_UNSUCCESSFUL; } return n.out.result; } return NT_STATUS_OK; } static NTSTATUS test_OpenUser_byname(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *name, struct policy_handle *user_handle) { NTSTATUS status; struct samr_OpenUser r; uint32_t rid; status = test_LookupName(b, tctx, domain_handle, name, &rid); if (!NT_STATUS_IS_OK(status)) { return status; } r.in.domain_handle = domain_handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.in.rid = rid; r.out.user_handle = user_handle; status = dcerpc_samr_OpenUser_r(b, tctx, &r); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(r.out.result)); } return r.out.result; } #if 0 static bool test_ChangePasswordNT3(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle) { NTSTATUS status; struct samr_ChangePasswordUser r; bool ret = true; struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; struct policy_handle user_handle; char *oldpass = "test"; char *newpass = "test2"; uint8_t old_nt_hash[16], new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; status = test_OpenUser_byname(p, tctx, handle, "testuser", &user_handle); if (!NT_STATUS_IS_OK(status)) { return false; } torture_comment(tctx, "Testing ChangePasswordUser for user 'testuser'\n"); torture_comment(tctx, "old password: %s\n", oldpass); torture_comment(tctx, "new password: %s\n", newpass); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); r.in.handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result)); ret = false; } if (!test_samr_handle_Close(p, tctx, &user_handle)) { ret = false; } return ret; } #endif static bool test_ChangePasswordUser(struct dcerpc_binding_handle *b, struct torture_context *tctx, const char *acct_name, struct policy_handle *handle, char **password) { NTSTATUS status; struct samr_ChangePasswordUser r; bool ret = true; struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; struct policy_handle user_handle; char *oldpass; uint8_t old_nt_hash[16], new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; bool changed = true; char *newpass; struct samr_GetUserPwInfo pwp; struct samr_PwInfo info; int policy_min_pw_len = 0; status = test_OpenUser_byname(b, tctx, handle, acct_name, &user_handle); if (!NT_STATUS_IS_OK(status)) { return false; } pwp.in.user_handle = &user_handle; pwp.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), "GetUserPwInfo failed"); if (NT_STATUS_IS_OK(pwp.out.result)) { policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); torture_comment(tctx, "Testing ChangePasswordUser\n"); torture_assert(tctx, *password != NULL, "Failing ChangePasswordUser as old password was NULL. Previous test failed?"); oldpass = *password; E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); r.in.user_handle = &user_handle; r.in.lm_present = 1; /* Break the NT hash */ hash3.hash[0]++; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); /* Do not proceed if this call has been removed */ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) { torture_skip(tctx, "ValidatePassword not supported by server\n"); } if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash"); } /* Unbreak the NT hash */ hash3.hash[0]--; r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; /* Break the LM hash */ hash1.hash[0]--; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash"); } /* Unbreak the NT hash */ hash3.hash[0]--; r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; /* Break the LM cross */ hash6.hash[0]++; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the LM cross-hash, got %s\n", nt_errstr(r.out.result)); ret = false; } /* Unbreak the LM cross */ hash6.hash[0]--; r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; /* Break the NT cross */ hash5.hash[0]++; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the NT cross-hash, got %s\n", nt_errstr(r.out.result)); ret = false; } /* Unbreak the NT cross */ hash5.hash[0]--; /* Reset the hashes to not broken values */ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 0; r.in.lm_cross = NULL; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_IS_OK(r.out.result)) { changed = true; *password = newpass; } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result)); ret = false; } oldpass = newpass; newpass = samr_rand_pass(tctx, policy_min_pw_len); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); /* Reset the hashes to not broken values */ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 0; r.in.nt_cross = NULL; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_IS_OK(r.out.result)) { changed = true; *password = newpass; } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result)); ret = false; } oldpass = newpass; newpass = samr_rand_pass(tctx, policy_min_pw_len); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); /* Reset the hashes to not broken values */ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); } else if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result)); ret = false; } else { changed = true; *password = newpass; } r.in.user_handle = &user_handle; r.in.lm_present = 1; r.in.old_lm_crypted = &hash1; r.in.new_lm_crypted = &hash2; r.in.nt_present = 1; r.in.old_nt_crypted = &hash3; r.in.new_nt_crypted = &hash4; r.in.cross1_present = 1; r.in.nt_cross = &hash5; r.in.cross2_present = 1; r.in.lm_cross = &hash6; if (changed) { torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), "ChangePasswordUser failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); } else if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(r.out.result)); ret = false; } } if (!test_samr_handle_Close(b, tctx, &user_handle)) { ret = false; } return ret; } static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, struct torture_context *tctx, const char *acct_name, struct policy_handle *handle, char **password) { struct samr_OemChangePasswordUser2 r; bool ret = true; struct samr_Password lm_verifier; struct samr_CryptPassword lm_pass; struct lsa_AsciiString server, account, account_bad; char *oldpass; char *newpass; struct dcerpc_binding_handle *b = p->binding_handle; uint8_t old_lm_hash[16], new_lm_hash[16]; gnutls_cipher_hd_t cipher_hnd = NULL; gnutls_datum_t session_key = { .data = old_lm_hash, .size = 16 }; struct samr_GetDomPwInfo dom_pw_info; struct samr_PwInfo info; int policy_min_pw_len = 0; struct lsa_String domain_name; domain_name.string = ""; dom_pw_info.in.domain_name = &domain_name; dom_pw_info.out.info = &info; torture_comment(tctx, "Testing OemChangePasswordUser2\n"); torture_assert(tctx, *password != NULL, "Failing OemChangePasswordUser2 as old password was NULL. Previous test failed?"); oldpass = *password; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), "GetDomPwInfo failed"); if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { policy_min_pw_len = dom_pw_info.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); account.string = acct_name; E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &session_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.password = &lm_pass; r.in.hash = &lm_verifier; /* Break the verification */ lm_verifier.hash[0]++; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (torture_setting_bool(tctx, "samba4", false)) { torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "Samba4 should refuse LM password change"); /* * No point continuing, once we have checked this is not * implemented */ return true; } if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", nt_errstr(r.out.result)); ret = false; } encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); /* Break the old password */ old_lm_hash[0]++; gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &session_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); /* unbreak it for the next operation */ old_lm_hash[0]--; E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.password = &lm_pass; r.in.hash = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n", nt_errstr(r.out.result)); ret = false; } encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &session_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); r.in.server = &server; r.in.account = &account; r.in.password = &lm_pass; r.in.hash = NULL; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n", nt_errstr(r.out.result)); ret = false; } /* This shouldn't be a valid name */ account_bad.string = TEST_ACCOUNT_NAME "XX"; r.in.account = &account_bad; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n", nt_errstr(r.out.result)); ret = false; } /* This shouldn't be a valid name */ account_bad.string = TEST_ACCOUNT_NAME "XX"; r.in.account = &account_bad; r.in.password = &lm_pass; r.in.hash = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD for invalid user - %s\n", nt_errstr(r.out.result)); ret = false; } /* This shouldn't be a valid name */ account_bad.string = TEST_ACCOUNT_NAME "XX"; r.in.account = &account_bad; r.in.password = NULL; r.in.hash = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n", nt_errstr(r.out.result)); ret = false; } E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &session_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.password = &lm_pass; r.in.hash = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), "OemChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_comment(tctx, "OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); } else if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, struct torture_context *tctx, const char *acct_name, char **password, char *newpass, bool allow_password_restriction) { struct samr_ChangePasswordUser2 r; bool ret = true; struct lsa_String server, account; struct samr_CryptPassword nt_pass, lm_pass; struct samr_Password nt_verifier, lm_verifier; char *oldpass; struct dcerpc_binding_handle *b = p->binding_handle; uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, sizeof(old_nt_hash)); struct samr_GetDomPwInfo dom_pw_info; struct samr_PwInfo info; struct lsa_String domain_name; NTSTATUS status; gnutls_cipher_hd_t cipher_hnd = NULL; gnutls_datum_t old_lm_key = { .data = old_lm_hash, .size = sizeof(old_lm_hash), }; domain_name.string = ""; dom_pw_info.in.domain_name = &domain_name; dom_pw_info.out.info = &info; torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name); torture_assert(tctx, *password != NULL, "Failing ChangePasswordUser2 as old password was NULL. Previous test failed?"); oldpass = *password; if (!newpass) { int policy_min_pw_len = 0; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), "GetDomPwInfo failed"); if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { policy_min_pw_len = dom_pw_info.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); } server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); init_lsa_String(&account, acct_name); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &old_lm_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 1; r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r), "ChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (allow_password_restriction && NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); } else if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result)); ret = false; } else { *password = newpass; } return ret; } static bool test_ChangePasswordUser2_ntstatus(struct dcerpc_pipe *p, struct torture_context *tctx, const char *acct_name, const char *password, NTSTATUS status) { struct samr_ChangePasswordUser2 r; struct lsa_String server, account; struct samr_CryptPassword nt_pass, lm_pass; struct samr_Password nt_verifier, lm_verifier; const char *oldpass; struct dcerpc_binding_handle *b = p->binding_handle; uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, sizeof(old_nt_hash)); gnutls_cipher_hd_t cipher_hnd = NULL; gnutls_datum_t old_lm_key = { .data = old_lm_hash, .size = sizeof(old_lm_hash), }; struct samr_GetDomPwInfo dom_pw_info; struct samr_PwInfo info; struct lsa_String domain_name; NTSTATUS crypt_status; char *newpass; int policy_min_pw_len = 0; domain_name.string = ""; dom_pw_info.in.domain_name = &domain_name; dom_pw_info.out.info = &info; torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name); oldpass = password; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), "GetDomPwInfo failed"); if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { policy_min_pw_len = dom_pw_info.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); init_lsa_String(&account, acct_name); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &old_lm_key, NULL); gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); crypt_status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, crypt_status, "init_samr_CryptPassword failed"); E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 1; r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r), "ChangePasswordUser2 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); } else { torture_assert_ntstatus_equal(tctx, r.out.result, status, "ChangePasswordUser2 returned unexpected value"); } return true; } bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tctx, const char *account_string, int policy_min_pw_len, char **password, const char *newpass, NTTIME last_password_change, bool handle_reject_reason) { struct samr_ChangePasswordUser3 r; bool ret = true; struct lsa_String server, account, account_bad; struct samr_CryptPassword nt_pass, lm_pass; struct samr_Password nt_verifier, lm_verifier; char *oldpass; struct dcerpc_binding_handle *b = p->binding_handle; uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; NTTIME t; struct samr_DomInfo1 *dominfo = NULL; struct userPwdChangeFailureInformation *reject = NULL; DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, 16); NTSTATUS status; torture_comment(tctx, "Testing ChangePasswordUser3\n"); if (newpass == NULL) { do { if (policy_min_pw_len == 0) { newpass = samr_rand_pass(tctx, policy_min_pw_len); } else { newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len); } } while (check_password_quality(newpass) == false); } else { torture_comment(tctx, "Using password '%s'\n", newpass); } torture_assert(tctx, *password != NULL, "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?"); oldpass = *password; server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); init_lsa_String(&account, account_string); E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); /* * The new plaintext password is encrypted using RC4 with the * old NT password hash (directly, with no confounder). The * password is at the end of the random padded buffer, * offering a little protection. * * This is almost certainly wrong, it should be the old LM * hash, it was switched in an unrelated commit * 579c13da43d5b40ac6d6c1436399fbc1d8dfd054 in 2004. */ status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &lm_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); /* * Now we prepare a DES cross-hash of the old LM and new NT * passwords to link the two buffers */ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); /* * The new plaintext password is also encrypted using RC4 with * the old NT password hash (directly, with no confounder). * The password is at the end of the random padded buffer, * offering a little protection. */ status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); /* * Another DES based cross-hash */ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); /* Break the verification */ nt_verifier.hash[0]++; r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 1; r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; r.out.dominfo = &dominfo; r.out.reject = &reject; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", nt_errstr(r.out.result)); ret = false; } status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &lm_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); /* Break the NT Hash */ old_nt_hash[0]++; status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); /* Unbreak it again */ old_nt_hash[0]--; E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 1; r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; r.out.dominfo = &dominfo; r.out.reject = &reject; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n", nt_errstr(r.out.result)); ret = false; } /* This shouldn't be a valid name */ init_lsa_String(&account_bad, talloc_asprintf(tctx, "%sXX", account_string)); r.in.account = &account_bad; torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n", nt_errstr(r.out.result)); ret = false; } E_md4hash(oldpass, old_nt_hash); E_md4hash(newpass, new_nt_hash); E_deshash(oldpass, old_lm_hash); E_deshash(newpass, new_lm_hash); status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &lm_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword"); E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 1; r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; r.out.dominfo = &dominfo; r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); torture_comment(tctx, "(%s): dominfo[%s], reject[%s], handle_reject_reason[%s], " "last_password_change[%s], dominfo->min_password_age[%lld]\n", __location__, (dominfo == NULL)? "NULL" : "present", reject ? "true" : "false", handle_reject_reason ? "true" : "false", null_nttime(last_password_change) ? "null" : "not null", dominfo ? (long long)dominfo->min_password_age : (long long)0); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && dominfo && reject && handle_reject_reason && (!null_nttime(last_password_change) || !dominfo->min_password_age)) { if (dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) { if (reject && (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR)) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); return false; } } /* We tested the order of precedence which is as follows: * pwd min_age * pwd length * pwd complexity * pwd history Guenther */ if ((dominfo->min_password_age < 0) && !null_nttime(last_password_change) && (last_password_change - dominfo->min_password_age > t)) { if (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); return false; } } else if ((dominfo->min_password_length > 0) && (strlen(newpass) < dominfo->min_password_length)) { if (reject->extendedFailureReason != SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PASSWORD_TOO_SHORT (%d), got %d\n", SAM_PWD_CHANGE_PASSWORD_TOO_SHORT, reject->extendedFailureReason); return false; } } else if ((dominfo->password_history_length > 0) && strequal(oldpass, newpass)) { if (reject->extendedFailureReason != SAM_PWD_CHANGE_PWD_IN_HISTORY) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PWD_IN_HISTORY (%d), got %d\n", SAM_PWD_CHANGE_PWD_IN_HISTORY, reject->extendedFailureReason); return false; } } else if (dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) { if (reject->extendedFailureReason != SAM_PWD_CHANGE_NOT_COMPLEX) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NOT_COMPLEX (%d), got %d\n", SAM_PWD_CHANGE_NOT_COMPLEX, reject->extendedFailureReason); return false; } } if (reject->extendedFailureReason == SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) { /* retry with adjusted size */ return test_ChangePasswordUser3(p, tctx, account_string, dominfo->min_password_length, password, NULL, 0, false); } } else if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); return false; } /* Perhaps the server has a 'min password age' set? */ } else { torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3"); *password = talloc_strdup(tctx, newpass); } return ret; } bool test_ChangePasswordUser4(struct dcerpc_pipe *p, struct torture_context *tctx, const char *account_string, int policy_min_pw_len, char **password, const char *newpassword) { struct dcerpc_binding_handle *b = p->binding_handle; struct samr_ChangePasswordUser4 r; const char *oldpassword = *password; char *srv_str = NULL; struct lsa_String server; struct lsa_String account; uint8_t old_nt_key_data[16] = {0}; gnutls_datum_t old_nt_key = { .data = old_nt_key_data, .size = sizeof(old_nt_key), }; uint8_t cek_data[16] = {0}; DATA_BLOB cek = { .data = cek_data, .length = sizeof(cek_data), }; uint8_t pw_data[514] = {0}; DATA_BLOB plaintext = { .data = pw_data, .length = sizeof(pw_data), }; DATA_BLOB ciphertext = data_blob_null; struct samr_EncryptedPasswordAES pwd_buf = {.cipher_len = 0}; DATA_BLOB iv = { .data = pwd_buf.salt, .length = sizeof(pwd_buf.salt), }; gnutls_datum_t iv_datum = { .data = iv.data, .size = iv.length, }; uint64_t pbkdf2_iterations = generate_random_u64_range(5000, 1000000); NTSTATUS status; bool ok; int rc; torture_comment(tctx, "Testing ChangePasswordUser4\n"); if (newpassword == NULL) { do { if (policy_min_pw_len == 0) { newpassword = samr_rand_pass(tctx, policy_min_pw_len); } else { newpassword = samr_rand_pass_fixed_len( tctx, policy_min_pw_len); } } while (check_password_quality(newpassword) == false); } else { torture_comment(tctx, "Using password '%s'\n", newpassword); } torture_assert_not_null(tctx, *password, "Failing ChangePasswordUser4 as old password " "was NULL. Previous test failed?"); srv_str = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); torture_assert_not_null(tctx, srv_str, "srvstr is NULL"); init_lsa_String(&server, srv_str); init_lsa_String(&account, account_string); E_md4hash(oldpassword, old_nt_key_data); generate_nonce_buffer(iv.data, iv.length); rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512, &old_nt_key, &iv_datum, pbkdf2_iterations, cek.data, cek.length); torture_assert_int_equal(tctx, rc, 0, "gnutls_pbkdf2 failed"); ok = encode_pwd_buffer514_from_str(pw_data, newpassword, STR_UNICODE); torture_assert(tctx, ok, "encode_aes_pw_buffer failed"); status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt( tctx, &plaintext, &cek, &samr_aes256_enc_key_salt, &samr_aes256_mac_key_salt, &iv, &ciphertext, pwd_buf.auth_data); torture_assert_ntstatus_ok( tctx, status, "samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt failed"); pwd_buf.cipher_len = ciphertext.length; pwd_buf.cipher = ciphertext.data; pwd_buf.PBKDF2Iterations = pbkdf2_iterations; r.in.server = &server; r.in.account = &account; r.in.password = &pwd_buf; status = dcerpc_samr_ChangePasswordUser4_r(b, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "ChangePasswordUser4 failed"); *password = talloc_strdup(tctx, newpassword); return true; } bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_context *tctx, const char *account_string, struct policy_handle *handle, char **password) { NTSTATUS status; struct samr_ChangePasswordUser3 r; struct samr_SetUserInfo s; union samr_UserInfo u; DATA_BLOB session_key; bool ret = true; struct lsa_String server, account; struct samr_CryptPassword nt_pass; struct samr_Password nt_verifier; DATA_BLOB new_random_pass; char *newpass; char *oldpass; struct dcerpc_binding_handle *b = p->binding_handle; uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, sizeof(old_nt_hash)); NTTIME t; struct samr_DomInfo1 *dominfo = NULL; struct userPwdChangeFailureInformation *reject = NULL; gnutls_cipher_hd_t cipher_hnd = NULL; uint8_t _confounder[16] = {0}; DATA_BLOB confounder = data_blob_const(_confounder, sizeof(_confounder)); DATA_BLOB pw_data; gnutls_datum_t old_nt_key = { .data = old_nt_hash, .size = sizeof(old_nt_hash), }; new_random_pass = samr_very_rand_pass(tctx, 128); torture_assert(tctx, *password != NULL, "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?"); oldpass = *password; server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); init_lsa_String(&account, account_string); s.in.user_handle = handle; s.in.info = &u; s.in.level = 25; ZERO_STRUCT(u); u.info25.info.fields_present = SAMR_FIELD_NT_PASSWORD_PRESENT; set_pw_in_buffer(u.info25.password.data, &new_random_pass); pw_data = data_blob_const(u.info25.password.data, 516); status = dcerpc_binding_handle_transport_session_key(b, tctx, &session_key); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", s.in.level, nt_errstr(status)); return false; } generate_random_buffer(_confounder, sizeof(_confounder)); samba_gnutls_arcfour_confounded_md5(&confounder, &session_key, &pw_data, SAMBA_GNUTLS_ENCRYPT); memcpy(&u.info25.password.data[516], _confounder, sizeof(_confounder)); torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with a password made up of only random bytes\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), "SetUserInfo failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, "RANDOM", nt_errstr(s.out.result)); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", s.in.level, nt_errstr(s.out.result)); ret = false; } torture_comment(tctx, "Testing ChangePasswordUser3 with a password made up of only random bytes\n"); mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length); new_random_pass = samr_very_rand_pass(tctx, 128); mdfour(new_nt_hash, new_random_pass.data, new_random_pass.length); set_pw_in_buffer(nt_pass.data, &new_random_pass); gnutls_cipher_init(&cipher_hnd, GNUTLS_CIPHER_ARCFOUR_128, &old_nt_key, NULL); gnutls_cipher_encrypt(cipher_hnd, nt_pass.data, 516); gnutls_cipher_deinit(cipher_hnd); E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 0; r.in.lm_password = NULL; r.in.lm_verifier = NULL; r.in.password3 = NULL; r.out.dominfo = &dominfo; r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, "RANDOM", nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); return false; } /* Perhaps the server has a 'min password age' set? */ } else if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed - %s\n", nt_errstr(r.out.result)); ret = false; } newpass = samr_rand_pass(tctx, 128); mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length); E_md4hash(newpass, new_nt_hash); status = init_samr_CryptPassword(newpass, &old_nt_hash_blob, &nt_pass); torture_assert_ntstatus_ok(tctx, status, "init_samr_CryptPassword failed"); E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); r.in.server = &server; r.in.account = &account; r.in.nt_password = &nt_pass; r.in.nt_verifier = &nt_verifier; r.in.lm_change = 0; r.in.lm_password = NULL; r.in.lm_verifier = NULL; r.in.password3 = NULL; r.out.dominfo = &dominfo; r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), "ChangePasswordUser3 failed"); torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", __location__, __FUNCTION__, oldpass, newpass, nt_errstr(r.out.result)); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); return false; } /* Perhaps the server has a 'min password age' set? */ } else { torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3 (on second random password)"); *password = talloc_strdup(tctx, newpass); } return ret; } static bool test_GetMembersInAlias(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *alias_handle) { struct samr_GetMembersInAlias r; struct lsa_SidArray sids; torture_comment(tctx, "Testing GetMembersInAlias\n"); r.in.alias_handle = alias_handle; r.out.sids = &sids; torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetMembersInAlias_r(b, tctx, &r), "GetMembersInAlias failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "GetMembersInAlias failed"); return true; } static bool test_AddMemberToAlias(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *alias_handle, const struct dom_sid *domain_sid) { struct samr_AddAliasMember r; struct samr_DeleteAliasMember d; struct dom_sid *sid; sid = dom_sid_add_rid(tctx, domain_sid, 512); torture_comment(tctx, "Testing AddAliasMember\n"); r.in.alias_handle = alias_handle; r.in.sid = sid; torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddAliasMember_r(b, tctx, &r), "AddAliasMember failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "AddAliasMember failed"); d.in.alias_handle = alias_handle; d.in.sid = sid; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteAliasMember_r(b, tctx, &d), "DeleteAliasMember failed"); torture_assert_ntstatus_ok(tctx, d.out.result, "DelAliasMember failed"); return true; } static bool test_AddMultipleMembersToAlias(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *alias_handle) { struct samr_AddMultipleMembersToAlias a; struct samr_RemoveMultipleMembersFromAlias r; struct lsa_SidArray sids; torture_comment(tctx, "Testing AddMultipleMembersToAlias\n"); a.in.alias_handle = alias_handle; a.in.sids = &sids; sids.num_sids = 3; sids.sids = talloc_array(tctx, struct lsa_SidPtr, 3); sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1"); sids.sids[1].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-2"); sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-3"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddMultipleMembersToAlias_r(b, tctx, &a), "AddMultipleMembersToAlias failed"); torture_assert_ntstatus_ok(tctx, a.out.result, "AddMultipleMembersToAlias"); torture_comment(tctx, "Testing RemoveMultipleMembersFromAlias\n"); r.in.alias_handle = alias_handle; r.in.sids = &sids; torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), "RemoveMultipleMembersFromAlias failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed"); /* strange! removing twice doesn't give any error */ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), "RemoveMultipleMembersFromAlias failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed"); /* but removing an alias that isn't there does */ sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-4"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), "RemoveMultipleMembersFromAlias failed"); torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, "RemoveMultipleMembersFromAlias"); return true; } static bool test_GetAliasMembership(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle) { struct samr_GetAliasMembership r; struct lsa_SidArray sids; struct samr_Ids rids; torture_comment(tctx, "Testing GetAliasMembership\n"); r.in.domain_handle = domain_handle; r.in.sids = &sids; r.out.rids = &rids; sids.num_sids = 0; sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), "GetAliasMembership failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "samr_GetAliasMembership failed"); torture_assert_int_equal(tctx, sids.num_sids, rids.count, "protocol misbehaviour"); sids.num_sids = 1; sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids); sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), "samr_GetAliasMembership failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "samr_GetAliasMembership failed"); #if 0 /* only true for w2k8 it seems * win7, xp, w2k3 will return a 0 length array pointer */ if (rids.ids && (rids.count == 0)) { torture_fail(tctx, "samr_GetAliasMembership returned 0 count and a rids array"); } #endif if (!rids.ids && rids.count) { torture_fail(tctx, "samr_GetAliasMembership returned non-0 count but no rids"); } return true; } static bool test_TestPrivateFunctionsUser(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *user_handle) { struct samr_TestPrivateFunctionsUser r; torture_comment(tctx, "Testing TestPrivateFunctionsUser\n"); r.in.user_handle = user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_TestPrivateFunctionsUser_r(b, tctx, &r), "TestPrivateFunctionsUser failed"); torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsUser"); return true; } static bool test_QueryUserInfo_pwdlastset(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle, bool use_info2, NTTIME *pwdlastset) { NTSTATUS status; uint16_t levels[] = { /* 3, */ 5, 21 }; int i; /* NTTIME pwdlastset3 = 0; */ NTTIME pwdlastset5 = 0; NTTIME pwdlastset21 = 0; torture_comment(tctx, "Testing QueryUserInfo%s level 5 and 21 call ", use_info2 ? "2":""); for (i=0; iinfo3.last_password_change; */ break; case 5: pwdlastset5 = info->info5.last_password_change; break; case 21: pwdlastset21 = info->info21.last_password_change; break; default: return false; } } /* torture_assert_int_equal(tctx, pwdlastset3, pwdlastset5, "pwdlastset mixup"); */ torture_assert_int_equal(tctx, pwdlastset5, pwdlastset21, "pwdlastset mixup"); *pwdlastset = pwdlastset21; torture_comment(tctx, "(pwdlastset: %llu)\n", (unsigned long long) *pwdlastset); return true; } static bool test_SamLogon(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *machine_credentials, struct cli_credentials *test_credentials, 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; struct dcerpc_binding_handle *b = p->binding_handle; torture_assert(tctx, (creds = cli_credentials_get_netlogon_creds(machine_credentials)), ""); if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) { flags |= CLI_CRED_NTLMv2_AUTH; } cli_credentials_get_ntlm_username_domain(test_credentials, tctx, &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 = 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_SUPPORTS_AES) { netlogon_creds_aes_encrypt(creds, pinfo.lmpassword.hash, 16); netlogon_creds_aes_encrypt(creds, pinfo.ntpassword.hash, 16); } else 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); } pinfo.identity_info = identity; logon.password = &pinfo; r.in.logon_level = NetlogonInteractiveInformation; } else { generate_random_buffer(ninfo.challenge, sizeof(ninfo.challenge)); chal = data_blob_const(ninfo.challenge, sizeof(ninfo.challenge)); names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials), cli_credentials_get_domain(test_credentials)); status = cli_credentials_get_ntlm_response(test_credentials, tctx, &flags, chal, NULL, /* server_timestamp */ names_blob, &lm_resp, &nt_resp, NULL, NULL); torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); ninfo.lm.data = lm_resp.data; ninfo.lm.length = lm_resp.length; ninfo.nt.data = nt_resp.data; ninfo.nt.length = nt_resp.length; 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 = &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", identity.account_name.string); r.in.validation_level = 6; torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), "netr_LogonSamLogonEx failed"); if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) { r.in.validation_level = 3; torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), "netr_LogonSamLogonEx failed"); } if (!NT_STATUS_IS_OK(r.out.result)) { torture_assert_ntstatus_equal(tctx, r.out.result, expected_result, "LogonSamLogonEx failed"); return true; } else { torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogonEx failed"); } return true; } static bool test_SamLogon_with_creds(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *machine_creds, const char *acct_name, const char *password, NTSTATUS expected_samlogon_result, bool interactive) { bool ret = true; struct cli_credentials *test_credentials; test_credentials = cli_credentials_init(tctx); cli_credentials_set_workstation(test_credentials, cli_credentials_get_workstation(machine_creds), CRED_SPECIFIED); cli_credentials_set_domain(test_credentials, cli_credentials_get_domain(machine_creds), CRED_SPECIFIED); cli_credentials_set_username(test_credentials, acct_name, CRED_SPECIFIED); cli_credentials_set_password(test_credentials, password, CRED_SPECIFIED); torture_comment(tctx, "Testing samlogon (%s) as %s password: %s\n", interactive ? "interactive" : "network", acct_name, password); if (!test_SamLogon(tctx, p, machine_creds, test_credentials, expected_samlogon_result, interactive)) { torture_result(tctx, TORTURE_FAIL, "new password did not work\n"); ret = false; } return ret; } static bool test_SetPassword_level(struct dcerpc_pipe *p, struct dcerpc_pipe *np, struct torture_context *tctx, struct policy_handle *handle, uint16_t level, uint32_t fields_present, uint8_t password_expired, bool *matched_expected_error, bool use_setinfo2, const char *acct_name, char **password, struct cli_credentials *machine_creds, bool use_queryinfo2, NTTIME *pwdlastset, NTSTATUS expected_samlogon_result) { const char *fields = NULL; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; switch (level) { case 21: case 23: case 25: case 32: fields = talloc_asprintf(tctx, "(fields_present: 0x%08x)", fields_present); break; default: break; } torture_comment(tctx, "Testing SetUserInfo%s level %d call " "(password_expired: %d) %s\n", use_setinfo2 ? "2":"", level, password_expired, fields ? fields : ""); if (!test_SetUserPass_level_ex(p, tctx, handle, level, fields_present, password, password_expired, use_setinfo2, matched_expected_error)) { ret = false; } if (!test_QueryUserInfo_pwdlastset(b, tctx, handle, use_queryinfo2, pwdlastset)) { ret = false; } if (*matched_expected_error == true) { return ret; } if (!test_SamLogon_with_creds(tctx, np, machine_creds, acct_name, *password, expected_samlogon_result, false)) { ret = false; } return ret; } static bool setup_schannel_netlogon_pipe(struct torture_context *tctx, struct cli_credentials *credentials, struct dcerpc_pipe **p) { struct dcerpc_binding *b; NTSTATUS status; torture_assert_ntstatus_ok(tctx, torture_rpc_binding(tctx, &b), "failed to get rpc binding"); /* We have to use schannel, otherwise the SamLogonEx fails * with INTERNAL_ERROR */ status = dcerpc_binding_set_flags(b, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SEAL | DCERPC_SCHANNEL_AUTO, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); torture_assert_ntstatus_ok(tctx, dcerpc_pipe_connect_b(tctx, p, b, &ndr_table_netlogon, credentials, tctx->ev, tctx->lp_ctx), "failed to bind to netlogon"); return true; } static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t acct_flags, const char *acct_name, struct policy_handle *handle, char **password, struct cli_credentials *machine_credentials) { int s = 0, q = 0, f = 0, l = 0, z = 0; bool ret = true; int delay = 50000; bool set_levels[] = { false, true }; bool query_levels[] = { false, true }; uint32_t levels[] = { 18, 21, 26, 23, 24, 25, 31 }; /* Second half only used when TEST_ALL_LEVELS defined */ uint32_t nonzeros[] = { 1, 24 }; uint32_t fields_present[] = { 0, SAMR_FIELD_EXPIRED_FLAG, SAMR_FIELD_LAST_PWD_CHANGE, SAMR_FIELD_EXPIRED_FLAG | SAMR_FIELD_LAST_PWD_CHANGE, SAMR_FIELD_COMMENT, SAMR_FIELD_NT_PASSWORD_PRESENT, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE | SAMR_FIELD_EXPIRED_FLAG }; struct dcerpc_pipe *np = NULL; if (torture_setting_bool(tctx, "samba3", false) || torture_setting_bool(tctx, "samba4", false)) { delay = 999999; torture_comment(tctx, "Samba3 has second granularity, setting delay to: %d\n", delay); } torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); /* set to 1 to enable testing for all possible opcode (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2) combinations */ #if 0 #define TEST_ALL_LEVELS 1 #define TEST_SET_LEVELS 1 #define TEST_QUERY_LEVELS 1 #endif #ifdef TEST_ALL_LEVELS for (l=0; l 0) && (pwdlastset_new > 0) && (pwdlastset_old >= pwdlastset_new)) { torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); ret = false; } break; } pwdlastset_old = pwdlastset_new; usleep(delay); /* set #2 */ /* set a password, pwdlastset needs to get updated (increased * value), password_expired value used here is 0 */ if (!test_SetPassword_level(p, np, tctx, handle, levels[l], fields_present[f], 0, &matched_expected_error, set_levels[s], acct_name, password, machine_credentials, query_levels[q], &pwdlastset_new, expected_samlogon_result)) { ret = false; } /* when a password has been changed, pwdlastset must not be 0 afterwards * and must be larger then the old value */ switch (levels[l]) { case 21: case 23: case 25: case 32: /* SAMR_FIELD_EXPIRED_FLAG has not been set and no * password has been changed, old and new pwdlastset * need to be the same value */ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) { torture_assert_int_equal(tctx, pwdlastset_old, pwdlastset_new, "pwdlastset must be equal"); break; } break; default: if (pwdlastset_old >= pwdlastset_new) { torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n", (unsigned long long) pwdlastset_old, (unsigned long long) pwdlastset_new); ret = false; } if (pwdlastset_new == 0) { torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " "expected non-0 pwdlastset, got: %llu\n", (unsigned long long) pwdlastset_new); ret = false; } break; } switch (levels[l]) { case 21: case 23: case 25: case 32: if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && (pwdlastset_old > 0) && (pwdlastset_new > 0) && (pwdlastset_old >= pwdlastset_new)) { torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); ret = false; } break; } pwdlastset_old = pwdlastset_new; usleep(delay); /* set #2b */ /* set a password, pwdlastset needs to get updated (increased * value), password_expired value used here is 0 */ if (!test_SetPassword_level(p, np, tctx, handle, levels[l], fields_present[f], 0, &matched_expected_error, set_levels[s], acct_name, password, machine_credentials, query_levels[q], &pwdlastset_new, expected_samlogon_result)) { ret = false; } /* when a password has been changed, pwdlastset must not be 0 afterwards * and must be larger then the old value */ switch (levels[l]) { case 21: case 23: case 25: case 32: /* SAMR_FIELD_EXPIRED_FLAG has not been set and no * password has been changed, old and new pwdlastset * need to be the same value */ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) { torture_assert_int_equal(tctx, pwdlastset_old, pwdlastset_new, "pwdlastset must be equal"); break; } break; default: if (pwdlastset_old >= pwdlastset_new) { torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n", (unsigned long long) pwdlastset_old, (unsigned long long) pwdlastset_new); ret = false; } if (pwdlastset_new == 0) { torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " "expected non-0 pwdlastset, got: %llu\n", (unsigned long long) pwdlastset_new); ret = false; } break; } switch (levels[l]) { case 21: case 23: case 25: case 32: if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && (pwdlastset_old > 0) && (pwdlastset_new > 0) && (pwdlastset_old >= pwdlastset_new)) { torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); ret = false; } break; } pwdlastset_old = pwdlastset_new; usleep(delay); /* set #3 */ /* set a password and force password change (pwdlastset 0) by * setting the password expired flag to a non-0 value */ if (!test_SetPassword_level(p, np, tctx, handle, levels[l], fields_present[f], nonzeros[z], &matched_expected_error, set_levels[s], acct_name, password, machine_credentials, query_levels[q], &pwdlastset_new, expected_samlogon_result)) { ret = false; } /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25 * set without the SAMR_FIELD_EXPIRED_FLAG */ switch (levels[l]) { case 21: case 23: case 25: case 32: if ((pwdlastset_new != 0) && !(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG)) { torture_comment(tctx, "not considering a non-0 " "pwdLastSet as a an error as the " "SAMR_FIELD_EXPIRED_FLAG has not " "been set\n"); break; } /* SAMR_FIELD_EXPIRED_FLAG has not been set and no * password has been changed, old and new pwdlastset * need to be the same value */ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) { torture_assert_int_equal(tctx, pwdlastset_old, pwdlastset_new, "pwdlastset must be equal"); break; } break; default: if (pwdlastset_new != 0) { torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " "expected pwdLastSet 0, got %llu\n", (unsigned long long) pwdlastset_old); ret = false; } break; } switch (levels[l]) { case 21: case 23: case 25: case 32: if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && (pwdlastset_old > 0) && (pwdlastset_new > 0) && (pwdlastset_old >= pwdlastset_new)) { torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); ret = false; } break; } /* if the level we are testing does not have a fields_present * field, skip all fields present tests by setting f to to * arraysize */ switch (levels[l]) { case 18: case 24: case 26: case 31: f = ARRAY_SIZE(fields_present); break; } #ifdef TEST_QUERY_LEVELS } #endif #ifdef TEST_SET_LEVELS } #endif } /* fields present */ } /* nonzeros */ } /* levels */ #undef TEST_SET_LEVELS #undef TEST_QUERY_LEVELS talloc_free(np); return ret; } static bool test_QueryUserInfo_badpwdcount(struct dcerpc_binding_handle *b, 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_r(b, tctx, &r), "failed to query userinfo"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query userinfo"); *badpwdcount = info->info3.bad_password_count; torture_comment(tctx, " (bad password count: %d)\n", *badpwdcount); return true; } static bool test_SetUserInfo_acct_flags(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *user_handle, uint32_t acct_flags) { struct samr_SetUserInfo r; union samr_UserInfo user_info; torture_comment(tctx, "Testing SetUserInfo level 16\n"); user_info.info16.acct_flags = acct_flags; r.in.user_handle = user_handle; r.in.level = 16; r.in.info = &user_info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &r), "failed to set account flags"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to set account flags"); 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 dcerpc_binding_handle *b = p->binding_handle; torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), "failed to set password"); torture_comment(tctx, "Testing SetUserInfo level 16 (enable account)\n"); torture_assert(tctx, test_SetUserInfo_acct_flags(b, tctx, user_handle, acct_flags & ~ACB_DISABLED), "failed to enable user"); torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), "failed to set password"); return true; } static bool test_SetDomainInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, enum samr_DomainInfoClass level, union samr_DomainInfo *info) { struct samr_SetDomainInfo r; r.in.domain_handle = domain_handle; r.in.level = level; r.in.info = info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &r), "failed to set domain info"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to set domain info"); return true; } static bool test_SetDomainInfo_ntstatus(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, enum samr_DomainInfoClass level, union samr_DomainInfo *info, NTSTATUS expected) { struct samr_SetDomainInfo r; r.in.domain_handle = domain_handle; r.in.level = level; r.in.info = info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &r), "SetDomainInfo failed"); torture_assert_ntstatus_equal(tctx, r.out.result, expected, ""); return true; } static bool test_QueryDomainInfo2_level(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, enum samr_DomainInfoClass level, union samr_DomainInfo **q_info) { struct samr_QueryDomainInfo2 r; r.in.domain_handle = domain_handle; r.in.level = level; r.out.info = q_info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r), "failed to query domain info"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query domain info"); 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; uint32_t lockout_seconds = 5; uint64_t delta_time_factor = 10 * 1000 * 1000; struct dcerpc_binding_handle *b = p->binding_handle; if (torture_setting_bool(tctx, "samba3", false)) { lockout_seconds = 60; } 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; info.info1.min_password_age = 0; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainPasswordInformation, &info), "failed to set password history length and min passwd age"); info.info12 = *info12; info.info12.lockout_threshold = lockout_threshold; /* set lockout duration of 5 seconds */ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); info.info12.lockout_window = ~(lockout_seconds * delta_time_factor); torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &info), "failed to set lockout threshold"); /* reset bad pwd count */ torture_assert(tctx, test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), ""); /* enable or disable account */ if (disable) { torture_assert(tctx, test_SetUserInfo_acct_flags(b, tctx, user_handle, acct_flags | ACB_DISABLED), "failed to disable user"); } else { torture_assert(tctx, test_SetUserInfo_acct_flags(b, tctx, user_handle, acct_flags & ~ACB_DISABLED), "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(b, 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(b, 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(b, 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, "did not successfully to obtain %s for %s login with old password (#%d of #%d in history)", nt_errstr(expected_success_status), interactive ? "interactive" : "network", i, password_history_length)); } torture_assert(tctx, test_QueryUserInfo_badpwdcount(b, 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(b, 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_handle *b = p->binding_handle; 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 }, }; torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); /* backup old policies */ torture_assert(tctx, test_QueryDomainInfo2_level(b, tctx, domain_handle, DomainPasswordInformation, &q_info), "failed to query domain info level 1"); info1 = q_info->info1; _info1 = info1; torture_assert(tctx, test_QueryDomainInfo2_level(b, tctx, domain_handle, DomainLockoutInformation, &q_info), "failed to query domain info level 12"); info12 = q_info->info12; _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; } if (!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)) { torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment); ret = false; } else { torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment); } } /* restore policies */ s_info.info1 = info1; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainPasswordInformation, &s_info), "failed to set password information"); s_info.info12 = info12; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &s_info), "failed to set lockout information"); return ret; } static bool test_QueryUserInfo_lockout(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *acct_name, uint16_t raw_bad_password_count, uint16_t effective_bad_password_count, uint32_t effective_acb_lockout) { struct policy_handle user_handle; union samr_UserInfo *i; struct samr_QueryUserInfo r; NTSTATUS status = test_OpenUser_byname(b, tctx, domain_handle, acct_name, &user_handle); if (!NT_STATUS_IS_OK(status)) { return false; } r.in.user_handle = &user_handle; r.in.level = 3; r.out.info = &i; torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), "failed to query userinfo"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query userinfo"); torture_comment(tctx, " (acct_flags: 0x%08x) (raw_bad_pwd_count: %u)\n", i->info3.acct_flags, i->info3.bad_password_count); torture_assert_int_equal(tctx, i->info3.bad_password_count, raw_bad_password_count, "raw badpwdcount"); torture_assert_int_equal(tctx, i->info3.acct_flags & ACB_AUTOLOCK, effective_acb_lockout, "effective acb_lockout"); TALLOC_FREE(i); r.in.user_handle = &user_handle; r.in.level = 5; r.out.info = &i; torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), "failed to query userinfo"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query userinfo"); torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n", i->info5.acct_flags, i->info5.bad_password_count); torture_assert_int_equal(tctx, i->info5.bad_password_count, effective_bad_password_count, "effective badpwdcount"); torture_assert_int_equal(tctx, i->info5.acct_flags & ACB_AUTOLOCK, effective_acb_lockout, "effective acb_lockout"); TALLOC_FREE(i); r.in.user_handle = &user_handle; r.in.level = 16; r.out.info = &i; torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), "failed to query userinfo"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query userinfo"); torture_comment(tctx, " (acct_flags: 0x%08x)\n", i->info16.acct_flags); torture_assert_int_equal(tctx, i->info16.acct_flags & ACB_AUTOLOCK, effective_acb_lockout, "effective acb_lockout"); TALLOC_FREE(i); r.in.user_handle = &user_handle; r.in.level = 21; r.out.info = &i; torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), "failed to query userinfo"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query userinfo"); torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n", i->info21.acct_flags, i->info21.bad_password_count); torture_assert_int_equal(tctx, i->info21.bad_password_count, effective_bad_password_count, "effective badpwdcount"); torture_assert_int_equal(tctx, i->info21.acct_flags & ACB_AUTOLOCK, effective_acb_lockout, "effective acb_lockout"); TALLOC_FREE(i); if (!test_samr_handle_Close(b, tctx, &user_handle)) { return false; } return true; } static bool test_Password_lockout(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, uint32_t password_history_length, NTSTATUS expected_success_status, struct samr_DomInfo1 *info1, struct samr_DomInfo12 *info12) { union samr_DomainInfo info; uint64_t lockout_threshold = 1; uint32_t lockout_seconds = 5; uint64_t delta_time_factor = 10 * 1000 * 1000; struct dcerpc_binding_handle *b = p->binding_handle; if (torture_setting_bool(tctx, "samba3", false)) { lockout_seconds = 60; } torture_comment(tctx, "\nTesting account lockout: %s\n", comment); /* set policies */ info.info1 = *info1; torture_comment(tctx, "setting password history length to %d.\n", password_history_length); info.info1.password_history_length = password_history_length; torture_comment(tctx, "setting min password again.\n"); info.info1.min_password_age = 0; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainPasswordInformation, &info), "failed to set password history length"); info.info12 = *info12; info.info12.lockout_threshold = lockout_threshold; /* set lockout duration < lockout window: should fail */ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); info.info12.lockout_window = ~((lockout_seconds + 1) * delta_time_factor); torture_assert(tctx, test_SetDomainInfo_ntstatus(b, tctx, domain_handle, DomainLockoutInformation, &info, NT_STATUS_INVALID_PARAMETER), "setting lockout duration < lockout window gave unexpected result"); info.info12.lockout_duration = 0; info.info12.lockout_window = 0; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &info), "failed to set lockout window and duration to 0"); /* set lockout duration of 5 seconds */ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); info.info12.lockout_window = ~(lockout_seconds * delta_time_factor); torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &info), "failed to set lockout window and duration to 5 seconds"); /* reset bad pwd count */ torture_assert(tctx, test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), ""); /* enable or disable account */ if (disable) { torture_assert(tctx, test_SetUserInfo_acct_flags(b, tctx, user_handle, acct_flags | ACB_DISABLED), "failed to disable user"); } else { torture_assert(tctx, test_SetUserInfo_acct_flags(b, tctx, user_handle, acct_flags & ~ACB_DISABLED), "failed to enable user"); } /* test logon with right password */ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, *password, expected_success_status, interactive)) { torture_fail(tctx, "failed to auth with latest password"); } torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 0, 0, 0), "expected account to not be locked"); /* test with wrong password ==> lockout */ 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"); } /* * curiously, windows does _not_ return fresh values of * effective bad_password_count and ACB_AUTOLOCK. */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to not be locked"); /* test with good password */ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT, interactive)) { torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT"); } /* bad pwd count should not get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT), "got wrong status from ChangePasswordUser2"); /* bad pwd count should not get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT), "got wrong status from ChangePasswordUser2"); /* bad pwd count should not get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); /* with bad password */ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, "random_crap2", NT_STATUS_ACCOUNT_LOCKED_OUT, interactive)) { torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT"); } /* bad pwd count should not get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); /* let lockout duration expire ==> unlock */ torture_comment(tctx, "let lockout duration expire...\n"); sleep(lockout_seconds + 1); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 0, 0), "expected account to not be locked"); if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, *password, expected_success_status, interactive)) { torture_fail(tctx, "failed to authenticate after lockout expired"); } if (NT_STATUS_IS_OK(expected_success_status)) { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 0, 0, 0), "expected account to not be locked"); } else { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 0, 0), "expected account to not be locked"); } torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), "got wrong status from ChangePasswordUser2"); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT), "got wrong status from ChangePasswordUser2"); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT), "got wrong status from ChangePasswordUser2"); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, ACB_AUTOLOCK), "expected account to be locked"); /* let lockout duration expire ==> unlock */ torture_comment(tctx, "let lockout duration expire...\n"); sleep(lockout_seconds + 1); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 0, 0), "expected account to not be locked"); if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, *password, expected_success_status, interactive)) { torture_fail(tctx, "failed to authenticate after lockout expired"); } if (NT_STATUS_IS_OK(expected_success_status)) { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 0, 0, 0), "expected account to not be locked"); } else { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 0, 0), "expected account to not be locked"); } /* Testing ChangePasswordUser behaviour with 3 attempts */ info.info12.lockout_threshold = 3; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &info), "failed to set lockout threshold to 3"); if (NT_STATUS_IS_OK(expected_success_status)) { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 0, 0, 0), "expected account to not be locked"); } else { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 0, 0), "expected account to not be locked"); } torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), "got wrong status from ChangePasswordUser2"); /* bad pwd count will get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 1, 1, 0), "expected account to not be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), "got wrong status from ChangePasswordUser2"); /* bad pwd count will get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 2, 2, 0), "expected account to not be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), "got wrong status from ChangePasswordUser2"); /* bad pwd count should get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 3, 3, ACB_AUTOLOCK), "expected account to be locked"); torture_assert(tctx, test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT), "got wrong status from ChangePasswordUser2"); /* bad pwd count should not get updated */ torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 3, 3, ACB_AUTOLOCK), "expected account to be locked"); /* let lockout duration expire ==> unlock */ torture_comment(tctx, "let lockout duration expire...\n"); sleep(lockout_seconds + 1); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 3, 0, 0), "expected account to not be locked"); torture_assert(tctx, test_ChangePasswordUser2(p, tctx, acct_name, password, NULL, false), "got wrong status from ChangePasswordUser2"); torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 3, 0, 0), "expected account to not be locked"); /* Used to reset the badPwdCount for the other tests */ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, *password, expected_success_status, interactive)) { torture_fail(tctx, "failed to authenticate after lockout expired"); } if (NT_STATUS_IS_OK(expected_success_status)) { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 0, 0, 0), "expected account to not be locked"); } else { torture_assert(tctx, test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, 3, 0, 0), "expected account to not be locked"); } return true; } static bool test_Password_lockout_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_handle *b = p->binding_handle; struct dcerpc_pipe *np; int i; struct { const char *comment; bool disabled; bool interactive; uint32_t password_history_length; 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 = "network logon (enabled account, history len = 1)", .disabled = false, .interactive = false, .expected_success_status= NT_STATUS_OK, .password_history_length = 1 }, { .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 }, { .comment = "interactive logon (enabled account, history len = 1)", .disabled = false, .interactive = true, .expected_success_status= NT_STATUS_OK, .password_history_length = 1 }, }; torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); /* backup old policies */ torture_assert(tctx, test_QueryDomainInfo2_level(b, tctx, domain_handle, DomainPasswordInformation, &q_info), "failed to query domain info level 1"); info1 = q_info->info1; _info1 = info1; torture_assert(tctx, test_QueryDomainInfo2_level(b, tctx, domain_handle, DomainLockoutInformation, &q_info), "failed to query domain info level 12"); info12 = q_info->info12; _info12 = info12; /* run tests */ for (i=0; i < ARRAY_SIZE(creds); i++) { bool test_passed; /* skip trust tests for now */ if (acct_flags & ACB_WSTRUST || acct_flags & ACB_SVRTRUST || acct_flags & ACB_DOMTRUST) { continue; } test_passed = test_Password_lockout(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].password_history_length, creds[i].expected_success_status, &_info1, &_info12); ret &= test_passed; if (!test_passed) { torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment); break; } else { torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment); } } /* restore policies */ s_info.info1 = info1; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainPasswordInformation, &s_info), "failed to set password information"); s_info.info12 = info12; torture_assert(tctx, test_SetDomainInfo(b, tctx, domain_handle, DomainLockoutInformation, &s_info), "failed to set lockout information"); return ret; } static bool test_DeleteUser_with_privs(struct dcerpc_pipe *p, struct dcerpc_pipe *lp, struct torture_context *tctx, struct policy_handle *domain_handle, struct policy_handle *lsa_handle, struct policy_handle *user_handle, const struct dom_sid *domain_sid, uint32_t rid, struct cli_credentials *machine_credentials) { bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; struct dcerpc_binding_handle *lb = lp->binding_handle; struct policy_handle lsa_acct_handle; struct dom_sid *user_sid; user_sid = dom_sid_add_rid(tctx, domain_sid, rid); { struct lsa_EnumAccountRights r; struct lsa_RightSet rights; torture_comment(tctx, "Testing LSA EnumAccountRights\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.out.rights = &rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), "lsa_EnumAccountRights failed"); torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, "Expected enum rights for account to fail"); } { struct lsa_RightSet rights; struct lsa_StringLarge names[2]; struct lsa_AddAccountRights r; torture_comment(tctx, "Testing LSA AddAccountRights\n"); init_lsa_StringLarge(&names[0], "SeMachineAccountPrivilege"); init_lsa_StringLarge(&names[1], NULL); rights.count = 1; rights.names = names; r.in.handle = lsa_handle; r.in.sid = user_sid; r.in.rights = &rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r), "lsa_AddAccountRights failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to add privileges"); } { struct lsa_RightSet rights; struct lsa_StringLarge names[2]; struct lsa_AddAccountRights r; torture_comment(tctx, "Testing LSA AddAccountRights 1\n"); init_lsa_StringLarge(&names[0], "SeInteractiveLogonRight"); init_lsa_StringLarge(&names[1], NULL); rights.count = 1; rights.names = names; r.in.handle = lsa_handle; r.in.sid = user_sid; r.in.rights = &rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r), "lsa_AddAccountRights 1 failed"); if (torture_setting_bool(tctx, "nt4_dc", false)) { /* * The NT4 DC doesn't implement Rights. */ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NO_SUCH_PRIVILEGE, "Add rights failed with incorrect error"); } else { torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to add rights"); } } { struct lsa_EnumAccounts r; uint32_t resume_handle = 0; struct lsa_SidArray lsa_sid_array; int i; bool found_sid = false; torture_comment(tctx, "Testing LSA EnumAccounts\n"); r.in.handle = lsa_handle; r.in.num_entries = 0x1000; r.in.resume_handle = &resume_handle; r.out.sids = &lsa_sid_array; r.out.resume_handle = &resume_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), "lsa_EnumAccounts failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to enum accounts"); for (i=0; i < lsa_sid_array.num_sids; i++) { if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { found_sid = true; } } torture_assert(tctx, found_sid, "failed to list privileged account"); } { struct lsa_EnumAccountRights r; struct lsa_RightSet user_rights; uint32_t expected_count = 2; if (torture_setting_bool(tctx, "nt4_dc", false)) { /* * NT4 DC doesn't store rights. */ expected_count = 1; } torture_comment(tctx, "Testing LSA EnumAccountRights\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.out.rights = &user_rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), "lsa_EnumAccountRights failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to enum rights for account"); if (user_rights.count < expected_count) { torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights"); return false; } } { struct lsa_OpenAccount r; torture_comment(tctx, "Testing LSA OpenAccount\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.acct_handle = &lsa_acct_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r), "lsa_OpenAccount failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to open lsa account"); } { struct lsa_GetSystemAccessAccount r; uint32_t access_mask; torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n"); r.in.handle = &lsa_acct_handle; r.out.access_mask = &access_mask; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r), "lsa_GetSystemAccessAccount failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to get lsa system access account"); } { struct lsa_Close r; torture_comment(tctx, "Testing LSA Close\n"); r.in.handle = &lsa_acct_handle; r.out.handle = &lsa_acct_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(lb, tctx, &r), "lsa_Close failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to close lsa"); } { struct samr_DeleteUser r; torture_comment(tctx, "Testing SAMR DeleteUser\n"); r.in.user_handle = user_handle; r.out.user_handle = user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &r), "DeleteUser failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "DeleteUser failed"); } { struct lsa_EnumAccounts r; uint32_t resume_handle = 0; struct lsa_SidArray lsa_sid_array; int i; bool found_sid = false; torture_comment(tctx, "Testing LSA EnumAccounts\n"); r.in.handle = lsa_handle; r.in.num_entries = 0x1000; r.in.resume_handle = &resume_handle; r.out.sids = &lsa_sid_array; r.out.resume_handle = &resume_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), "lsa_EnumAccounts failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to enum accounts"); for (i=0; i < lsa_sid_array.num_sids; i++) { if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { found_sid = true; } } torture_assert(tctx, found_sid, "failed to list privileged account"); } { struct lsa_EnumAccountRights r; struct lsa_RightSet user_rights; torture_comment(tctx, "Testing LSA EnumAccountRights\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.out.rights = &user_rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), "lsa_EnumAccountRights failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to enum rights for account"); if (user_rights.count < 1) { torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights"); return false; } } { struct lsa_OpenAccount r; torture_comment(tctx, "Testing LSA OpenAccount\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.acct_handle = &lsa_acct_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r), "lsa_OpenAccount failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to open lsa account"); } { struct lsa_GetSystemAccessAccount r; uint32_t access_mask; torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n"); r.in.handle = &lsa_acct_handle; r.out.access_mask = &access_mask; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r), "lsa_GetSystemAccessAccount failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to get lsa system access account"); } { struct lsa_DeleteObject r; torture_comment(tctx, "Testing LSA DeleteObject\n"); r.in.handle = &lsa_acct_handle; r.out.handle = &lsa_acct_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(lb, tctx, &r), "lsa_DeleteObject failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to delete object"); } { struct lsa_EnumAccounts r; uint32_t resume_handle = 0; struct lsa_SidArray lsa_sid_array; int i; bool found_sid = false; torture_comment(tctx, "Testing LSA EnumAccounts\n"); r.in.handle = lsa_handle; r.in.num_entries = 0x1000; r.in.resume_handle = &resume_handle; r.out.sids = &lsa_sid_array; r.out.resume_handle = &resume_handle; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), "lsa_EnumAccounts failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "Failed to enum accounts"); for (i=0; i < lsa_sid_array.num_sids; i++) { if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { found_sid = true; } } torture_assert(tctx, !found_sid, "should not have listed privileged account"); } { struct lsa_EnumAccountRights r; struct lsa_RightSet user_rights; torture_comment(tctx, "Testing LSA EnumAccountRights\n"); r.in.handle = lsa_handle; r.in.sid = user_sid; r.out.rights = &user_rights; torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), "lsa_EnumAccountRights failed"); torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, "Failed to enum rights for account"); } return ret; } static bool test_user_ops(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *user_handle, struct policy_handle *domain_handle, const struct dom_sid *domain_sid, uint32_t base_acct_flags, const char *base_acct_name, enum torture_samr_choice which_ops, struct cli_credentials *machine_credentials) { char *password = NULL; struct samr_QueryUserInfo q; union samr_UserInfo *info; NTSTATUS status; struct dcerpc_binding_handle *b = p->binding_handle; bool ret = true; int i; uint32_t rid; const uint32_t password_fields[] = { SAMR_FIELD_NT_PASSWORD_PRESENT, SAMR_FIELD_LM_PASSWORD_PRESENT, SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT, 0 }; status = test_LookupName(b, tctx, domain_handle, base_acct_name, &rid); if (!NT_STATUS_IS_OK(status)) { ret = false; } switch (which_ops) { case TORTURE_SAMR_USER_ATTRIBUTES: if (!test_QuerySecurity(b, tctx, user_handle)) { ret = false; } if (!test_QueryUserInfo(b, tctx, user_handle)) { ret = false; } if (!test_QueryUserInfo2(b, tctx, user_handle)) { ret = false; } if (!test_SetUserInfo(b, tctx, user_handle, base_acct_flags, base_acct_name)) { ret = false; } if (!test_GetUserPwInfo(b, tctx, user_handle)) { ret = false; } if (!test_TestPrivateFunctionsUser(b, tctx, user_handle)) { ret = false; } if (!test_SetUserPass(p, tctx, user_handle, &password)) { ret = false; } break; case TORTURE_SAMR_PASSWORDS: if (base_acct_flags & (ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST)) { char simple_pass[9]; char *v = generate_random_str(tctx, 1); ZERO_STRUCT(simple_pass); memset(simple_pass, *v, sizeof(simple_pass) - 1); torture_comment(tctx, "Testing machine account password policy rules\n"); /* Workstation trust accounts don't seem to need to honour password quality policy */ if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { ret = false; } if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, simple_pass, false)) { ret = false; } /* reset again, to allow another 'user' password change */ if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { ret = false; } /* Try a 'short' password */ if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, samr_rand_pass(tctx, 4), false)) { ret = false; } /* Try a completely random password */ if (!test_ChangePasswordRandomBytes(p, tctx, base_acct_name, user_handle, &password)) { ret = false; } } for (i = 0; password_fields[i]; i++) { if (!test_SetUserPass_23(p, tctx, user_handle, password_fields[i], &password)) { ret = false; } /* check it was set right */ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { ret = false; } } for (i = 0; password_fields[i]; i++) { if (!test_SetUserPass_25(p, tctx, user_handle, password_fields[i], &password)) { ret = false; } /* check it was set right */ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { ret = false; } } if (!test_SetUserPass_31(p, tctx, user_handle, false, &password)) { ret = false; } for (i = 0; password_fields[i]; i++) { if (!test_SetUserPass_32(p, tctx, user_handle, password_fields[i], &password)) { ret = false; } /* check it was set right */ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { ret = false; } } if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) { ret = false; } if (!test_SetUserPassEx(p, tctx, user_handle, false, &password)) { ret = false; } if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) { ret = false; } if (!test_SetUserPass_18(p, tctx, user_handle, &password)) { ret = false; } if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { ret = false; } for (i = 0; password_fields[i]; i++) { if (password_fields[i] == SAMR_FIELD_LM_PASSWORD_PRESENT) { /* we need to skip as that would break * the ChangePasswordUser3 verify */ continue; } if (!test_SetUserPass_21(p, tctx, user_handle, password_fields[i], &password)) { ret = false; } /* check it was set right */ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { ret = false; } } q.in.user_handle = user_handle; q.in.level = 5; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), "QueryUserInfo failed"); if (!NT_STATUS_IS_OK(q.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", q.in.level, nt_errstr(q.out.result)); ret = false; } else { uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED); if ((info->info5.acct_flags) != expected_flags) { /* FIXME: GD */ if (!torture_setting_bool(tctx, "samba3", false)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", info->info5.acct_flags, expected_flags); ret = false; } } if (info->info5.rid != rid) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned %u when we expected rid of %u\n", info->info5.rid, rid); } } break; case TORTURE_SAMR_PASSWORDS_PWDLASTSET: /* test last password change timestamp behaviour */ torture_assert(tctx, test_SetPassword_pwdlastset(p, tctx, base_acct_flags, base_acct_name, user_handle, &password, machine_credentials), "pwdLastSet test failed\n"); break; case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: /* test bad pwd count change behaviour */ torture_assert(tctx, test_Password_badpwdcount_wrap(p, tctx, base_acct_flags, base_acct_name, domain_handle, user_handle, &password, machine_credentials), "badPwdCount test failed\n"); break; case TORTURE_SAMR_PASSWORDS_LOCKOUT: torture_assert(tctx, test_Password_lockout_wrap(p, tctx, base_acct_flags, base_acct_name, domain_handle, user_handle, &password, machine_credentials), "Lockout test failed"); break; case TORTURE_SAMR_USER_PRIVILEGES: { struct dcerpc_pipe *lp; struct policy_handle *lsa_handle; struct dcerpc_binding_handle *lb; status = torture_rpc_connection(tctx, &lp, &ndr_table_lsarpc); torture_assert_ntstatus_ok(tctx, status, "Failed to open LSA pipe"); lb = lp->binding_handle; if (!test_lsa_OpenPolicy2(lb, tctx, &lsa_handle)) { ret = false; } if (!test_DeleteUser_with_privs(p, lp, tctx, domain_handle, lsa_handle, user_handle, domain_sid, rid, machine_credentials)) { ret = false; } if (!test_lsa_Close(lb, tctx, lsa_handle)) { ret = false; } if (!ret) { torture_result(tctx, TORTURE_FAIL, "privileged user delete test failed\n"); } break; } case TORTURE_SAMR_OTHER: case TORTURE_SAMR_MANY_ACCOUNTS: case TORTURE_SAMR_MANY_GROUPS: case TORTURE_SAMR_MANY_ALIASES: /* We just need the account to exist */ break; } return ret; } static bool test_alias_ops(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *alias_handle, const struct dom_sid *domain_sid) { bool ret = true; if (!torture_setting_bool(tctx, "samba3", false)) { if (!test_QuerySecurity(b, tctx, alias_handle)) { ret = false; } } if (!test_QueryAliasInfo(b, tctx, alias_handle)) { ret = false; } if (!test_SetAliasInfo(b, tctx, alias_handle)) { ret = false; } if (!test_AddMemberToAlias(b, tctx, alias_handle, domain_sid)) { ret = false; } if (torture_setting_bool(tctx, "samba3", false) || torture_setting_bool(tctx, "samba4", false)) { torture_comment(tctx, "skipping MultipleMembers Alias tests against Samba\n"); return ret; } if (!test_AddMultipleMembersToAlias(b, tctx, alias_handle)) { ret = false; } return ret; } static bool test_DeleteUser(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *user_handle) { struct samr_DeleteUser d; torture_comment(tctx, "Testing DeleteUser\n"); d.in.user_handle = user_handle; d.out.user_handle = user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d), "DeleteUser failed"); torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteUser"); return true; } bool test_DeleteUser_byname(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle, const char *name) { NTSTATUS status; struct samr_DeleteUser d; struct policy_handle user_handle; uint32_t rid; status = test_LookupName(b, tctx, handle, name, &rid); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = test_OpenUser_byname(b, tctx, handle, name, &user_handle); if (!NT_STATUS_IS_OK(status)) { goto failed; } d.in.user_handle = &user_handle; d.out.user_handle = &user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d), "DeleteUser failed"); if (!NT_STATUS_IS_OK(d.out.result)) { status = d.out.result; goto failed; } return true; failed: torture_result(tctx, TORTURE_FAIL, "DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status)); return false; } static bool test_DeleteGroup_byname(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle, const char *name) { NTSTATUS status; struct samr_OpenGroup r; struct samr_DeleteDomainGroup d; struct policy_handle group_handle; uint32_t rid; status = test_LookupName(b, tctx, handle, name, &rid); if (!NT_STATUS_IS_OK(status)) { goto failed; } r.in.domain_handle = handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.in.rid = rid; r.out.group_handle = &group_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r), "OpenGroup failed"); if (!NT_STATUS_IS_OK(r.out.result)) { status = r.out.result; goto failed; } d.in.group_handle = &group_handle; d.out.group_handle = &group_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomainGroup_r(b, tctx, &d), "DeleteDomainGroup failed"); if (!NT_STATUS_IS_OK(d.out.result)) { status = d.out.result; goto failed; } return true; failed: torture_result(tctx, TORTURE_FAIL, "DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status)); return false; } static bool test_DeleteAlias_byname(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *name) { NTSTATUS status; struct samr_OpenAlias r; struct samr_DeleteDomAlias d; struct policy_handle alias_handle; uint32_t rid; torture_comment(tctx, "Testing DeleteAlias_byname\n"); status = test_LookupName(b, tctx, domain_handle, name, &rid); if (!NT_STATUS_IS_OK(status)) { goto failed; } r.in.domain_handle = domain_handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.in.rid = rid; r.out.alias_handle = &alias_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenAlias_r(b, tctx, &r), "OpenAlias failed"); if (!NT_STATUS_IS_OK(r.out.result)) { status = r.out.result; goto failed; } d.in.alias_handle = &alias_handle; d.out.alias_handle = &alias_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d), "DeleteDomAlias failed"); if (!NT_STATUS_IS_OK(d.out.result)) { status = d.out.result; goto failed; } return true; failed: torture_result(tctx, TORTURE_FAIL, "DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status)); return false; } static bool test_DeleteAlias(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *alias_handle) { struct samr_DeleteDomAlias d; bool ret = true; torture_comment(tctx, "Testing DeleteAlias\n"); d.in.alias_handle = alias_handle; d.out.alias_handle = alias_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d), "DeleteDomAlias failed"); if (!NT_STATUS_IS_OK(d.out.result)) { torture_result(tctx, TORTURE_FAIL, "DeleteAlias failed - %s\n", nt_errstr(d.out.result)); ret = false; } return ret; } static bool test_CreateAlias(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *alias_name, struct policy_handle *alias_handle, const struct dom_sid *domain_sid, bool test_alias) { struct samr_CreateDomAlias r; struct lsa_String name; uint32_t rid; bool ret = true; init_lsa_String(&name, alias_name); r.in.domain_handle = domain_handle; r.in.alias_name = &name; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.alias_handle = alias_handle; r.out.rid = &rid; torture_comment(tctx, "Testing CreateAlias (%s)\n", r.in.alias_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r), "CreateDomAlias failed"); if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) { torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.alias_name->string); return true; } else { torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string, nt_errstr(r.out.result)); return false; } } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ALIAS_EXISTS)) { if (!test_DeleteAlias_byname(b, tctx, domain_handle, r.in.alias_name->string)) { return false; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r), "CreateDomAlias failed"); } if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "CreateAlias failed - %s\n", nt_errstr(r.out.result)); return false; } if (!test_alias) { return ret; } if (!test_alias_ops(b, tctx, alias_handle, domain_sid)) { ret = false; } return ret; } static bool test_ChangePassword(struct dcerpc_pipe *p, struct torture_context *tctx, const char *acct_name, struct policy_handle *domain_handle, char **password) { bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; if (!*password) { return false; } if (!test_ChangePasswordUser(b, tctx, acct_name, domain_handle, password)) { ret = false; } if (!test_ChangePasswordUser2(p, tctx, acct_name, password, 0, true)) { ret = false; } if (!test_OemChangePasswordUser2(p, tctx, acct_name, domain_handle, password)) { ret = false; } /* test what happens when setting the old password again */ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, *password, 0, true)) { ret = false; } { char simple_pass[9]; char *v = generate_random_str(tctx, 1); ZERO_STRUCT(simple_pass); memset(simple_pass, *v, sizeof(simple_pass) - 1); /* test what happens when picking a simple password */ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, simple_pass, 0, true)) { ret = false; } } /* set samr_SetDomainInfo level 1 with min_length 5 */ { struct samr_QueryDomainInfo r; union samr_DomainInfo *info = NULL; struct samr_SetDomainInfo s; uint16_t len_old, len; uint32_t pwd_prop_old; int64_t min_pwd_age_old; len = 5; r.in.domain_handle = domain_handle; r.in.level = 1; r.out.info = &info; torture_comment(tctx, "Testing samr_QueryDomainInfo level 1\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), "QueryDomainInfo failed"); if (!NT_STATUS_IS_OK(r.out.result)) { return false; } s.in.domain_handle = domain_handle; s.in.level = 1; s.in.info = info; /* remember the old min length, so we can reset it */ len_old = s.in.info->info1.min_password_length; s.in.info->info1.min_password_length = len; pwd_prop_old = s.in.info->info1.password_properties; /* turn off password complexity checks for this test */ s.in.info->info1.password_properties &= ~DOMAIN_PASSWORD_COMPLEX; min_pwd_age_old = s.in.info->info1.min_password_age; s.in.info->info1.min_password_age = 0; torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), "SetDomainInfo failed"); if (!NT_STATUS_IS_OK(s.out.result)) { return false; } torture_comment(tctx, "calling test_ChangePasswordUser3 with too short password\n"); if (!test_ChangePasswordUser3(p, tctx, acct_name, len - 1, password, NULL, 0, true)) { ret = false; } s.in.info->info1.min_password_length = len_old; s.in.info->info1.password_properties = pwd_prop_old; s.in.info->info1.min_password_age = min_pwd_age_old; torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), "SetDomainInfo failed"); if (!NT_STATUS_IS_OK(s.out.result)) { return false; } } { struct samr_OpenUser r; struct samr_QueryUserInfo q; union samr_UserInfo *info; struct samr_LookupNames n; struct policy_handle user_handle; struct samr_Ids rids, types; n.in.domain_handle = domain_handle; n.in.num_names = 1; n.in.names = talloc_array(tctx, struct lsa_String, 1); n.in.names[0].string = acct_name; n.out.rids = &rids; n.out.types = &types; torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n), "LookupNames failed"); if (!NT_STATUS_IS_OK(n.out.result)) { torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result)); return false; } r.in.domain_handle = domain_handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.in.rid = n.out.rids->ids[0]; r.out.user_handle = &user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), "OpenUser failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", n.out.rids->ids[0], nt_errstr(r.out.result)); return false; } q.in.user_handle = &user_handle; q.in.level = 5; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), "QueryUserInfo failed"); if (!NT_STATUS_IS_OK(q.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo failed - %s\n", nt_errstr(q.out.result)); return false; } torture_comment(tctx, "calling test_ChangePasswordUser3 with too early password change\n"); if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, info->info5.last_password_change, true)) { ret = false; } } /* we change passwords twice - this has the effect of verifying they were changed correctly for the final call */ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, 0, true)) { ret = false; } if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, 0, true)) { ret = false; } if (!test_ChangePasswordUser4(p, tctx, acct_name, 0, password, NULL)) { ret = false; } return ret; } static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *domain_handle, const char *user_name, struct policy_handle *user_handle_out, struct dom_sid *domain_sid, enum torture_samr_choice which_ops, struct cli_credentials *machine_credentials, bool test_user) { TALLOC_CTX *user_ctx; struct samr_CreateUser r; struct samr_QueryUserInfo q; union samr_UserInfo *info; struct samr_DeleteUser d; uint32_t rid; /* This call creates a 'normal' account - check that it really does */ const uint32_t acct_flags = ACB_NORMAL; struct lsa_String name; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; struct policy_handle user_handle; user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); init_lsa_String(&name, user_name); r.in.domain_handle = domain_handle; r.in.account_name = &name; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.user_handle = &user_handle; r.out.rid = &rid; torture_comment(tctx, "Testing CreateUser(%s)\n", r.in.account_name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r), "CreateUser failed"); if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string); return true; } else { torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, nt_errstr(r.out.result)); return false; } } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) { talloc_free(user_ctx); return false; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r), "CreateUser failed"); } if (!NT_STATUS_IS_OK(r.out.result)) { talloc_free(user_ctx); torture_result(tctx, TORTURE_FAIL, "CreateUser failed - %s\n", nt_errstr(r.out.result)); return false; } if (!test_user) { if (user_handle_out) { *user_handle_out = user_handle; } return ret; } { q.in.user_handle = &user_handle; q.in.level = 16; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q), "QueryUserInfo failed"); if (!NT_STATUS_IS_OK(q.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", q.in.level, nt_errstr(q.out.result)); ret = false; } else { if ((info->info16.acct_flags & acct_flags) != acct_flags) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n", info->info16.acct_flags, acct_flags); ret = false; } } if (!test_user_ops(p, tctx, &user_handle, domain_handle, domain_sid, acct_flags, name.string, which_ops, machine_credentials)) { ret = false; } if (user_handle_out) { *user_handle_out = user_handle; } else { torture_comment(tctx, "Testing DeleteUser (createuser test)\n"); d.in.user_handle = &user_handle; d.out.user_handle = &user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d), "DeleteUser failed"); if (!NT_STATUS_IS_OK(d.out.result)) { torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result)); ret = false; } } } talloc_free(user_ctx); return ret; } static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *domain_handle, struct dom_sid *domain_sid, enum torture_samr_choice which_ops, struct cli_credentials *machine_credentials) { struct samr_CreateUser2 r; struct samr_QueryUserInfo q; union samr_UserInfo *info; struct samr_DeleteUser d; struct policy_handle user_handle; uint32_t rid; struct lsa_String name; bool ret = true; int i; struct dcerpc_binding_handle *b = p->binding_handle; struct { uint32_t acct_flags; const char *account_name; NTSTATUS nt_status; } account_types[] = { { ACB_NORMAL, TEST_ACCOUNT_NAME, NT_STATUS_OK }, { ACB_NORMAL | ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, { ACB_NORMAL | ACB_PWNOEXP, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, { ACB_WSTRUST, TEST_MACHINENAME, NT_STATUS_OK }, { ACB_WSTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, { ACB_WSTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, { ACB_SVRTRUST, TEST_MACHINENAME, NT_STATUS_OK }, { ACB_SVRTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, { ACB_SVRTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_ACCESS_DENIED }, { ACB_DOMTRUST | ACB_DISABLED, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, { ACB_DOMTRUST | ACB_PWNOEXP, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, { 0, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, { ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, { 0, NULL, NT_STATUS_INVALID_PARAMETER } }; for (i = 0; account_types[i].account_name; i++) { TALLOC_CTX *user_ctx; uint32_t acct_flags = account_types[i].acct_flags; uint32_t access_granted; user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); init_lsa_String(&name, account_types[i].account_name); r.in.domain_handle = domain_handle; r.in.account_name = &name; r.in.acct_flags = acct_flags; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.user_handle = &user_handle; r.out.access_granted = &access_granted; r.out.rid = &rid; torture_comment(tctx, "Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags); torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r), "CreateUser2 failed"); if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string); continue; } else { torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, nt_errstr(r.out.result)); ret = false; continue; } } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) { talloc_free(user_ctx); ret = false; continue; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r), "CreateUser2 failed"); } if (!NT_STATUS_EQUAL(r.out.result, account_types[i].nt_status)) { torture_result(tctx, TORTURE_FAIL, "CreateUser2 failed gave incorrect error return - %s (should be %s)\n", nt_errstr(r.out.result), nt_errstr(account_types[i].nt_status)); ret = false; } if (NT_STATUS_IS_OK(r.out.result)) { q.in.user_handle = &user_handle; q.in.level = 5; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q), "QueryUserInfo failed"); if (!NT_STATUS_IS_OK(q.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", q.in.level, nt_errstr(q.out.result)); ret = false; } else { uint32_t expected_flags = (acct_flags | ACB_PWNOTREQ | ACB_DISABLED); if (acct_flags == ACB_NORMAL) { expected_flags |= ACB_PW_EXPIRED; } if ((info->info5.acct_flags) != expected_flags) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", info->info5.acct_flags, expected_flags); ret = false; } switch (acct_flags) { case ACB_SVRTRUST: if (info->info5.primary_gid != DOMAIN_RID_DCS) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: DC should have had Primary Group %d, got %d\n", DOMAIN_RID_DCS, info->info5.primary_gid); ret = false; } break; case ACB_WSTRUST: if (info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n", DOMAIN_RID_DOMAIN_MEMBERS, info->info5.primary_gid); ret = false; } break; case ACB_NORMAL: if (info->info5.primary_gid != DOMAIN_RID_USERS) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Users should have had Primary Group %d, got %d\n", DOMAIN_RID_USERS, info->info5.primary_gid); ret = false; } break; } } if (!test_user_ops(p, tctx, &user_handle, domain_handle, domain_sid, acct_flags, name.string, which_ops, machine_credentials)) { ret = false; } if (!ndr_policy_handle_empty(&user_handle)) { torture_comment(tctx, "Testing DeleteUser (createuser2 test)\n"); d.in.user_handle = &user_handle; d.out.user_handle = &user_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d), "DeleteUser failed"); if (!NT_STATUS_IS_OK(d.out.result)) { torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result)); ret = false; } } } talloc_free(user_ctx); } return ret; } static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryAliasInfo r; union samr_AliasInfo *info; uint16_t levels[] = {1, 2, 3}; int i; bool ret = true; for (i=0;istring, "NewName"); } #endif if (s.in.level == 4) { init_lsa_String(&s.in.info->description, "test description"); } torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetGroupInfo_r(b, tctx, &s), "SetGroupInfo failed"); if (set_ok[i]) { if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u failed - %s\n", r.in.level, nt_errstr(s.out.result)); ret = false; continue; } } else { if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", r.in.level, nt_errstr(s.out.result)); ret = false; continue; } } } return ret; } static bool test_QueryUserInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryUserInfo r; union samr_UserInfo *info; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20, 21}; int i; bool ret = true; for (i=0;iinfo16.acct_flags) == 0) { torture_result(tctx, TORTURE_FAIL, "Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n", acct_flag_mask, info->info16.acct_flags, rid); ret = false; } } if (!test_samr_handle_Close(b, tctx, &user_handle)) { ret = false; } return ret; } static bool test_EnumDomainUsers_all(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_EnumDomainUsers r; uint32_t mask, resume_handle=0; int i, mask_idx; bool ret = true; struct samr_LookupNames n; struct samr_LookupRids lr ; struct lsa_Strings names; struct samr_Ids rids, types; struct samr_SamArray *sam = NULL; uint32_t num_entries = 0; uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST, ACB_DISABLED, ACB_NORMAL | ACB_DISABLED, ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST, ACB_PWNOEXP, 0}; torture_comment(tctx, "Testing EnumDomainUsers\n"); for (mask_idx=0;mask_idxcount == 0) { continue; } for (i=0;icount;i++) { if (mask) { if (!check_mask(b, tctx, handle, sam->entries[i].idx, mask)) { ret = false; } } else if (!test_OpenUser(b, tctx, handle, sam->entries[i].idx)) { ret = false; } } } torture_comment(tctx, "Testing LookupNames\n"); n.in.domain_handle = handle; n.in.num_names = sam->count; n.in.names = talloc_array(tctx, struct lsa_String, sam->count); n.out.rids = &rids; n.out.types = &types; for (i=0;icount;i++) { n.in.names[i].string = sam->entries[i].name.string; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n), "LookupNames failed"); if (!NT_STATUS_IS_OK(n.out.result)) { torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result)); ret = false; } torture_comment(tctx, "Testing LookupRids\n"); lr.in.domain_handle = handle; lr.in.num_rids = sam->count; lr.in.rids = talloc_array(tctx, uint32_t, sam->count); lr.out.names = &names; lr.out.types = &types; for (i=0;icount;i++) { lr.in.rids[i] = sam->entries[i].idx; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &lr), "LookupRids failed"); torture_assert_ntstatus_ok(tctx, lr.out.result, "LookupRids"); return ret; } /* try blasting the server with a bunch of sync requests */ static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle) { struct samr_EnumDomainUsers r; uint32_t resume_handle=0; int i; #define ASYNC_COUNT 100 struct tevent_req *req[ASYNC_COUNT]; if (!torture_setting_bool(tctx, "dangerous", false)) { torture_skip(tctx, "samr async test disabled - enable dangerous tests to use\n"); } torture_comment(tctx, "Testing EnumDomainUsers_async\n"); r.in.domain_handle = handle; r.in.resume_handle = &resume_handle; r.in.acct_flags = 0; r.in.max_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; for (i=0;iev, p->binding_handle, &r); } for (i=0;iev); torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r_recv(req[i], tctx), talloc_asprintf(tctx, "EnumDomainUsers[%d] failed - %s\n", i, nt_errstr(r.out.result))); } torture_comment(tctx, "%d async requests OK\n", i); return true; } static bool test_EnumDomainGroups_all(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_EnumDomainGroups r; uint32_t resume_handle=0; struct samr_SamArray *sam = NULL; uint32_t num_entries = 0; int i; bool ret = true; bool universal_group_found = false; torture_comment(tctx, "Testing EnumDomainGroups\n"); r.in.domain_handle = handle; r.in.resume_handle = &resume_handle; r.in.max_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; r.out.num_entries = &num_entries; r.out.sam = &sam; torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), "EnumDomainGroups failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "EnumDomainGroups failed - %s\n", nt_errstr(r.out.result)); return false; } if (!sam) { return false; } for (i=0;icount;i++) { if (!test_OpenGroup(b, tctx, handle, sam->entries[i].idx)) { ret = false; } if ((ret == true) && (strcasecmp(sam->entries[i].name.string, "Enterprise Admins") == 0)) { universal_group_found = true; } } /* when we are running this on s4 we should get back at least the * "Enterprise Admins" universal group. If we don't get a group entry * at all we probably are performing the test on the builtin domain. * So ignore this case. */ if (torture_setting_bool(tctx, "samba4", false)) { if ((sam->count > 0) && (!universal_group_found)) { ret = false; } } return ret; } static bool test_EnumDomainAliases_all(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_EnumDomainAliases r; uint32_t resume_handle=0; struct samr_SamArray *sam = NULL; uint32_t num_entries = 0; int i; bool ret = true; torture_comment(tctx, "Testing EnumDomainAliases\n"); r.in.domain_handle = handle; r.in.resume_handle = &resume_handle; r.in.max_size = (uint32_t)-1; r.out.sam = &sam; r.out.num_entries = &num_entries; r.out.resume_handle = &resume_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), "EnumDomainAliases failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "EnumDomainAliases failed - %s\n", nt_errstr(r.out.result)); return false; } if (!sam) { return false; } for (i=0;icount;i++) { if (!test_OpenAlias(b, tctx, handle, sam->entries[i].idx)) { ret = false; } } return ret; } static bool test_GetDisplayEnumerationIndex(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_GetDisplayEnumerationIndex r; bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; struct lsa_String name; uint32_t idx = 0; int i; for (i=0;iin.domain_handle, .access_mask = SEC_FLAG_MAXIMUM_ALLOWED, }, }; struct samr_QueryUserInfo q; union samr_UserInfo *info; struct policy_handle user_handle; int i, ret = true; for (i = 0; ; i++) { switch (querydisplayinfo->in.level) { case 1: if (i >= querydisplayinfo->out.info->info1.count) { return ret; } r.in.rid = querydisplayinfo->out.info->info1.entries[i].rid; break; case 2: if (i >= querydisplayinfo->out.info->info2.count) { return ret; } r.in.rid = querydisplayinfo->out.info->info2.entries[i].rid; break; case 3: /* Groups */ case 4: case 5: /* Not interested in validating just the account name */ return true; } r.out.user_handle = &user_handle; switch (querydisplayinfo->in.level) { case 1: case 2: torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), "OpenUser failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result)); return false; } } q.in.user_handle = &user_handle; q.in.level = 21; q.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), "QueryUserInfo failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result)); return false; } switch (querydisplayinfo->in.level) { case 1: if (seen_testuser && strcmp(info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) { *seen_testuser = true; } STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].full_name, info->info21.full_name, info->info21.account_name); STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].account_name, info->info21.account_name, info->info21.account_name); STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].description, info->info21.description, info->info21.account_name); INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].rid, info->info21.rid, info->info21.account_name); INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].acct_flags, info->info21.acct_flags, info->info21.account_name); break; case 2: STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].account_name, info->info21.account_name, info->info21.account_name); STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].description, info->info21.description, info->info21.account_name); INT_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].rid, info->info21.rid, info->info21.account_name); INT_EQUAL_QUERY((querydisplayinfo->out.info->info2.entries[i].acct_flags & ~ACB_NORMAL), info->info21.acct_flags, info->info21.account_name); if (!(querydisplayinfo->out.info->info2.entries[i].acct_flags & ACB_NORMAL)) { torture_result(tctx, TORTURE_FAIL, "Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n", info->info21.account_name.string); } if (!(info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) { torture_result(tctx, TORTURE_FAIL, "Found non-trust account %s in trust account listing: 0x%x 0x%x\n", info->info21.account_name.string, querydisplayinfo->out.info->info2.entries[i].acct_flags, info->info21.acct_flags); return false; } break; } if (!test_samr_handle_Close(b, tctx, &user_handle)) { return false; } } return ret; } static bool test_QueryDisplayInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryDisplayInfo r; struct samr_QueryDomainInfo dom_info; union samr_DomainInfo *info = NULL; bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; bool seen_testuser = false; uint32_t total_size; uint32_t returned_size; union samr_DispInfo disp_info; for (i=0;iinfo1.count; break; case 2: if (!test_each_DisplayInfo_user(b, tctx, &r, NULL)) { ret = false; } r.in.start_idx += r.out.info->info2.count; break; case 3: r.in.start_idx += r.out.info->info3.count; break; case 4: r.in.start_idx += r.out.info->info4.count; break; case 5: r.in.start_idx += r.out.info->info5.count; break; } } dom_info.in.domain_handle = handle; dom_info.in.level = 2; dom_info.out.info = &info; /* Check number of users returned is correct */ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &dom_info), "QueryDomainInfo failed"); if (!NT_STATUS_IS_OK(dom_info.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n", r.in.level, nt_errstr(dom_info.out.result)); ret = false; break; } switch (r.in.level) { case 1: case 4: if (info->general.num_users < r.in.start_idx) { /* On AD deployments this numbers don't match * since QueryDisplayInfo returns universal and * global groups, QueryDomainInfo only global * ones. */ if (torture_setting_bool(tctx, "samba3", false)) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n", r.in.start_idx, info->general.num_groups, info->general.domain_name.string); ret = false; } } if (!seen_testuser) { struct policy_handle user_handle; if (NT_STATUS_IS_OK(test_OpenUser_byname(b, tctx, handle, TEST_ACCOUNT_NAME, &user_handle))) { torture_result(tctx, TORTURE_FAIL, "Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n", info->general.domain_name.string); ret = false; test_samr_handle_Close(b, tctx, &user_handle); } } break; case 3: case 5: if (info->general.num_groups != r.in.start_idx) { /* On AD deployments this numbers don't match * since QueryDisplayInfo returns universal and * global groups, QueryDomainInfo only global * ones. */ if (torture_setting_bool(tctx, "samba3", false)) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n", r.in.start_idx, info->general.num_groups, info->general.domain_name.string); ret = false; } } break; } } return ret; } static bool test_QueryDisplayInfo2(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryDisplayInfo2 r; bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; uint32_t total_size; uint32_t returned_size; union samr_DispInfo info; for (i=0;iinfo1.entries[0].idx != r.in.start_idx + 1) { torture_result(tctx, TORTURE_FAIL, "expected idx %d but got %d\n", r.in.start_idx + 1, r.out.info->info1.entries[0].idx); break; } } if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level %u failed - %s\n", r.in.level, nt_errstr(r.out.result)); ret = false; break; } r.in.start_idx++; } while ((NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) || NT_STATUS_IS_OK(r.out.result)) && *r.out.returned_size != 0); return ret; } static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryDomainInfo r; union samr_DomainInfo *info = NULL; struct samr_SetDomainInfo s; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; uint16_t set_ok[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; int i; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; const char *domain_comment = talloc_asprintf(tctx, "Tortured by Samba4 RPC-SAMR: %s", timestring(tctx, time(NULL))); s.in.domain_handle = handle; s.in.level = 4; s.in.info = talloc(tctx, union samr_DomainInfo); s.in.info->oem.oem_information.string = domain_comment; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), "SetDomainInfo failed"); if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u (set comment) failed - %s\n", s.in.level, nt_errstr(s.out.result)); return false; } for (i=0;igeneral.oem_information.string, domain_comment) != 0) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", levels[i], info->general.oem_information.string, domain_comment); if (!torture_setting_bool(tctx, "samba3", false)) { ret = false; } } if (!info->general.primary.string) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n", levels[i]); ret = false; } else if (info->general.role == SAMR_ROLE_DOMAIN_PDC) { if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), info->general.primary.string) != 0) { if (torture_setting_bool(tctx, "samba3", false)) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n", levels[i], info->general.primary.string, dcerpc_server_name(p)); } } } break; case 4: if (strcmp(info->oem.oem_information.string, domain_comment) != 0) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", levels[i], info->oem.oem_information.string, domain_comment); if (!torture_setting_bool(tctx, "samba3", false)) { ret = false; } } break; case 6: if (!info->info6.primary.string) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n", levels[i]); ret = false; } break; case 11: if (strcmp(info->general2.general.oem_information.string, domain_comment) != 0) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different comment (%s, expected %s)\n", levels[i], info->general2.general.oem_information.string, domain_comment); if (!torture_setting_bool(tctx, "samba3", false)) { ret = false; } } break; } torture_comment(tctx, "Testing SetDomainInfo level %u\n", levels[i]); s.in.domain_handle = handle; s.in.level = levels[i]; s.in.info = info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), "SetDomainInfo failed"); if (set_ok[i]) { if (!NT_STATUS_IS_OK(s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u failed - %s\n", r.in.level, nt_errstr(s.out.result)); ret = false; continue; } } else { if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) { torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", r.in.level, nt_errstr(s.out.result)); ret = false; continue; } } torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), "QueryDomainInfo failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n", r.in.level, nt_errstr(r.out.result)); ret = false; continue; } } return ret; } static bool test_QueryDomainInfo2(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_QueryDomainInfo2 r; union samr_DomainInfo *info = NULL; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; int i; bool ret = true; for (i=0;ientries[i].name.string, &names, &num_names); } } torture_assert_ntstatus_ok(tctx, status, "EnumDomainGroups"); torture_assert(tctx, sam, "EnumDomainGroups failed to return sam"); if (builtin_domain) { torture_assert(tctx, num_names == 0, "EnumDomainGroups shouldn't return any group in the builtin domain!"); } q2.in.domain_handle = handle; q2.in.level = 5; q2.in.start_idx = 0; q2.in.max_entries = 5; q2.in.buf_size = (uint32_t)-1; q2.out.total_size = &total_size; q2.out.returned_size = &returned_size; q2.out.info = &info; status = STATUS_MORE_ENTRIES; while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &q2), "QueryDisplayInfo failed"); status = q2.out.result; if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; for (i=0; iinfo5.count; i++) { int j; const char *name = q2.out.info->info5.entries[i].account_name.string; bool found = false; for (j=0; jinfo5.count; } if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level 5 failed - %s\n", nt_errstr(status)); ret = false; } if (builtin_domain) { torture_assert(tctx, q2.in.start_idx != 0, "QueryDisplayInfo should return all domain groups also on the builtin domain handle!"); } for (i=0; icount; i++) { if (rids->rids[i] == rid) { found_member = true; } } torture_assert(tctx, found_member, "QueryGroupMember did not list newly added member"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteGroupMember_r(b, tctx, &d), "DeleteGroupMember failed"); torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteGroupMember"); rids = NULL; found_member = false; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &q), "QueryGroupMember failed"); torture_assert_ntstatus_ok(tctx, q.out.result, "QueryGroupMember"); torture_assert(tctx, rids, "QueryGroupMember did not fill in rids structure"); for (i=0; i < rids->count; i++) { if (rids->rids[i] == rid) { found_member = true; } } torture_assert(tctx, !found_member, "QueryGroupMember does still list removed member"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r), "AddGroupMember failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "AddGroupMember"); return true; } static bool test_CreateDomainGroup(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, const char *group_name, struct policy_handle *group_handle, struct dom_sid *domain_sid, bool test_group) { struct samr_CreateDomainGroup r; uint32_t rid; struct lsa_String name; bool ret = true; init_lsa_String(&name, group_name); r.in.domain_handle = domain_handle; r.in.name = &name; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.group_handle = group_handle; r.out.rid = &rid; torture_comment(tctx, "Testing CreateDomainGroup(%s)\n", r.in.name->string); torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), "CreateDomainGroup failed"); if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) { torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.name->string); return true; } else { torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.name->string, nt_errstr(r.out.result)); return false; } } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_GROUP_EXISTS)) { if (!test_DeleteGroup_byname(b, tctx, domain_handle, r.in.name->string)) { torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string, nt_errstr(r.out.result)); return false; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), "CreateDomainGroup failed"); } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.name->string)) { torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string, nt_errstr(r.out.result)); return false; } torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), "CreateDomainGroup failed"); } torture_assert_ntstatus_ok(tctx, r.out.result, "CreateDomainGroup"); if (!test_group) { return ret; } if (!test_AddGroupMember(b, tctx, domain_handle, group_handle)) { torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed - %s\n", nt_errstr(r.out.result)); ret = false; } if (!test_SetGroupInfo(b, tctx, group_handle)) { ret = false; } return ret; } /* its not totally clear what this does. It seems to accept any sid you like. */ static bool test_RemoveMemberFromForeignDomain(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle) { struct samr_RemoveMemberFromForeignDomain r; r.in.domain_handle = domain_handle; r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32-12-34-56-78"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMemberFromForeignDomain_r(b, tctx, &r), "RemoveMemberFromForeignDomain failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMemberFromForeignDomain"); return true; } static bool test_EnumDomainUsers(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, uint32_t *total_num_entries_p) { NTSTATUS status; struct samr_EnumDomainUsers r; uint32_t resume_handle = 0; uint32_t num_entries = 0; uint32_t total_num_entries = 0; struct samr_SamArray *sam; r.in.domain_handle = domain_handle; r.in.acct_flags = 0; r.in.max_size = (uint32_t)-1; r.in.resume_handle = &resume_handle; r.out.sam = &sam; r.out.num_entries = &num_entries; r.out.resume_handle = &resume_handle; torture_comment(tctx, "Testing EnumDomainUsers\n"); do { torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r(b, tctx, &r), "EnumDomainUsers failed"); if (NT_STATUS_IS_ERR(r.out.result)) { torture_assert_ntstatus_ok(tctx, r.out.result, "failed to enumerate users"); } status = r.out.result; total_num_entries += num_entries; } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); if (total_num_entries_p) { *total_num_entries_p = total_num_entries; } return true; } static bool test_EnumDomainGroups(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, uint32_t *total_num_entries_p) { NTSTATUS status; struct samr_EnumDomainGroups r; uint32_t resume_handle = 0; uint32_t num_entries = 0; uint32_t total_num_entries = 0; struct samr_SamArray *sam; r.in.domain_handle = domain_handle; r.in.max_size = (uint32_t)-1; r.in.resume_handle = &resume_handle; r.out.sam = &sam; r.out.num_entries = &num_entries; r.out.resume_handle = &resume_handle; torture_comment(tctx, "Testing EnumDomainGroups\n"); do { torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), "EnumDomainGroups failed"); if (NT_STATUS_IS_ERR(r.out.result)) { torture_assert_ntstatus_ok(tctx, r.out.result, "failed to enumerate groups"); } status = r.out.result; total_num_entries += num_entries; } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); if (total_num_entries_p) { *total_num_entries_p = total_num_entries; } return true; } static bool test_EnumDomainAliases(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *domain_handle, uint32_t *total_num_entries_p) { NTSTATUS status; struct samr_EnumDomainAliases r; uint32_t resume_handle = 0; uint32_t num_entries = 0; uint32_t total_num_entries = 0; struct samr_SamArray *sam; r.in.domain_handle = domain_handle; r.in.max_size = (uint32_t)-1; r.in.resume_handle = &resume_handle; r.out.sam = &sam; r.out.num_entries = &num_entries; r.out.resume_handle = &resume_handle; torture_comment(tctx, "Testing EnumDomainAliases\n"); do { torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), "EnumDomainAliases failed"); if (NT_STATUS_IS_ERR(r.out.result)) { torture_assert_ntstatus_ok(tctx, r.out.result, "failed to enumerate aliases"); } status = r.out.result; total_num_entries += num_entries; } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); if (total_num_entries_p) { *total_num_entries_p = total_num_entries; } return true; } static bool test_QueryDisplayInfo_level(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle, uint16_t level, uint32_t *total_num_entries_p) { NTSTATUS status; struct samr_QueryDisplayInfo r; uint32_t total_num_entries = 0; r.in.domain_handle = handle; r.in.level = level; r.in.start_idx = 0; r.in.max_entries = (uint32_t)-1; r.in.buf_size = (uint32_t)-1; torture_comment(tctx, "Testing QueryDisplayInfo\n"); do { uint32_t total_size; uint32_t returned_size; union samr_DispInfo info; r.out.total_size = &total_size; r.out.returned_size = &returned_size; r.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r), "failed to query displayinfo"); if (NT_STATUS_IS_ERR(r.out.result)) { torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query displayinfo"); } status = r.out.result; if (*r.out.returned_size == 0) { break; } switch (r.in.level) { case 1: total_num_entries += info.info1.count; r.in.start_idx += info.info1.entries[info.info1.count - 1].idx + 1; break; case 2: total_num_entries += info.info2.count; r.in.start_idx += info.info2.entries[info.info2.count - 1].idx + 1; break; case 3: total_num_entries += info.info3.count; r.in.start_idx += info.info3.entries[info.info3.count - 1].idx + 1; break; case 4: total_num_entries += info.info4.count; r.in.start_idx += info.info4.entries[info.info4.count - 1].idx + 1; break; case 5: total_num_entries += info.info5.count; r.in.start_idx += info.info5.entries[info.info5.count - 1].idx + 1; break; default: return false; } } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); if (total_num_entries_p) { *total_num_entries_p = total_num_entries; } return true; } static bool test_ManyObjects(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *domain_handle, struct dom_sid *domain_sid, struct torture_samr_context *ctx) { uint32_t num_total = ctx->num_objects_large_dc; uint32_t num_enum = 0; uint32_t num_disp = 0; uint32_t num_created = 0; uint32_t num_anounced = 0; uint32_t i; struct dcerpc_binding_handle *b = p->binding_handle; struct policy_handle *handles = talloc_zero_array(tctx, struct policy_handle, num_total); /* query */ { struct samr_QueryDomainInfo2 r; union samr_DomainInfo *info; r.in.domain_handle = domain_handle; r.in.level = 2; r.out.info = &info; torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r), "QueryDomainInfo2 failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "failed to query domain info"); switch (ctx->choice) { case TORTURE_SAMR_MANY_ACCOUNTS: num_anounced = info->general.num_users; break; case TORTURE_SAMR_MANY_GROUPS: num_anounced = info->general.num_groups; break; case TORTURE_SAMR_MANY_ALIASES: num_anounced = info->general.num_aliases; break; default: return false; } } /* create */ for (i=0; i < num_total; i++) { const char *name = NULL; switch (ctx->choice) { case TORTURE_SAMR_MANY_ACCOUNTS: name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i); torture_assert(tctx, test_CreateUser(p, tctx, domain_handle, name, &handles[i], domain_sid, 0, NULL, false), "failed to create user"); break; case TORTURE_SAMR_MANY_GROUPS: name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i); torture_assert(tctx, test_CreateDomainGroup(b, tctx, domain_handle, name, &handles[i], domain_sid, false), "failed to create group"); break; case TORTURE_SAMR_MANY_ALIASES: name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i); torture_assert(tctx, test_CreateAlias(b, tctx, domain_handle, name, &handles[i], domain_sid, false), "failed to create alias"); break; default: return false; } if (!ndr_policy_handle_empty(&handles[i])) { num_created++; } } /* enum */ switch (ctx->choice) { case TORTURE_SAMR_MANY_ACCOUNTS: torture_assert(tctx, test_EnumDomainUsers(b, tctx, domain_handle, &num_enum), "failed to enum users"); break; case TORTURE_SAMR_MANY_GROUPS: torture_assert(tctx, test_EnumDomainGroups(b, tctx, domain_handle, &num_enum), "failed to enum groups"); break; case TORTURE_SAMR_MANY_ALIASES: torture_assert(tctx, test_EnumDomainAliases(b, tctx, domain_handle, &num_enum), "failed to enum aliases"); break; default: return false; } /* dispinfo */ switch (ctx->choice) { case TORTURE_SAMR_MANY_ACCOUNTS: torture_assert(tctx, test_QueryDisplayInfo_level(b, tctx, domain_handle, 1, &num_disp), "failed to query display info"); break; case TORTURE_SAMR_MANY_GROUPS: torture_assert(tctx, test_QueryDisplayInfo_level(b, tctx, domain_handle, 3, &num_disp), "failed to query display info"); break; case TORTURE_SAMR_MANY_ALIASES: /* no aliases in dispinfo */ break; default: return false; } /* close or delete */ for (i=0; i < num_total; i++) { if (ndr_policy_handle_empty(&handles[i])) { continue; } if (torture_setting_bool(tctx, "samba3", false)) { torture_assert(tctx, test_samr_handle_Close(b, tctx, &handles[i]), "failed to close handle"); } else { switch (ctx->choice) { case TORTURE_SAMR_MANY_ACCOUNTS: torture_assert(tctx, test_DeleteUser(b, tctx, &handles[i]), "failed to delete user"); break; case TORTURE_SAMR_MANY_GROUPS: torture_assert(tctx, test_DeleteDomainGroup(b, tctx, &handles[i]), "failed to delete group"); break; case TORTURE_SAMR_MANY_ALIASES: torture_assert(tctx, test_DeleteAlias(b, tctx, &handles[i]), "failed to delete alias"); break; default: return false; } } } talloc_free(handles); if (ctx->choice == TORTURE_SAMR_MANY_ACCOUNTS && num_enum != num_anounced + num_created) { torture_comment(tctx, "unexpected number of results (%u) returned in enum call, expected %u\n", num_enum, num_anounced + num_created); torture_comment(tctx, "unexpected number of results (%u) returned in dispinfo, call, expected %u\n", num_disp, num_anounced + num_created); } return true; } static bool test_Connect(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle); static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, struct torture_samr_context *ctx, struct dom_sid *sid) { struct samr_OpenDomain r; struct policy_handle domain_handle; struct policy_handle alias_handle; struct policy_handle user_handle; struct policy_handle group_handle; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; ZERO_STRUCT(alias_handle); ZERO_STRUCT(user_handle); ZERO_STRUCT(group_handle); ZERO_STRUCT(domain_handle); torture_comment(tctx, "Testing OpenDomain of %s\n", dom_sid_string(tctx, sid)); r.in.connect_handle = &ctx->handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.in.sid = sid; r.out.domain_handle = &domain_handle; torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r), "OpenDomain failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "OpenDomain failed"); /* run the domain tests with the main handle closed - this tests the servers reference counting */ torture_assert(tctx, test_samr_handle_Close(b, tctx, &ctx->handle), "Failed to close SAMR handle"); switch (ctx->choice) { case TORTURE_SAMR_PASSWORDS: case TORTURE_SAMR_USER_PRIVILEGES: if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL); } ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); if (!ret) { torture_result(tctx, TORTURE_FAIL, "Testing PASSWORDS or PRIVILEGES on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_USER_ATTRIBUTES: if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL); } ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); /* This test needs 'complex' users to validate */ ret &= test_QueryDisplayInfo(b, tctx, &domain_handle); if (!ret) { torture_result(tctx, TORTURE_FAIL, "Testing ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_PASSWORDS_PWDLASTSET: case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: case TORTURE_SAMR_PASSWORDS_LOCKOUT: 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_result(tctx, TORTURE_FAIL, "Testing PASSWORDS PWDLASTSET or BADPWDCOUNT on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_MANY_ACCOUNTS: case TORTURE_SAMR_MANY_GROUPS: case TORTURE_SAMR_MANY_ALIASES: ret &= test_ManyObjects(p, tctx, &domain_handle, sid, ctx); if (!ret) { torture_result(tctx, TORTURE_FAIL, "Testing MANY-{ACCOUNTS,GROUPS,ALIASES} on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_OTHER: ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); if (!ret) { torture_result(tctx, TORTURE_FAIL, "Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid)); } if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_QuerySecurity(b, tctx, &domain_handle); } ret &= test_RemoveMemberFromForeignDomain(b, tctx, &domain_handle); ret &= test_CreateAlias(b, tctx, &domain_handle, TEST_ALIASNAME, &alias_handle, sid, true); ret &= test_CreateDomainGroup(b, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true); ret &= test_GetAliasMembership(b, tctx, &domain_handle); ret &= test_QueryDomainInfo(p, tctx, &domain_handle); ret &= test_QueryDomainInfo2(b, tctx, &domain_handle); ret &= test_EnumDomainUsers_all(b, tctx, &domain_handle); ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle); ret &= test_EnumDomainGroups_all(b, tctx, &domain_handle); ret &= test_EnumDomainAliases_all(b, tctx, &domain_handle); ret &= test_QueryDisplayInfo2(b, tctx, &domain_handle); ret &= test_QueryDisplayInfo3(b, tctx, &domain_handle); ret &= test_QueryDisplayInfo_continue(b, tctx, &domain_handle); if (torture_setting_bool(tctx, "samba4", false)) { torture_comment(tctx, "skipping GetDisplayEnumerationIndex test against Samba4\n"); } else { ret &= test_GetDisplayEnumerationIndex(b, tctx, &domain_handle); ret &= test_GetDisplayEnumerationIndex2(b, tctx, &domain_handle); } ret &= test_GroupList(b, tctx, sid, &domain_handle); ret &= test_TestPrivateFunctionsDomain(b, tctx, &domain_handle); ret &= test_RidToSid(b, tctx, sid, &domain_handle); ret &= test_GetBootKeyInformation(b, tctx, &domain_handle); if (!ret) { torture_comment(tctx, "Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; } if (!ndr_policy_handle_empty(&user_handle) && !test_DeleteUser(b, tctx, &user_handle)) { ret = false; } if (!ndr_policy_handle_empty(&alias_handle) && !test_DeleteAlias(b, tctx, &alias_handle)) { ret = false; } if (!ndr_policy_handle_empty(&group_handle) && !test_DeleteDomainGroup(b, tctx, &group_handle)) { ret = false; } torture_assert(tctx, test_samr_handle_Close(b, tctx, &domain_handle), "Failed to close SAMR domain handle"); torture_assert(tctx, test_Connect(b, tctx, &ctx->handle), "Failed to re-connect SAMR handle"); /* reconnect the main handle */ if (!ret) { torture_result(tctx, TORTURE_FAIL, "Testing domain %s failed!\n", dom_sid_string(tctx, sid)); } return ret; } static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tctx, struct torture_samr_context *ctx, const char *domain) { struct samr_LookupDomain r; struct dom_sid2 *sid = NULL; struct lsa_String n1; struct lsa_String n2; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; torture_comment(tctx, "Testing LookupDomain(%s)\n", domain); /* check for correct error codes */ r.in.connect_handle = &ctx->handle; r.in.domain_name = &n2; r.out.sid = &sid; n2.string = NULL; torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), "LookupDomain failed"); torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER, r.out.result, "LookupDomain expected NT_STATUS_INVALID_PARAMETER"); init_lsa_String(&n2, "xxNODOMAINxx"); torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), "LookupDomain failed"); torture_assert_ntstatus_equal(tctx, NT_STATUS_NO_SUCH_DOMAIN, r.out.result, "LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN"); r.in.connect_handle = &ctx->handle; init_lsa_String(&n1, domain); r.in.domain_name = &n1; torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), "LookupDomain failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "LookupDomain"); if (!test_GetDomPwInfo(p, tctx, &n1)) { ret = false; } if (!test_OpenDomain(p, tctx, ctx, *r.out.sid)) { ret = false; } return ret; } static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx, struct torture_samr_context *ctx) { struct samr_EnumDomains r; uint32_t resume_handle = 0; uint32_t num_entries = 0; struct samr_SamArray *sam = NULL; int i; bool ret = true; struct dcerpc_binding_handle *b = p->binding_handle; r.in.connect_handle = &ctx->handle; r.in.resume_handle = &resume_handle; r.in.buf_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; r.out.num_entries = &num_entries; r.out.sam = &sam; torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), "EnumDomains failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed"); if (!*r.out.sam) { return false; } for (i=0;icount;i++) { if (!test_LookupDomain(p, tctx, ctx, sam->entries[i].name.string)) { ret = false; } } torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), "EnumDomains failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed"); return ret; } static bool test_Connect(struct dcerpc_binding_handle *b, struct torture_context *tctx, struct policy_handle *handle) { struct samr_Connect r; struct samr_Connect2 r2; struct samr_Connect3 r3; struct samr_Connect4 r4; struct samr_Connect5 r5; union samr_ConnectInfo info; struct policy_handle h; uint32_t level_out = 0; bool ret = true, got_handle = false; torture_comment(tctx, "Testing samr_Connect\n"); r.in.system_name = NULL; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.connect_handle = &h; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect_r(b, tctx, &r), "Connect failed"); if (!NT_STATUS_IS_OK(r.out.result)) { torture_comment(tctx, "Connect failed - %s\n", nt_errstr(r.out.result)); ret = false; } else { got_handle = true; *handle = h; } torture_comment(tctx, "Testing samr_Connect2\n"); r2.in.system_name = NULL; r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r2.out.connect_handle = &h; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect2_r(b, tctx, &r2), "Connect2 failed"); if (!NT_STATUS_IS_OK(r2.out.result)) { torture_comment(tctx, "Connect2 failed - %s\n", nt_errstr(r2.out.result)); ret = false; } else { if (got_handle) { test_samr_handle_Close(b, tctx, handle); } got_handle = true; *handle = h; } torture_comment(tctx, "Testing samr_Connect3\n"); r3.in.system_name = NULL; r3.in.unknown = 0; r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r3.out.connect_handle = &h; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect3_r(b, tctx, &r3), "Connect3 failed"); if (!NT_STATUS_IS_OK(r3.out.result)) { torture_result(tctx, TORTURE_FAIL, "Connect3 failed - %s\n", nt_errstr(r3.out.result)); ret = false; } else { if (got_handle) { test_samr_handle_Close(b, tctx, handle); } got_handle = true; *handle = h; } torture_comment(tctx, "Testing samr_Connect4\n"); r4.in.system_name = ""; r4.in.client_version = 0; r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r4.out.connect_handle = &h; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect4_r(b, tctx, &r4), "Connect4 failed"); if (!NT_STATUS_IS_OK(r4.out.result)) { torture_result(tctx, TORTURE_FAIL, "Connect4 failed - %s\n", nt_errstr(r4.out.result)); ret = false; } else { if (got_handle) { test_samr_handle_Close(b, tctx, handle); } got_handle = true; *handle = h; } torture_comment(tctx, "Testing samr_Connect5\n"); info.info1.client_version = 0; info.info1.supported_features = 0; r5.in.system_name = ""; r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r5.in.level_in = 1; r5.out.level_out = &level_out; r5.in.info_in = &info; r5.out.info_out = &info; r5.out.connect_handle = &h; torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect5_r(b, tctx, &r5), "Connect5 failed"); if (!NT_STATUS_IS_OK(r5.out.result)) { torture_result(tctx, TORTURE_FAIL, "Connect5 failed - %s\n", nt_errstr(r5.out.result)); ret = false; } else { if (got_handle) { test_samr_handle_Close(b, tctx, handle); } got_handle = true; *handle = h; } return ret; } static bool test_samr_ValidatePassword(struct torture_context *tctx, struct dcerpc_pipe *p) { struct samr_ValidatePassword r; union samr_ValidatePasswordReq req; union samr_ValidatePasswordRep *repp = NULL; NTSTATUS status; const char *passwords[] = { "penguin", "p@ssw0rd", "p@ssw0rd123$", NULL }; int i; struct dcerpc_binding_handle *b = p->binding_handle; torture_comment(tctx, "Testing samr_ValidatePassword\n"); if (dcerpc_binding_handle_get_transport(b) != NCACN_IP_TCP) { torture_comment(tctx, "samr_ValidatePassword only should succeed over NCACN_IP_TCP!\n"); } ZERO_STRUCT(r); r.in.level = NetValidatePasswordReset; r.in.req = &req; r.out.rep = &repp; ZERO_STRUCT(req); req.req3.account.string = "non-existent-account-aklsdji"; for (i=0; passwords[i]; i++) { req.req3.password.string = passwords[i]; status = dcerpc_samr_ValidatePassword_r(b, tctx, &r); if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { torture_skip(tctx, "ValidatePassword not supported by server\n"); } torture_assert_ntstatus_ok(tctx, status, "samr_ValidatePassword failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "samr_ValidatePassword failed"); torture_comment(tctx, "Server %s password '%s' with code %i\n", repp->ctr3.status==SAMR_VALIDATION_STATUS_SUCCESS?"allowed":"refused", req.req3.password.string, repp->ctr3.status); } return true; } bool torture_rpc_samr(struct torture_context *torture) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_OTHER; ret &= test_Connect(b, torture, &ctx->handle); if (!torture_setting_bool(torture, "samba3", false)) { ret &= test_QuerySecurity(b, torture, &ctx->handle); } ret &= test_EnumDomains(p, torture, ctx); ret &= test_SetDsrmPassword(b, torture, &ctx->handle); ret &= test_Shutdown(b, torture, &ctx->handle); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } bool torture_rpc_samr_users(struct torture_context *torture) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_USER_ATTRIBUTES; ret &= test_Connect(b, torture, &ctx->handle); if (!torture_setting_bool(torture, "samba3", false)) { ret &= test_QuerySecurity(b, torture, &ctx->handle); } ret &= test_EnumDomains(p, torture, ctx); ret &= test_SetDsrmPassword(b, torture, &ctx->handle); ret &= test_Shutdown(b, torture, &ctx->handle); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } bool torture_rpc_samr_passwords(struct torture_context *torture) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_PASSWORDS; ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } static bool torture_rpc_samr_pwdlastset(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; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_PASSWORDS_PWDLASTSET; ctx->machine_credentials = machine_credentials; ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } struct torture_suite *torture_rpc_samr_passwords_pwdlastset(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.pwdlastset"); 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, "pwdLastSet", torture_rpc_samr_pwdlastset); return suite; } static bool torture_rpc_samr_users_privileges_delete_user(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; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_USER_PRIVILEGES; ctx->machine_credentials = machine_credentials; ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } struct torture_suite *torture_rpc_samr_user_privileges(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.users.privileges"); 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, "delete_privileged_user", torture_rpc_samr_users_privileges_delete_user); return suite; } static bool torture_rpc_samr_many_accounts(struct torture_context *torture, struct dcerpc_pipe *p2, void *data) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx = talloc_get_type_abort(data, struct torture_samr_context); struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx->choice = TORTURE_SAMR_MANY_ACCOUNTS; ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", ctx->num_objects_large_dc); ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } static bool torture_rpc_samr_many_groups(struct torture_context *torture, struct dcerpc_pipe *p2, void *data) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx = talloc_get_type_abort(data, struct torture_samr_context); struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx->choice = TORTURE_SAMR_MANY_GROUPS; ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", ctx->num_objects_large_dc); ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } static bool torture_rpc_samr_many_aliases(struct torture_context *torture, struct dcerpc_pipe *p2, void *data) { NTSTATUS status; struct dcerpc_pipe *p; bool ret = true; struct torture_samr_context *ctx = talloc_get_type_abort(data, struct torture_samr_context); struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx->choice = TORTURE_SAMR_MANY_ALIASES; ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", ctx->num_objects_large_dc); ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.large-dc"); struct torture_rpc_tcase *tcase; struct torture_samr_context *ctx; tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", &ndr_table_samr); ctx = talloc_zero(suite, struct torture_samr_context); ctx->num_objects_large_dc = 150; torture_rpc_tcase_add_test_ex(tcase, "many_aliases", torture_rpc_samr_many_aliases, ctx); torture_rpc_tcase_add_test_ex(tcase, "many_groups", torture_rpc_samr_many_groups, ctx); torture_rpc_tcase_add_test_ex(tcase, "many_accounts", torture_rpc_samr_many_accounts, 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; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_PASSWORDS_BADPWDCOUNT; ctx->machine_credentials = machine_credentials; ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, 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; } static bool torture_rpc_samr_lockout(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; struct dcerpc_binding_handle *b; status = torture_rpc_connection(torture, &p, &ndr_table_samr); if (!NT_STATUS_IS_OK(status)) { return false; } b = p->binding_handle; ctx = talloc_zero(torture, struct torture_samr_context); ctx->choice = TORTURE_SAMR_PASSWORDS_LOCKOUT; ctx->machine_credentials = machine_credentials; ret &= test_Connect(b, torture, &ctx->handle); ret &= test_EnumDomains(p, torture, ctx); ret &= test_samr_handle_Close(b, torture, &ctx->handle); return ret; } struct torture_suite *torture_rpc_samr_passwords_lockout(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.lockout"); 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, "lockout", torture_rpc_samr_lockout); return suite; } struct torture_suite *torture_rpc_samr_passwords_validate(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.validate"); struct torture_rpc_tcase *tcase; tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", &ndr_table_samr); torture_rpc_tcase_add_test(tcase, "validate", test_samr_ValidatePassword); return suite; }