diff options
-rw-r--r-- | source4/librpc/idl/netlogon.idl | 42 | ||||
-rw-r--r-- | source4/torture/rpc/samsync.c | 169 |
2 files changed, 195 insertions, 16 deletions
diff --git a/source4/librpc/idl/netlogon.idl b/source4/librpc/idl/netlogon.idl index f55049d30e..37c6949fad 100644 --- a/source4/librpc/idl/netlogon.idl +++ b/source4/librpc/idl/netlogon.idl @@ -303,8 +303,32 @@ interface netlogon } netr_DELTA_DELETE_USER; typedef struct { + uint16 length; + [value(r->length)] uint16 size; + uint32 flags; + samr_Password pwd; + } netr_USER_KEY16; + + typedef struct { + netr_USER_KEY16 lmpassword; + netr_USER_KEY16 ntpassword; + + } netr_USER_KEYS2; + + typedef struct { + netr_USER_KEYS2 keys2; + } netr_USER_KEY_UNION; + + typedef [public] struct { + uint32 version; + netr_USER_KEY_UNION keys; + } netr_USER_KEYS; + + typedef struct { bool8 SensitiveDataFlag; uint32 DataLength; + + /* netr_USER_KEYS encrypted with the session key */ [size_is(DataLength)] uint8 *SensitiveData; } netr_USER_PRIVATE_INFO; @@ -349,7 +373,7 @@ interface netlogon } netr_DELTA_USER; typedef struct { - netr_String DomainName; + netr_String domain_name; netr_String OEMInfo; NTTIME forcedlogoff; uint16 min_password_len; @@ -465,7 +489,7 @@ interface netlogon } netr_DELTA_POLICY; typedef struct { - netr_String DomainName; + netr_String domain_name; uint32 num_controllers; [size_is(num_controllers)] netr_String *controller_names; uint32 SecurityInformation; @@ -478,7 +502,7 @@ interface netlogon uint32 unknown6; uint32 unknown7; uint32 unknown8; - } netr_DELTA_TRUSTED_DOMAINS; + } netr_DELTA_TRUSTED_DOMAIN; typedef struct { uint16 unknown; @@ -548,7 +572,7 @@ interface netlogon NETR_DELTA_RENAME_ALIAS = 11, NETR_DELTA_ALIAS_MEMBER = 12, NETR_DELTA_POLICY = 13, - NETR_DELTA_TRUSTED_DOMAINS = 14, + NETR_DELTA_TRUSTED_DOMAIN = 14, NETR_DELTA_DELETE_TRUST = 15, NETR_DELTA_ACCOUNTS = 16, NETR_DELTA_DELETE_ACCOUNT = 17, @@ -573,12 +597,12 @@ interface netlogon [case(NETR_DELTA_RENAME_ALIAS)] netr_DELTA_RENAME *rename_alias; [case(NETR_DELTA_ALIAS_MEMBER)] netr_DELTA_ALIAS_MEMBER *alias_member; [case(NETR_DELTA_POLICY)] netr_DELTA_POLICY *policy; - [case(NETR_DELTA_TRUSTED_DOMAINS)] netr_DELTA_TRUSTED_DOMAINS *trusted_domains; - [case(NETR_DELTA_DELETE_TRUST)] netr_DELTA_DELETE_TRUST delete_trust; + [case(NETR_DELTA_TRUSTED_DOMAIN)] netr_DELTA_TRUSTED_DOMAIN *trusted_domain; + [case(NETR_DELTA_DELETE_TRUST)] netr_DELTA_DELETE_TRUST delete_trust; [case(NETR_DELTA_ACCOUNTS)] netr_DELTA_ACCOUNTS *accounts; - [case(NETR_DELTA_DELETE_ACCOUNT)] netr_DELTA_DELETE_ACCOUNT delete_account; + [case(NETR_DELTA_DELETE_ACCOUNT)] netr_DELTA_DELETE_ACCOUNT delete_account; [case(NETR_DELTA_SECRET)] netr_DELTA_SECRET *secret; - [case(NETR_DELTA_DELETE_SECRET)] netr_DELTA_DELETE_SECRET delete_secret; + [case(NETR_DELTA_DELETE_SECRET)] netr_DELTA_DELETE_SECRET delete_secret; [case(NETR_DELTA_DELETE_GROUP2)] netr_DELTA_DELETE_USER *delete_group; [case(NETR_DELTA_DELETE_USER2)] netr_DELTA_DELETE_USER *delete_user; [case(NETR_DELTA_MODIFY_COUNT)] uint64 *modified_count; @@ -598,7 +622,7 @@ interface netlogon [case(NETR_DELTA_RENAME_ALIAS)] uint32 rid; [case(NETR_DELTA_ALIAS_MEMBER)] uint32 rid; [case(NETR_DELTA_POLICY)] dom_sid2 *sid; - [case(NETR_DELTA_TRUSTED_DOMAINS)] dom_sid2 *sid; + [case(NETR_DELTA_TRUSTED_DOMAIN)] dom_sid2 *sid; [case(NETR_DELTA_DELETE_TRUST)] dom_sid2 *sid; [case(NETR_DELTA_ACCOUNTS)] dom_sid2 *sid; [case(NETR_DELTA_DELETE_ACCOUNT)] dom_sid2 *sid; diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c index 8bb6ec2ec6..aaab3ef988 100644 --- a/source4/torture/rpc/samsync.c +++ b/source4/torture/rpc/samsync.c @@ -25,6 +25,8 @@ #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" @@ -55,12 +57,18 @@ static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 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)); @@ -81,7 +89,9 @@ static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, printf("Credential chaining failed\n"); } - *info3 = r.out.validation.sam3; + if (info3) { + *info3 = r.out.validation.sam3; + } return status; } @@ -89,6 +99,20 @@ static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct samsync_state { uint64_t seq_num; char *domain_name; + struct samsync_secret *secrets; + struct samsync_trusted_domain *trusted_domains; +}; + +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 samsync_state *samsync_state; @@ -103,7 +127,7 @@ static BOOL samsync_handle_domain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, domain->sequence_num; samsync_state[database_id].domain_name = - talloc_reference(samsync_state, domain->DomainName.string); + talloc_reference(samsync_state, domain->domain_name.string); printf("\tsequence_nums[%d/%s]=%llu\n", database_id, samsync_state[database_id].domain_name, @@ -120,6 +144,8 @@ static BOOL samsync_handle_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 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[database_id].domain_name ? samsync_state[database_id].domain_name : lp_workgroup(); @@ -130,22 +156,53 @@ static BOOL samsync_handle_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, if (user->lmpassword_present) { sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0); + lm_hash_p = &lm_hash; } if (user->ntpassword_present) { sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0); + nt_hash_p = &nt_hash; } - if (!user->lmpassword_present && !user->lmpassword_present) { + 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(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)) { + return False; + } + if (keys.keys.keys2.lmpassword.length == 16) { + sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0); + dump_data(0, keys.keys.keys2.lmpassword.pwd.hash, + sizeof(keys.keys.keys2.lmpassword.pwd.hash)); + + 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); + dump_data(0, keys.keys.keys2.ntpassword.pwd.hash, + sizeof(keys.keys.keys2.ntpassword.pwd.hash)); + 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(p, mem_ctx, creds, domain, username, - user->lmpassword_present ? &lm_hash : NULL, - user->ntpassword_present ? &nt_hash : NULL, + lm_hash_p, + nt_hash_p, &info3); if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) { @@ -178,7 +235,7 @@ static BOOL samsync_handle_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, user->acct_flags, info3->base.acct_flags); return False; } - if (strcmp(user->full_name.string, info3->base.full_name.string) != 0) { + if (strcmp_safe(user->full_name.string, info3->base.full_name.string) != 0) { printf("Full name mismatch: %s != %s\n", user->full_name.string, info3->base.full_name.string); return False; @@ -192,6 +249,44 @@ static BOOL samsync_handle_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, return False; } +static BOOL samsync_handle_secret(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + 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(creds, secret->current_cipher.cipher_data, + secret->current_cipher.maxlen); + + creds_arcfour_crypt(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[database_id].secrets, new); + + return True; +} + +static BOOL samsync_handle_trusted_domain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + 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[database_id].trusted_domains, new); + + return True; +} + /* we remember the sequence numbers so we can easily do a DatabaseDelta */ static uint64_t sequence_nums[3]; @@ -206,6 +301,8 @@ static BOOL test_DatabaseSync(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 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; samsync_state = talloc_zero_array_p(mem_ctx, struct samsync_state, 3); @@ -247,9 +344,67 @@ static BOOL test_DatabaseSync(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, ret &= samsync_handle_user(p, mem_ctx, creds, r.in.database_id, &r.out.delta_enum_array->delta_enum[d]); break; + case NETR_DELTA_TRUSTED_DOMAIN: + ret &= samsync_handle_trusted_domain(p, mem_ctx, creds, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d]); + break; + case NETR_DELTA_SECRET: + ret &= samsync_handle_secret(p, mem_ctx, creds, + r.in.database_id, &r.out.delta_enum_array->delta_enum[d]); + break; } } } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + for (t=samsync_state[r.in.database_id].trusted_domains; t; t=t->next) { + const char *domain = samsync_state[r.in.database_id].domain_name + ? samsync_state[r.in.database_id].domain_name + : lp_workgroup(); + char *username = talloc_asprintf(mem_ctx, "%s$", domain); + char *secret_name = talloc_asprintf(mem_ctx, "G$$%s", t->name); + for (s=samsync_state[r.in.database_id].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(p, mem_ctx, 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(p, mem_ctx, 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; + } + } + talloc_free(secret_name); + talloc_free(username); + } } return ret; |