mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
3b217ffcf2
certainly not complete, we need to verify all incoming data, not just
accounts.
This needs to be exercised against some 'intersting' users, but for
now it shows that the IDL is already very accurate, particularly after
the previous commits cleanups of names.
Also commit the changes to keep RPC-LOGIN building.
Andrew Bartlett
(This used to be commit 87ec6834bc
)
815 lines
25 KiB
C
815 lines
25 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
|
|
test suite for netlogon rpc operations
|
|
|
|
Copyright (C) Andrew Tridgell 2003
|
|
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
|
|
Copyright (C) Tim Potter 2003
|
|
|
|
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 2 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, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "librpc/gen_ndr/ndr_netlogon.h"
|
|
#include "auth/auth.h"
|
|
#include "dlinklist.h"
|
|
#include "lib/crypto/crypto.h"
|
|
|
|
#define TEST_MACHINE_NAME "samsynctest"
|
|
|
|
/*
|
|
try a netlogon SamLogon
|
|
*/
|
|
static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
|
|
struct creds_CredentialState *creds,
|
|
const char *domain, const char *username,
|
|
struct samr_Password *lm_hash,
|
|
struct samr_Password *nt_hash,
|
|
struct netr_SamInfo3 **info3)
|
|
{
|
|
NTSTATUS status;
|
|
struct netr_LogonSamLogon r;
|
|
struct netr_Authenticator auth, auth2;
|
|
struct netr_NetworkInfo ninfo;
|
|
|
|
ninfo.identity_info.domain_name.string = domain;
|
|
ninfo.identity_info.parameter_control = 0;
|
|
ninfo.identity_info.logon_id_low = 0;
|
|
ninfo.identity_info.logon_id_high = 0;
|
|
ninfo.identity_info.account_name.string = username;
|
|
ninfo.identity_info.workstation.string = TEST_MACHINE_NAME;
|
|
generate_random_buffer(ninfo.challenge,
|
|
sizeof(ninfo.challenge));
|
|
if (nt_hash) {
|
|
ninfo.nt.length = 24;
|
|
ninfo.nt.data = talloc(mem_ctx, 24);
|
|
SMBOWFencrypt(nt_hash->hash, ninfo.challenge, ninfo.nt.data);
|
|
} else {
|
|
ninfo.nt.length = 0;
|
|
ninfo.nt.data = NULL;
|
|
}
|
|
|
|
if (lm_hash) {
|
|
ninfo.lm.length = 24;
|
|
ninfo.lm.data = talloc(mem_ctx, 24);
|
|
SMBOWFencrypt(lm_hash->hash, ninfo.challenge, ninfo.lm.data);
|
|
} else {
|
|
ninfo.lm.length = 0;
|
|
ninfo.lm.data = NULL;
|
|
}
|
|
|
|
r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
|
|
r.in.workstation = TEST_MACHINE_NAME;
|
|
r.in.credential = &auth;
|
|
r.in.return_authenticator = &auth2;
|
|
r.in.logon_level = 2;
|
|
r.in.logon.network = &ninfo;
|
|
|
|
ZERO_STRUCT(auth2);
|
|
creds_client_authenticator(creds, &auth);
|
|
|
|
r.in.validation_level = 3;
|
|
|
|
status = dcerpc_netr_LogonSamLogon(p, mem_ctx, &r);
|
|
|
|
if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
|
|
printf("Credential chaining failed\n");
|
|
}
|
|
|
|
if (info3) {
|
|
*info3 = r.out.validation.sam3;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
struct samsync_state {
|
|
/* we remember the sequence numbers so we can easily do a DatabaseDelta */
|
|
uint64_t seq_num[3];
|
|
char *domain_name[2];
|
|
struct samsync_secret *secrets;
|
|
struct samsync_trusted_domain *trusted_domains;
|
|
struct creds_CredentialState *creds;
|
|
struct policy_handle *connect_handle;
|
|
struct policy_handle *domain_handle[2];
|
|
struct dom_sid *sid[2];
|
|
struct dcerpc_pipe *p;
|
|
struct dcerpc_pipe *p_samr;
|
|
};
|
|
|
|
struct samsync_secret {
|
|
struct samsync_secret *prev, *next;
|
|
DATA_BLOB secret;
|
|
char *name;
|
|
};
|
|
|
|
struct samsync_trusted_domain {
|
|
struct samsync_trusted_domain *prev, *next;
|
|
struct dom_sid *sid;
|
|
char *name;
|
|
};
|
|
|
|
static struct policy_handle *samsync_open_domain(TALLOC_CTX *mem_ctx,
|
|
struct samsync_state *samsync_state,
|
|
const char *domain,
|
|
struct dom_sid **sid)
|
|
{
|
|
struct samr_String name;
|
|
struct samr_OpenDomain o;
|
|
struct samr_LookupDomain l;
|
|
struct policy_handle *domain_handle = talloc_p(mem_ctx, struct policy_handle);
|
|
NTSTATUS nt_status;
|
|
|
|
name.string = domain;
|
|
l.in.connect_handle = samsync_state->connect_handle;
|
|
l.in.domain = &name;
|
|
|
|
nt_status = dcerpc_samr_LookupDomain(samsync_state->p_samr, mem_ctx, &l);
|
|
if (!NT_STATUS_IS_OK(nt_status)) {
|
|
printf("LookupDomain failed - %s\n", nt_errstr(nt_status));
|
|
return NULL;
|
|
}
|
|
|
|
o.in.connect_handle = samsync_state->connect_handle;
|
|
o.in.access_mask = SEC_RIGHTS_MAXIMUM_ALLOWED;
|
|
o.in.sid = l.out.sid;
|
|
o.out.domain_handle = domain_handle;
|
|
|
|
if (sid) {
|
|
*sid = l.out.sid;
|
|
}
|
|
|
|
nt_status = dcerpc_samr_OpenDomain(samsync_state->p_samr, mem_ctx, &o);
|
|
if (!NT_STATUS_IS_OK(nt_status)) {
|
|
printf("OpenDomain failed - %s\n", nt_errstr(nt_status));
|
|
return NULL;
|
|
}
|
|
|
|
return domain_handle;
|
|
}
|
|
|
|
static BOOL samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
|
|
int database_id, struct netr_DELTA_ENUM *delta)
|
|
{
|
|
struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
|
|
struct dom_sid *dom_sid;
|
|
samsync_state->seq_num[database_id] =
|
|
domain->sequence_num;
|
|
switch (database_id) {
|
|
case SAM_DATABASE_DOMAIN:
|
|
break;
|
|
case SAM_DATABASE_BUILTIN:
|
|
if (StrCaseCmp("BUILTIN", domain->domain_name.string) != 0) {
|
|
printf("BUILTIN domain has different name: %s\n", domain->domain_name.string);
|
|
}
|
|
break;
|
|
case SAM_DATABASE_PRIVS:
|
|
printf("DOMAIN entry on privs DB!\n");
|
|
return False;
|
|
break;
|
|
}
|
|
|
|
if (!samsync_state->domain_name[database_id]) {
|
|
samsync_state->domain_name[database_id] =
|
|
talloc_reference(samsync_state, domain->domain_name.string);
|
|
} else {
|
|
if (StrCaseCmp(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) {
|
|
printf("Domain has name varies!: %s != %s\n", samsync_state->domain_name[database_id],
|
|
domain->domain_name.string);
|
|
return False;
|
|
}
|
|
}
|
|
|
|
if (!samsync_state->domain_handle[database_id]) {
|
|
samsync_state->domain_handle[database_id]
|
|
= samsync_open_domain(mem_ctx, samsync_state, samsync_state->domain_name[database_id],
|
|
&dom_sid);
|
|
}
|
|
if (samsync_state->domain_handle[database_id]) {
|
|
samsync_state->sid[database_id] = talloc_reference(samsync_state, dom_sid);
|
|
}
|
|
|
|
printf("\tsequence_nums[%d/%s]=%llu\n",
|
|
database_id, domain->domain_name.string,
|
|
samsync_state->seq_num[database_id]);
|
|
return True;
|
|
}
|
|
|
|
static BOOL samsync_handle_policy(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
|
|
int database_id, struct netr_DELTA_ENUM *delta)
|
|
{
|
|
struct netr_DELTA_POLICY *policy = delta->delta_union.policy;
|
|
|
|
samsync_state->seq_num[database_id] =
|
|
policy->sequence_num;
|
|
|
|
if (!samsync_state->domain_name[SAM_DATABASE_DOMAIN]) {
|
|
samsync_state->domain_name[SAM_DATABASE_DOMAIN] =
|
|
talloc_reference(samsync_state, policy->primary_domain_name.string);
|
|
} else {
|
|
if (StrCaseCmp(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) {
|
|
printf("PRIMARY domain has name varies between DOMAIN and POLICY!: %s != %s\n", samsync_state->domain_name[SAM_DATABASE_DOMAIN],
|
|
policy->primary_domain_name.string);
|
|
return False;
|
|
}
|
|
}
|
|
|
|
if (!sid_equal(samsync_state->sid[SAM_DATABASE_DOMAIN], policy->sid)) {
|
|
printf("Domain SID from POLICY (%s) does not match domain sid from SAMR (%s)\n",
|
|
dom_sid_string(mem_ctx, policy->sid), dom_sid_string(mem_ctx, samsync_state->sid[SAM_DATABASE_DOMAIN]));
|
|
return False;
|
|
}
|
|
|
|
printf("\tsequence_nums[%d/PRIVS]=%llu\n",
|
|
database_id,
|
|
samsync_state->seq_num[database_id]);
|
|
return True;
|
|
}
|
|
|
|
#define TEST_INT_EQUAL(i1, i2) do {\
|
|
if (i1 != i2) {\
|
|
printf("integer mismatch: " #i1 ":%d != " #i2 ": %d\n", \
|
|
i1, i2);\
|
|
ret = False;\
|
|
} \
|
|
} while (0)
|
|
#define TEST_TIME_EQUAL(t1, t2) do {\
|
|
if (t1 != t2) {\
|
|
printf("NTTIME mismatch: " #t1 ":%s != " #t2 ": %s\n", \
|
|
nt_time_string(mem_ctx, t1), nt_time_string(mem_ctx, t2));\
|
|
ret = False;\
|
|
} \
|
|
} while (0)
|
|
#define TEST_STRING_EQUAL(s1, s2) do {\
|
|
if (!((!s1.string || s1.string[0]=='\0') && (!s2.string || s2.string[0]=='\0')) \
|
|
&& strcmp_safe(s1.string, s2.string) != 0) {\
|
|
printf("string mismatch: " #s1 ":%s != " #s2 ": %s\n", \
|
|
s1.string, s2.string);\
|
|
ret = False;\
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
static BOOL samsync_handle_user(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
|
|
int database_id, struct netr_DELTA_ENUM *delta)
|
|
{
|
|
uint32 rid = delta->delta_id_union.rid;
|
|
struct netr_DELTA_USER *user = delta->delta_union.user;
|
|
struct netr_SamInfo3 *info3;
|
|
struct samr_Password lm_hash;
|
|
struct samr_Password nt_hash;
|
|
struct samr_Password *lm_hash_p = NULL;
|
|
struct samr_Password *nt_hash_p = NULL;
|
|
const char *domain = samsync_state->domain_name[database_id];
|
|
const char *username = user->account_name.string;
|
|
NTSTATUS nt_status;
|
|
BOOL ret = True;
|
|
|
|
struct samr_OpenUser r;
|
|
struct samr_QueryUserInfo q;
|
|
struct policy_handle user_handle;
|
|
|
|
if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
|
|
printf("SamSync needs domain information before the users\n");
|
|
return False;
|
|
}
|
|
|
|
r.in.domain_handle = samsync_state->domain_handle[database_id];
|
|
r.in.access_mask = SEC_RIGHTS_MAXIMUM_ALLOWED;
|
|
r.in.rid = rid;
|
|
r.out.user_handle = &user_handle;
|
|
|
|
nt_status = dcerpc_samr_OpenUser(samsync_state->p_samr, mem_ctx, &r);
|
|
if (!NT_STATUS_IS_OK(nt_status)) {
|
|
printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
|
|
return False;
|
|
}
|
|
|
|
q.in.user_handle = &user_handle;
|
|
q.in.level = 21;
|
|
|
|
nt_status = dcerpc_samr_QueryUserInfo(samsync_state->p_samr, mem_ctx, &q);
|
|
if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &user_handle)) {
|
|
return False;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(nt_status)) {
|
|
printf("QueryUserInfo level %u failed - %s\n",
|
|
q.in.level, nt_errstr(nt_status));
|
|
return False;
|
|
}
|
|
|
|
TEST_STRING_EQUAL(q.out.info->info21.account_name, user->account_name);
|
|
TEST_STRING_EQUAL(q.out.info->info21.full_name, user->full_name);
|
|
TEST_INT_EQUAL(q.out.info->info21.rid, user->rid);
|
|
TEST_INT_EQUAL(q.out.info->info21.primary_gid, user->primary_gid);
|
|
TEST_STRING_EQUAL(q.out.info->info21.home_directory, user->home_directory);
|
|
TEST_STRING_EQUAL(q.out.info->info21.home_drive, user->home_drive);
|
|
TEST_STRING_EQUAL(q.out.info->info21.logon_script, user->logon_script);
|
|
TEST_STRING_EQUAL(q.out.info->info21.description, user->description);
|
|
TEST_STRING_EQUAL(q.out.info->info21.workstations, user->workstations);
|
|
|
|
TEST_TIME_EQUAL(q.out.info->info21.last_logon, user->last_logon);
|
|
TEST_TIME_EQUAL(q.out.info->info21.last_logoff, user->last_logoff);
|
|
|
|
|
|
TEST_INT_EQUAL(q.out.info->info21.logon_hours.units_per_week,
|
|
user->logon_hours.units_per_week);
|
|
if (ret) {
|
|
if (memcmp(q.out.info->info21.logon_hours.bitmap, user->logon_hours.bitmap,
|
|
q.out.info->info21.logon_hours.units_per_week/8) != 0) {
|
|
printf("Logon hours mismatch\n");
|
|
ret = False;
|
|
}
|
|
}
|
|
|
|
TEST_INT_EQUAL(q.out.info->info21.bad_password_count,
|
|
user->bad_password_count);
|
|
TEST_INT_EQUAL(q.out.info->info21.logon_count,
|
|
user->logon_count);
|
|
|
|
TEST_TIME_EQUAL(q.out.info->info21.last_password_change,
|
|
user->last_password_change);
|
|
TEST_TIME_EQUAL(q.out.info->info21.acct_expiry,
|
|
user->acct_expiry);
|
|
|
|
TEST_INT_EQUAL(q.out.info->info21.logon_hours.units_per_week,
|
|
user->logon_hours.units_per_week);
|
|
|
|
TEST_INT_EQUAL(q.out.info->info21.acct_flags, user->acct_flags);
|
|
TEST_INT_EQUAL(q.out.info->info21.nt_password_set, user->nt_password_present);
|
|
TEST_INT_EQUAL(q.out.info->info21.lm_password_set, user->lm_password_present);
|
|
TEST_INT_EQUAL(q.out.info->info21.password_expired, user->password_expired);
|
|
|
|
TEST_STRING_EQUAL(q.out.info->info21.comment, user->comment);
|
|
TEST_STRING_EQUAL(q.out.info->info21.parameters, user->parameters);
|
|
|
|
TEST_INT_EQUAL(q.out.info->info21.country_code, user->country_code);
|
|
TEST_INT_EQUAL(q.out.info->info21.code_page, user->code_page);
|
|
|
|
TEST_STRING_EQUAL(q.out.info->info21.profile_path, user->profile_path);
|
|
|
|
if (user->lm_password_present) {
|
|
sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
|
|
lm_hash_p = &lm_hash;
|
|
}
|
|
if (user->nt_password_present) {
|
|
sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
|
|
nt_hash_p = &nt_hash;
|
|
}
|
|
|
|
if (user->user_private_info.SensitiveData) {
|
|
DATA_BLOB data;
|
|
struct netr_USER_KEYS keys;
|
|
data.data = user->user_private_info.SensitiveData;
|
|
data.length = user->user_private_info.DataLength;
|
|
creds_arcfour_crypt(samsync_state->creds, data.data, data.length);
|
|
#if 0
|
|
printf("Sensitive Data for %s:\n", username);
|
|
dump_data(0, data.data, data.length);
|
|
#endif
|
|
nt_status = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
|
|
if (NT_STATUS_IS_OK(nt_status)) {
|
|
if (keys.keys.keys2.lmpassword.length == 16) {
|
|
sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
|
|
lm_hash_p = &lm_hash;
|
|
}
|
|
if (keys.keys.keys2.ntpassword.length == 16) {
|
|
sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
|
|
nt_hash_p = &nt_hash;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!lm_hash_p && !nt_hash_p) {
|
|
printf("NO password set for %s\n",
|
|
user->account_name.string);
|
|
return True;
|
|
}
|
|
|
|
nt_status = test_SamLogon(samsync_state->p, mem_ctx, samsync_state->creds,
|
|
domain,
|
|
username,
|
|
lm_hash_p,
|
|
nt_hash_p,
|
|
&info3);
|
|
|
|
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) {
|
|
if (user->acct_flags & ACB_DISABLED) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)) {
|
|
if (user->acct_flags & ACB_WSTRUST) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT)) {
|
|
if (user->acct_flags & ACB_SVRTRUST) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
|
|
if (user->acct_flags & ACB_DOMTRUST) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
|
|
if (user->acct_flags & ACB_DOMTRUST) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
|
|
if (user->acct_flags & ACB_AUTOLOCK) {
|
|
return True;
|
|
}
|
|
} else if (NT_STATUS_IS_OK(nt_status)) {
|
|
TEST_INT_EQUAL(user->rid, info3->base.rid);
|
|
TEST_INT_EQUAL(user->primary_gid, info3->base.primary_gid);
|
|
TEST_INT_EQUAL(user->acct_flags, info3->base.acct_flags);
|
|
TEST_STRING_EQUAL(user->account_name, info3->base.account_name);
|
|
TEST_STRING_EQUAL(user->full_name, info3->base.full_name);
|
|
TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script);
|
|
TEST_STRING_EQUAL(user->profile_path, info3->base.profile_path);
|
|
TEST_STRING_EQUAL(user->home_directory, info3->base.home_directory);
|
|
TEST_STRING_EQUAL(user->home_drive, info3->base.home_drive);
|
|
TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script);
|
|
|
|
|
|
TEST_TIME_EQUAL(user->last_logon, info3->base.last_logon);
|
|
TEST_TIME_EQUAL(user->acct_expiry, info3->base.acct_expiry);
|
|
TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change);
|
|
|
|
/* Does the concept of a logoff time ever really
|
|
* exist? (not in any sensible way, according to the
|
|
* doco I read -- abartlet) */
|
|
|
|
/* This copes with the two different versions of 0 I see */
|
|
if (!((user->last_logoff == 0)
|
|
&& (info3->base.last_logoff == 0x7fffffffffffffffLL))) {
|
|
TEST_TIME_EQUAL(user->last_logoff, info3->base.last_logoff);
|
|
}
|
|
return ret;
|
|
} else {
|
|
printf("Could not validate password for user %s\\%s: %s\n",
|
|
domain, username, nt_errstr(nt_status));
|
|
return False;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static BOOL samsync_handle_secret(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
|
|
int database_id, struct netr_DELTA_ENUM *delta)
|
|
{
|
|
struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
|
|
const char *name = delta->delta_id_union.name;
|
|
struct samsync_secret *new = talloc_p(samsync_state, struct samsync_secret);
|
|
|
|
creds_arcfour_crypt(samsync_state->creds, secret->current_cipher.cipher_data,
|
|
secret->current_cipher.maxlen);
|
|
|
|
creds_arcfour_crypt(samsync_state->creds, secret->old_cipher.cipher_data,
|
|
secret->old_cipher.maxlen);
|
|
|
|
new->name = talloc_reference(new, name);
|
|
new->secret = data_blob_talloc(new, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
|
|
|
|
DLIST_ADD(samsync_state->secrets, new);
|
|
|
|
return True;
|
|
}
|
|
|
|
static BOOL samsync_handle_trusted_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
|
|
int database_id, struct netr_DELTA_ENUM *delta)
|
|
{
|
|
struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain;
|
|
struct dom_sid *dom_sid = delta->delta_id_union.sid;
|
|
|
|
struct samsync_trusted_domain *new = talloc_p(samsync_state, struct samsync_trusted_domain);
|
|
new->name = talloc_reference(new, trusted_domain->domain_name.string);
|
|
new->sid = talloc_reference(new, dom_sid);
|
|
|
|
DLIST_ADD(samsync_state->trusted_domains, new);
|
|
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
try a netlogon DatabaseSync
|
|
*/
|
|
static BOOL test_DatabaseSync(struct samsync_state *samsync_state,
|
|
TALLOC_CTX *mem_ctx)
|
|
{
|
|
NTSTATUS status;
|
|
struct netr_DatabaseSync r;
|
|
const uint32_t database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
|
|
int i, d;
|
|
BOOL ret = True;
|
|
struct samsync_trusted_domain *t;
|
|
struct samsync_secret *s;
|
|
|
|
const char *domain, *username;
|
|
|
|
r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p));
|
|
r.in.computername = TEST_MACHINE_NAME;
|
|
r.in.preferredmaximumlength = (uint32_t)-1;
|
|
ZERO_STRUCT(r.in.return_authenticator);
|
|
|
|
for (i=0;i<ARRAY_SIZE(database_ids);i++) {
|
|
r.in.sync_context = 0;
|
|
r.in.database_id = database_ids[i];
|
|
|
|
printf("Testing DatabaseSync of id %d\n", r.in.database_id);
|
|
|
|
do {
|
|
creds_client_authenticator(samsync_state->creds, &r.in.credential);
|
|
|
|
status = dcerpc_netr_DatabaseSync(samsync_state->p, mem_ctx, &r);
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
printf("DatabaseSync - %s\n", nt_errstr(status));
|
|
ret = False;
|
|
break;
|
|
}
|
|
|
|
if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) {
|
|
printf("Credential chaining failed\n");
|
|
}
|
|
|
|
r.in.sync_context = r.out.sync_context;
|
|
|
|
for (d=0; d < r.out.delta_enum_array->num_deltas; d++) {
|
|
switch (r.out.delta_enum_array->delta_enum[d].delta_type) {
|
|
case NETR_DELTA_DOMAIN:
|
|
ret &= samsync_handle_domain(mem_ctx, samsync_state,
|
|
r.in.database_id, &r.out.delta_enum_array->delta_enum[d]);
|
|
break;
|
|
case NETR_DELTA_USER:
|
|
ret &= samsync_handle_user(mem_ctx, samsync_state,
|
|
r.in.database_id, &r.out.delta_enum_array->delta_enum[d]);
|
|
break;
|
|
case NETR_DELTA_TRUSTED_DOMAIN:
|
|
ret &= samsync_handle_trusted_domain(mem_ctx, samsync_state,
|
|
r.in.database_id, &r.out.delta_enum_array->delta_enum[d]);
|
|
break;
|
|
case NETR_DELTA_SECRET:
|
|
ret &= samsync_handle_secret(mem_ctx, samsync_state,
|
|
r.in.database_id, &r.out.delta_enum_array->delta_enum[d]);
|
|
break;
|
|
case NETR_DELTA_POLICY:
|
|
ret &= samsync_handle_policy(mem_ctx, samsync_state,
|
|
r.in.database_id, &r.out.delta_enum_array->delta_enum[d]);
|
|
break;
|
|
}
|
|
}
|
|
} while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
|
|
|
|
}
|
|
|
|
domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN];
|
|
if (!domain) {
|
|
printf("Never got a DOMAIN object in samsync!\n");
|
|
return False;
|
|
}
|
|
|
|
username = talloc_asprintf(mem_ctx, "%s$", domain);
|
|
for (t=samsync_state->trusted_domains; t; t=t->next) {
|
|
char *secret_name = talloc_asprintf(mem_ctx, "G$$%s", t->name);
|
|
for (s=samsync_state->secrets; s; s=s->next) {
|
|
printf("Checking secret %s against %s\n",
|
|
s->name, secret_name);
|
|
if (StrCaseCmp(s->name, secret_name) == 0) {
|
|
NTSTATUS nt_status;
|
|
struct samr_Password nt_hash;
|
|
mdfour(nt_hash.hash, s->secret.data, s->secret.length);
|
|
|
|
printf("Checking password for %s\\%s\n", t->name, username);
|
|
nt_status = test_SamLogon(samsync_state->p, mem_ctx, samsync_state->creds,
|
|
t->name,
|
|
username,
|
|
NULL,
|
|
&nt_hash,
|
|
NULL);
|
|
if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
|
|
printf("Could not verify trust password to %s: %s\n",
|
|
t->name, nt_errstr(nt_status));
|
|
ret = False;
|
|
}
|
|
|
|
/* break it */
|
|
nt_hash.hash[0]++;
|
|
nt_status = test_SamLogon(samsync_state->p, mem_ctx, samsync_state->creds,
|
|
t->name,
|
|
username,
|
|
NULL,
|
|
&nt_hash,
|
|
NULL);
|
|
|
|
if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
|
|
printf("Verifiction of trust password to %s: should have failed (wrong password), instead: %s\n",
|
|
t->name, nt_errstr(nt_status));
|
|
ret = False;
|
|
ret = False;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
try a netlogon DatabaseDeltas
|
|
*/
|
|
static BOOL test_DatabaseDeltas(struct samsync_state *samsync_state, TALLOC_CTX *mem_ctx)
|
|
{
|
|
NTSTATUS status;
|
|
struct netr_DatabaseDeltas r;
|
|
const uint32_t database_ids[] = {0, 1, 2};
|
|
int i;
|
|
BOOL ret = True;
|
|
|
|
r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p));
|
|
r.in.computername = TEST_MACHINE_NAME;
|
|
r.in.preferredmaximumlength = (uint32_t)-1;
|
|
ZERO_STRUCT(r.in.return_authenticator);
|
|
|
|
for (i=0;i<ARRAY_SIZE(database_ids);i++) {
|
|
r.in.database_id = database_ids[i];
|
|
r.in.sequence_num = samsync_state->seq_num[i];
|
|
|
|
if (r.in.sequence_num == 0) continue;
|
|
|
|
r.in.sequence_num -= 1;
|
|
|
|
|
|
printf("Testing DatabaseDeltas of id %d at %llu\n",
|
|
r.in.database_id, r.in.sequence_num);
|
|
|
|
do {
|
|
creds_client_authenticator(samsync_state->creds, &r.in.credential);
|
|
|
|
status = dcerpc_netr_DatabaseDeltas(samsync_state->p, mem_ctx, &r);
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
printf("DatabaseDeltas - %s\n", nt_errstr(status));
|
|
ret = False;
|
|
break;
|
|
}
|
|
|
|
if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) {
|
|
printf("Credential chaining failed\n");
|
|
}
|
|
|
|
r.in.sequence_num++;
|
|
} while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
try a netlogon DatabaseSync2
|
|
*/
|
|
static BOOL test_DatabaseSync2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
|
|
struct creds_CredentialState *creds)
|
|
{
|
|
NTSTATUS status;
|
|
struct netr_DatabaseSync2 r;
|
|
const uint32_t database_ids[] = {0, 1, 2};
|
|
int i;
|
|
BOOL ret = True;
|
|
|
|
r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
|
|
r.in.computername = TEST_MACHINE_NAME;
|
|
r.in.preferredmaximumlength = (uint32_t)-1;
|
|
ZERO_STRUCT(r.in.return_authenticator);
|
|
|
|
for (i=0;i<ARRAY_SIZE(database_ids);i++) {
|
|
r.in.sync_context = 0;
|
|
r.in.database_id = database_ids[i];
|
|
r.in.restart_state = 0;
|
|
|
|
printf("Testing DatabaseSync2 of id %d\n", r.in.database_id);
|
|
|
|
do {
|
|
creds_client_authenticator(creds, &r.in.credential);
|
|
|
|
status = dcerpc_netr_DatabaseSync2(p, mem_ctx, &r);
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
printf("DatabaseSync2 - %s\n", nt_errstr(status));
|
|
ret = False;
|
|
break;
|
|
}
|
|
|
|
if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
|
|
printf("Credential chaining failed\n");
|
|
}
|
|
|
|
r.in.sync_context = r.out.sync_context;
|
|
} while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
BOOL torture_rpc_samsync(void)
|
|
{
|
|
NTSTATUS status;
|
|
TALLOC_CTX *mem_ctx;
|
|
BOOL ret = True;
|
|
struct test_join *join_ctx;
|
|
const char *machine_password;
|
|
const char *binding = lp_parm_string(-1, "torture", "binding");
|
|
struct dcerpc_binding b;
|
|
struct samr_Connect c;
|
|
|
|
struct samsync_state *samsync_state;
|
|
|
|
mem_ctx = talloc_init("torture_rpc_netlogon");
|
|
|
|
join_ctx = torture_join_domain(TEST_MACHINE_NAME, lp_workgroup(), ACB_SVRTRUST,
|
|
&machine_password);
|
|
if (!join_ctx) {
|
|
printf("Failed to join as BDC\n");
|
|
return False;
|
|
}
|
|
|
|
samsync_state = talloc_zero_p(mem_ctx, struct samsync_state);
|
|
|
|
samsync_state->p_samr = torture_join_samr_pipe(join_ctx);
|
|
samsync_state->connect_handle = talloc_zero_p(samsync_state, struct policy_handle);
|
|
c.in.system_name = NULL;
|
|
c.in.access_mask = SEC_RIGHTS_MAXIMUM_ALLOWED;
|
|
c.out.connect_handle = samsync_state->connect_handle;
|
|
|
|
status = dcerpc_samr_Connect(samsync_state->p_samr, mem_ctx, &c);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
printf("samr_Connect failed\n");
|
|
ret = False;
|
|
goto failed;
|
|
}
|
|
|
|
status = dcerpc_parse_binding(mem_ctx, binding, &b);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
printf("Bad binding string %s\n", binding);
|
|
return False;
|
|
goto failed;
|
|
}
|
|
|
|
b.flags &= ~DCERPC_AUTH_OPTIONS;
|
|
b.flags |= DCERPC_SCHANNEL_BDC | DCERPC_SIGN | DCERPC_SCHANNEL_128;
|
|
|
|
status = dcerpc_pipe_connect_b(&samsync_state->p, &b,
|
|
DCERPC_NETLOGON_UUID,
|
|
DCERPC_NETLOGON_VERSION,
|
|
lp_workgroup(),
|
|
TEST_MACHINE_NAME,
|
|
machine_password);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
ret = False;
|
|
goto failed;
|
|
}
|
|
|
|
status = dcerpc_schannel_creds(samsync_state->p->security_state.generic_state, mem_ctx, &samsync_state->creds);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
ret = False;
|
|
}
|
|
|
|
if (!test_DatabaseSync(samsync_state, mem_ctx)) {
|
|
ret = False;
|
|
}
|
|
|
|
if (!test_DatabaseDeltas(samsync_state, mem_ctx)) {
|
|
ret = False;
|
|
}
|
|
|
|
if (!test_DatabaseSync2(samsync_state->p, mem_ctx, samsync_state->creds)) {
|
|
ret = False;
|
|
}
|
|
failed:
|
|
torture_rpc_close(samsync_state->p);
|
|
|
|
torture_leave_domain(join_ctx);
|
|
|
|
talloc_destroy(mem_ctx);
|
|
|
|
return ret;
|
|
}
|