r3716: Improvements in the RPC-SAMSYNC tests:
authorAndrew Bartlett <abartlet@samba.org>
Sat, 13 Nov 2004 03:57:54 +0000 (03:57 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:05:46 +0000 (13:05 -0500)
 We now (for the first time) start to parse the 'user sensitive info'
 field, which reveals the user's NT and LM passwords from Win2k3.

 Using this, the 'validate samsync against netlogon' portion of the
 tests works for accounts.

 Trusted domains and secrets are now retreived, but like users,
 require further cross-validation work.

Andrew Bartlett

source/librpc/idl/netlogon.idl
source/torture/rpc/samsync.c

index f55049d30e1c743fc542b7b6ec28791195372ddc..37c6949fadf86fbaf663d1268d363d14f75303e0 100644 (file)
@@ -302,9 +302,33 @@ interface netlogon
                uint32 unknown8;
        } 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;
index 8bb6ec2ec67ef06ded97da4295fb957bc6bf5284..aaab3ef988cb1c6c085e0784bec72f06f47789f8 100644 (file)
@@ -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;