mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
1666d1d74d
This will make it easier to catch all places where we need to implement the logic for netr_ServerAuthenticateKerberos... BUG: https://bugzilla.samba.org/show_bug.cgi?id=15425 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
9470 lines
280 KiB
C
9470 lines
280 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
test suite for samr rpc operations
|
|
|
|
Copyright (C) Andrew Tridgell 2003
|
|
Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "torture/torture.h"
|
|
#include <tevent.h>
|
|
#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 <gnutls/gnutls.h>
|
|
#include <gnutls/crypto.h>
|
|
|
|
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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing SetAliasInfo level %u\n", levels[i]);
|
|
|
|
r.in.alias_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.info = talloc(tctx, union samr_AliasInfo);
|
|
switch (r.in.level) {
|
|
case ALIASINFONAME: init_lsa_String(&r.in.info->name,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; i<ARRAY_SIZE(levels); i++) {
|
|
|
|
struct samr_QueryUserInfo r;
|
|
struct samr_QueryUserInfo2 r2;
|
|
union samr_UserInfo *info;
|
|
|
|
if (use_info2) {
|
|
r2.in.user_handle = handle;
|
|
r2.in.level = levels[i];
|
|
r2.out.info = &info;
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo2_r(b, tctx, &r2),
|
|
"QueryUserInfo2 failed");
|
|
status = r2.out.result;
|
|
|
|
} else {
|
|
r.in.user_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
|
|
"QueryUserInfo failed");
|
|
status = r.out.result;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryUserInfo%s level %u failed - %s\n",
|
|
use_info2 ? "2":"", levels[i], nt_errstr(status));
|
|
return false;
|
|
}
|
|
|
|
switch (levels[i]) {
|
|
case 3:
|
|
/* pwdlastset3 = info->info3.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;
|
|
enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
|
|
enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
|
|
status = netlogon_creds_encrypt_samlogon_logon(creds,
|
|
r.in.logon_level,
|
|
r.in.logon,
|
|
auth_type,
|
|
auth_level);
|
|
torture_assert_ntstatus_ok(tctx, status, "encrypt_samlogon_logon");
|
|
|
|
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<ARRAY_SIZE(levels); l++) {
|
|
#else
|
|
for (l=0; l<(ARRAY_SIZE(levels))/2; l++) {
|
|
#endif
|
|
for (z=0; z<ARRAY_SIZE(nonzeros); z++) {
|
|
for (f=0; f<ARRAY_SIZE(fields_present); f++) {
|
|
#ifdef TEST_SET_LEVELS
|
|
for (s=0; s<ARRAY_SIZE(set_levels); s++) {
|
|
#endif
|
|
#ifdef TEST_QUERY_LEVELS
|
|
for (q=0; q<ARRAY_SIZE(query_levels); q++) {
|
|
#endif
|
|
NTTIME pwdlastset_old = 0;
|
|
NTTIME pwdlastset_new = 0;
|
|
bool matched_expected_error = false;
|
|
NTSTATUS expected_samlogon_result = NT_STATUS_ACCOUNT_DISABLED;
|
|
|
|
torture_comment(tctx, "------------------------------\n"
|
|
"Testing pwdLastSet attribute for flags: 0x%08x "
|
|
"(s: %d (l: %d), q: %d)\n",
|
|
acct_flags, s, levels[l], q);
|
|
|
|
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))) {
|
|
expected_samlogon_result = NT_STATUS_WRONG_PASSWORD;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
/* set #1 */
|
|
|
|
/* 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;
|
|
}
|
|
|
|
if (matched_expected_error == true) {
|
|
/* skipping on expected failure */
|
|
continue;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
break;
|
|
default:
|
|
if (pwdlastset_new != 0) {
|
|
torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
|
|
"expected pwdLastSet 0 but 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;
|
|
}
|
|
|
|
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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryAliasInfo level %u\n", levels[i]);
|
|
|
|
r.in.alias_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryAliasInfo_r(b, tctx, &r),
|
|
"QueryAliasInfo failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryAliasInfo level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_QueryGroupInfo(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryGroupInfo r;
|
|
union samr_GroupInfo *info;
|
|
uint16_t levels[] = {1, 2, 3, 4, 5};
|
|
int i;
|
|
bool ret = true;
|
|
|
|
for (i=0;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryGroupInfo level %u\n", levels[i]);
|
|
|
|
r.in.group_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupInfo_r(b, tctx, &r),
|
|
"QueryGroupInfo failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryGroupInfo level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_QueryGroupMember(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryGroupMember r;
|
|
struct samr_RidAttrArray *rids = NULL;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing QueryGroupMember\n");
|
|
|
|
r.in.group_handle = handle;
|
|
r.out.rids = &rids;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &r),
|
|
"QueryGroupMember failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryGroupMember failed - %s\n", nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static bool test_SetGroupInfo(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryGroupInfo r;
|
|
union samr_GroupInfo *info;
|
|
struct samr_SetGroupInfo s;
|
|
uint16_t levels[] = {1, 2, 3, 4};
|
|
uint16_t set_ok[] = {0, 1, 1, 1};
|
|
int i;
|
|
bool ret = true;
|
|
|
|
for (i=0;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryGroupInfo level %u\n", levels[i]);
|
|
|
|
r.in.group_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupInfo_r(b, tctx, &r),
|
|
"QueryGroupInfo failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryGroupInfo level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
|
|
torture_comment(tctx, "Testing SetGroupInfo level %u\n", levels[i]);
|
|
|
|
s.in.group_handle = handle;
|
|
s.in.level = levels[i];
|
|
s.in.info = *r.out.info;
|
|
|
|
#if 0
|
|
/* disabled this, as it changes the name only from the point of view of samr,
|
|
but leaves the name from the point of view of w2k3 internals (and ldap). This means
|
|
the name is still reserved, so creating the old name fails, but deleting by the old name
|
|
also fails */
|
|
if (s.in.level == 2) {
|
|
init_lsa_String(&s.in.info->string, "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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryUserInfo level %u\n", levels[i]);
|
|
|
|
r.in.user_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
|
|
"QueryUserInfo failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_QueryUserInfo2(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryUserInfo2 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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryUserInfo2 level %u\n", levels[i]);
|
|
|
|
r.in.user_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo2_r(b, tctx, &r),
|
|
"QueryUserInfo2 failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryUserInfo2 level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_OpenUser(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle, uint32_t rid)
|
|
{
|
|
struct samr_OpenUser r;
|
|
struct policy_handle user_handle;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing OpenUser(%u)\n", rid);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
r.in.rid = rid;
|
|
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", rid, nt_errstr(r.out.result));
|
|
return false;
|
|
}
|
|
|
|
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_GetUserPwInfo(b, tctx, &user_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
if (!test_GetGroupsForUser(b, tctx, &user_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
if (!test_samr_handle_Close(b, tctx, &user_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_OpenGroup(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle, uint32_t rid)
|
|
{
|
|
struct samr_OpenGroup r;
|
|
struct policy_handle group_handle;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing OpenGroup(%u)\n", rid);
|
|
|
|
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)) {
|
|
torture_result(tctx, TORTURE_FAIL, "OpenGroup(%u) failed - %s\n", rid, nt_errstr(r.out.result));
|
|
return false;
|
|
}
|
|
|
|
if (!torture_setting_bool(tctx, "samba3", false)) {
|
|
if (!test_QuerySecurity(b, tctx, &group_handle)) {
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
if (!test_QueryGroupInfo(b, tctx, &group_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
if (!test_QueryGroupMember(b, tctx, &group_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
if (!test_samr_handle_Close(b, tctx, &group_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_OpenAlias(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle, uint32_t rid)
|
|
{
|
|
struct samr_OpenAlias r;
|
|
struct policy_handle alias_handle;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing OpenAlias(%u)\n", rid);
|
|
|
|
r.in.domain_handle = 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)) {
|
|
torture_result(tctx, TORTURE_FAIL, "OpenAlias(%u) failed - %s\n", rid, nt_errstr(r.out.result));
|
|
return false;
|
|
}
|
|
|
|
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_GetMembersInAlias(b, tctx, &alias_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
if (!test_samr_handle_Close(b, tctx, &alias_handle)) {
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool check_mask(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle, uint32_t rid,
|
|
uint32_t acct_flag_mask)
|
|
{
|
|
struct samr_OpenUser r;
|
|
struct samr_QueryUserInfo q;
|
|
union samr_UserInfo *info;
|
|
struct policy_handle user_handle;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing OpenUser(%u)\n", rid);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
r.in.rid = rid;
|
|
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", rid, nt_errstr(r.out.result));
|
|
return false;
|
|
}
|
|
|
|
q.in.user_handle = &user_handle;
|
|
q.in.level = 16;
|
|
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 16 failed - %s\n",
|
|
nt_errstr(q.out.result));
|
|
ret = false;
|
|
} else {
|
|
if ((acct_flag_mask & info->info16.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_idx<ARRAY_SIZE(masks);mask_idx++) {
|
|
r.in.domain_handle = handle;
|
|
r.in.resume_handle = &resume_handle;
|
|
r.in.acct_flags = mask = masks[mask_idx];
|
|
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_EnumDomainUsers_r(b, tctx, &r),
|
|
"EnumDomainUsers failed");
|
|
if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) &&
|
|
!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "EnumDomainUsers failed - %s\n", nt_errstr(r.out.result));
|
|
return false;
|
|
}
|
|
|
|
torture_assert(tctx, sam, "EnumDomainUsers failed: r.out.sam unexpectedly NULL");
|
|
|
|
if (sam->count == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (i=0;i<sam->count;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;i<sam->count;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;i<sam->count;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;i<ASYNC_COUNT;i++) {
|
|
req[i] = dcerpc_samr_EnumDomainUsers_r_send(tctx, tctx->ev, p->binding_handle, &r);
|
|
}
|
|
|
|
for (i=0;i<ASYNC_COUNT;i++) {
|
|
tevent_req_poll(req[i], tctx->ev);
|
|
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;i<sam->count;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;i<sam->count;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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing GetDisplayEnumerationIndex level %u\n", levels[i]);
|
|
|
|
init_lsa_String(&name, TEST_ACCOUNT_NAME);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.name = &name;
|
|
r.out.idx = &idx;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex_r(b, tctx, &r),
|
|
"GetDisplayEnumerationIndex failed");
|
|
|
|
if (ok_lvl[i] &&
|
|
!NT_STATUS_IS_OK(r.out.result) &&
|
|
!NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
|
|
init_lsa_String(&name, "zzzzzzzz");
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex_r(b, tctx, &r),
|
|
"GetDisplayEnumerationIndex failed");
|
|
|
|
if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_GetDisplayEnumerationIndex2(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_GetDisplayEnumerationIndex2 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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]);
|
|
|
|
init_lsa_String(&name, TEST_ACCOUNT_NAME);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.name = &name;
|
|
r.out.idx = &idx;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex2_r(b, tctx, &r),
|
|
"GetDisplayEnumerationIndex2 failed");
|
|
if (ok_lvl[i] &&
|
|
!NT_STATUS_IS_OK(r.out.result) &&
|
|
!NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex2 level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
|
|
init_lsa_String(&name, "zzzzzzzz");
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex2_r(b, tctx, &r),
|
|
"GetDisplayEnumerationIndex2 failed");
|
|
if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex2 level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define STRING_EQUAL_QUERY(s1, s2, user) \
|
|
if (s1.string == NULL && s2.string != NULL && s2.string[0] == '\0') { \
|
|
/* odd, but valid */ \
|
|
} else if ((s1.string && !s2.string) || (s2.string && !s1.string) || strcmp(s1.string, s2.string)) { \
|
|
torture_result(tctx, TORTURE_FAIL, "%s mismatch for %s: %s != %s (%s)\n", \
|
|
#s1, user.string, s1.string, s2.string, __location__); \
|
|
ret = false; \
|
|
}
|
|
#define INT_EQUAL_QUERY(s1, s2, user) \
|
|
if (s1 != s2) { \
|
|
torture_result(tctx, TORTURE_FAIL, "%s mismatch for %s: 0x%llx != 0x%llx (%s)\n", \
|
|
#s1, user.string, (unsigned long long)s1, (unsigned long long)s2, __location__); \
|
|
ret = false; \
|
|
}
|
|
|
|
static bool test_each_DisplayInfo_user(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct samr_QueryDisplayInfo *querydisplayinfo,
|
|
bool *seen_testuser)
|
|
{
|
|
struct samr_OpenUser r = {
|
|
.in = {
|
|
.domain_handle = querydisplayinfo->in.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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryDisplayInfo level %u\n", levels[i]);
|
|
|
|
r.in.start_idx = 0;
|
|
r.out.result = STATUS_MORE_ENTRIES;
|
|
while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) {
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.max_entries = 2;
|
|
r.in.buf_size = (uint32_t)-1;
|
|
r.out.total_size = &total_size;
|
|
r.out.returned_size = &returned_size;
|
|
r.out.info = &disp_info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
|
|
"QueryDisplayInfo failed");
|
|
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",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
switch (r.in.level) {
|
|
case 1:
|
|
if (!test_each_DisplayInfo_user(b, tctx, &r, &seen_testuser)) {
|
|
ret = false;
|
|
}
|
|
r.in.start_idx += r.out.info->info1.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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryDisplayInfo2 level %u\n", levels[i]);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.start_idx = 0;
|
|
r.in.max_entries = 1000;
|
|
r.in.buf_size = (uint32_t)-1;
|
|
r.out.total_size = &total_size;
|
|
r.out.returned_size = &returned_size;
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo2_r(b, tctx, &r),
|
|
"QueryDisplayInfo2 failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo2 level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_QueryDisplayInfo3(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryDisplayInfo3 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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryDisplayInfo3 level %u\n", levels[i]);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.in.start_idx = 0;
|
|
r.in.max_entries = 1000;
|
|
r.in.buf_size = (uint32_t)-1;
|
|
r.out.total_size = &total_size;
|
|
r.out.returned_size = &returned_size;
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo3_r(b, tctx, &r),
|
|
"QueryDisplayInfo3 failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo3 level %u failed - %s\n",
|
|
levels[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static bool test_QueryDisplayInfo_continue(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_QueryDisplayInfo r;
|
|
bool ret = true;
|
|
uint32_t total_size;
|
|
uint32_t returned_size;
|
|
union samr_DispInfo info;
|
|
|
|
torture_comment(tctx, "Testing QueryDisplayInfo continuation\n");
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = 1;
|
|
r.in.start_idx = 0;
|
|
r.in.max_entries = 1;
|
|
r.in.buf_size = (uint32_t)-1;
|
|
r.out.total_size = &total_size;
|
|
r.out.returned_size = &returned_size;
|
|
r.out.info = &info;
|
|
|
|
do {
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
|
|
"QueryDisplayInfo failed");
|
|
if (NT_STATUS_IS_OK(r.out.result) && *r.out.returned_size != 0) {
|
|
if (r.out.info->info1.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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryDomainInfo level %u\n", levels[i]);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
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;
|
|
}
|
|
|
|
switch (levels[i]) {
|
|
case 2:
|
|
if (strcmp(info->general.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;i<ARRAY_SIZE(levels);i++) {
|
|
torture_comment(tctx, "Testing QueryDomainInfo2 level %u\n", levels[i]);
|
|
|
|
r.in.domain_handle = handle;
|
|
r.in.level = levels[i];
|
|
r.out.info = &info;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r),
|
|
"QueryDomainInfo2 failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo2 level %u failed - %s\n",
|
|
r.in.level, nt_errstr(r.out.result));
|
|
ret = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Test whether querydispinfo level 5 and enumdomgroups return the same
|
|
set of group names. */
|
|
static bool test_GroupList(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct dom_sid *domain_sid,
|
|
struct policy_handle *handle)
|
|
{
|
|
struct samr_EnumDomainGroups q1;
|
|
struct samr_QueryDisplayInfo q2;
|
|
NTSTATUS status;
|
|
uint32_t resume_handle=0;
|
|
struct samr_SamArray *sam = NULL;
|
|
uint32_t num_entries = 0;
|
|
int i;
|
|
bool ret = true;
|
|
uint32_t total_size;
|
|
uint32_t returned_size;
|
|
union samr_DispInfo info;
|
|
|
|
size_t num_names = 0;
|
|
const char **names = NULL;
|
|
|
|
bool builtin_domain = dom_sid_compare(domain_sid,
|
|
&global_sid_Builtin) == 0;
|
|
|
|
torture_comment(tctx, "Testing coherency of querydispinfo vs enumdomgroups\n");
|
|
|
|
q1.in.domain_handle = handle;
|
|
q1.in.resume_handle = &resume_handle;
|
|
q1.in.max_size = 5;
|
|
q1.out.resume_handle = &resume_handle;
|
|
q1.out.num_entries = &num_entries;
|
|
q1.out.sam = &sam;
|
|
|
|
status = STATUS_MORE_ENTRIES;
|
|
while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &q1),
|
|
"EnumDomainGroups failed");
|
|
status = q1.out.result;
|
|
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
|
|
break;
|
|
|
|
for (i=0; i<*q1.out.num_entries; i++) {
|
|
add_string_to_array(tctx,
|
|
sam->entries[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; i<q2.out.info->info5.count; i++) {
|
|
int j;
|
|
const char *name = q2.out.info->info5.entries[i].account_name.string;
|
|
bool found = false;
|
|
for (j=0; j<num_names; j++) {
|
|
if (names[j] == NULL)
|
|
continue;
|
|
if (strequal(names[j], name)) {
|
|
names[j] = NULL;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((!found) && (!builtin_domain)) {
|
|
torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo gave name [%s] that EnumDomainGroups did not\n",
|
|
name);
|
|
ret = false;
|
|
}
|
|
}
|
|
q2.in.start_idx += q2.out.info->info5.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; i<num_names; i++) {
|
|
if (names[i] != NULL) {
|
|
torture_result(tctx, TORTURE_FAIL, "EnumDomainGroups gave name [%s] that QueryDisplayInfo did not\n",
|
|
names[i]);
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_DeleteDomainGroup(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *group_handle)
|
|
{
|
|
struct samr_DeleteDomainGroup d;
|
|
|
|
torture_comment(tctx, "Testing DeleteDomainGroup\n");
|
|
|
|
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");
|
|
torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteDomainGroup");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_TestPrivateFunctionsDomain(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *domain_handle)
|
|
{
|
|
struct samr_TestPrivateFunctionsDomain r;
|
|
bool ret = true;
|
|
|
|
torture_comment(tctx, "Testing TestPrivateFunctionsDomain\n");
|
|
|
|
r.in.domain_handle = domain_handle;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_TestPrivateFunctionsDomain_r(b, tctx, &r),
|
|
"TestPrivateFunctionsDomain failed");
|
|
torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsDomain");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_RidToSid(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct dom_sid *domain_sid,
|
|
struct policy_handle *domain_handle)
|
|
{
|
|
struct samr_RidToSid r;
|
|
bool ret = true;
|
|
struct dom_sid *calc_sid, *out_sid;
|
|
int rids[] = { 0, 42, 512, 10200 };
|
|
int i;
|
|
|
|
for (i=0;i<ARRAY_SIZE(rids);i++) {
|
|
torture_comment(tctx, "Testing RidToSid\n");
|
|
|
|
calc_sid = dom_sid_dup(tctx, domain_sid);
|
|
r.in.domain_handle = domain_handle;
|
|
r.in.rid = rids[i];
|
|
r.out.sid = &out_sid;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_RidToSid_r(b, tctx, &r),
|
|
"RidToSid failed");
|
|
if (!NT_STATUS_IS_OK(r.out.result)) {
|
|
torture_result(tctx, TORTURE_FAIL, "RidToSid for %d failed - %s\n", rids[i], nt_errstr(r.out.result));
|
|
ret = false;
|
|
} else {
|
|
calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]);
|
|
|
|
if (!dom_sid_equal(calc_sid, out_sid)) {
|
|
torture_result(tctx, TORTURE_FAIL, "RidToSid for %d failed - got %s, expected %s\n", rids[i],
|
|
dom_sid_string(tctx, out_sid),
|
|
dom_sid_string(tctx, calc_sid));
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_GetBootKeyInformation(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *domain_handle)
|
|
{
|
|
struct samr_GetBootKeyInformation r;
|
|
bool ret = true;
|
|
uint32_t unknown = 0;
|
|
NTSTATUS status;
|
|
|
|
torture_comment(tctx, "Testing GetBootKeyInformation\n");
|
|
|
|
r.in.domain_handle = domain_handle;
|
|
r.out.unknown = &unknown;
|
|
|
|
status = dcerpc_samr_GetBootKeyInformation_r(b, tctx, &r);
|
|
if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(r.out.result)) {
|
|
status = r.out.result;
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
/* w2k3 seems to fail this sometimes and pass it sometimes */
|
|
torture_comment(tctx, "GetBootKeyInformation (ignored) - %s\n", nt_errstr(status));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_AddGroupMember(struct dcerpc_binding_handle *b,
|
|
struct torture_context *tctx,
|
|
struct policy_handle *domain_handle,
|
|
struct policy_handle *group_handle)
|
|
{
|
|
NTSTATUS status;
|
|
struct samr_AddGroupMember r;
|
|
struct samr_DeleteGroupMember d;
|
|
struct samr_QueryGroupMember q;
|
|
struct samr_RidAttrArray *rids = NULL;
|
|
struct samr_SetMemberAttributesOfGroup s;
|
|
uint32_t rid;
|
|
bool found_member = false;
|
|
int i;
|
|
|
|
status = test_LookupName(b, tctx, domain_handle, TEST_ACCOUNT_NAME, &rid);
|
|
torture_assert_ntstatus_ok(tctx, status, "test_AddGroupMember looking up name " TEST_ACCOUNT_NAME);
|
|
|
|
r.in.group_handle = group_handle;
|
|
r.in.rid = rid;
|
|
r.in.flags = 0; /* ??? */
|
|
|
|
torture_comment(tctx, "Testing AddGroupMember, QueryGroupMember and DeleteGroupMember\n");
|
|
|
|
d.in.group_handle = group_handle;
|
|
d.in.rid = rid;
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteGroupMember_r(b, tctx, &d),
|
|
"DeleteGroupMember failed");
|
|
torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_NOT_IN_GROUP, d.out.result, "DeleteGroupMember");
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r),
|
|
"AddGroupMember failed");
|
|
torture_assert_ntstatus_ok(tctx, r.out.result, "AddGroupMember");
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r),
|
|
"AddGroupMember failed");
|
|
torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_IN_GROUP, r.out.result, "AddGroupMember");
|
|
|
|
if (torture_setting_bool(tctx, "samba4", false) ||
|
|
torture_setting_bool(tctx, "samba3", false)) {
|
|
torture_comment(tctx, "skipping SetMemberAttributesOfGroup test against Samba\n");
|
|
} else {
|
|
/* this one is quite strange. I am using random inputs in the
|
|
hope of triggering an error that might give us a clue */
|
|
|
|
s.in.group_handle = group_handle;
|
|
s.in.unknown1 = random();
|
|
s.in.unknown2 = random();
|
|
|
|
torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetMemberAttributesOfGroup_r(b, tctx, &s),
|
|
"SetMemberAttributesOfGroup failed");
|
|
torture_assert_ntstatus_ok(tctx, s.out.result, "SetMemberAttributesOfGroup");
|
|
}
|
|
|
|
q.in.group_handle = group_handle;
|
|
q.out.rids = &rids;
|
|
|
|
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 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;i<sam->count;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;
|
|
}
|