r24611: Following up on the re-opening of bug 4817 is it pretty clear that
authorAndrew Bartlett <abartlet@samba.org>
Wed, 22 Aug 2007 04:28:15 +0000 (04:28 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 20:02:23 +0000 (15:02 -0500)
machine accounts are not subject to password policy in Win2k3 R2 (at
least in terms of password quality).

In testing this, I found that Win2k3 R2 has changed the way the old
ChangePassword RPC call is handled - the 'cross-checks' between new LM
and NT passwords are not required.

Andrew Bartlett

source/dsdb/samdb/samdb.c
source/rpc_server/dcerpc_server.c
source/rpc_server/netlogon/dcerpc_netlogon.c
source/rpc_server/samr/samr_password.c
source/torture/rpc/samr.c

index 148be885328569dc001e668298d62812e5d044e1..7a20ea8665f73879c9114140ed2b232596591553 100644 (file)
@@ -1515,7 +1515,6 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
                            struct samr_Password *lmNewHash, 
                            struct samr_Password *ntNewHash,
                            BOOL user_change,
-                           BOOL restrictions,
                            enum samr_RejectReason *reject_reason,
                            struct samr_DomInfo1 **_dominfo)
 {
@@ -1536,6 +1535,7 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
        int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
        struct dom_sid *domain_sid;
        struct ldb_message **res;
+       BOOL restrictions;
        int count;
        time_t now = time(NULL);
        NTTIME now_nt;
@@ -1558,6 +1558,13 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
        ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "unicodePwd");
        pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
 
+       /* Only non-trust accounts have restrictions (possibly this
+        * test is the wrong way around, but I like to be restrictive
+        * if possible */
+       restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT
+                                              |UF_WORKSTATION_TRUST_ACCOUNT
+                                              |UF_SERVER_TRUST_ACCOUNT)); 
+
        if (domain_dn) {
                /* pull the domain parameters */
                count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
@@ -1605,7 +1612,8 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
                *_dominfo = dominfo;
        }
 
-       if (new_pass) {
+       if (restrictions && new_pass) {
+
                /* check the various password restrictions */
                if (restrictions && minPwdLength > strlen_m(new_pass)) {
                        if (reject_reason) {
@@ -1637,7 +1645,7 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
                ntNewHash = &local_ntNewHash;
        }
 
-       if (restrictions && user_change) {
+       if (user_change) {
                /* are all password changes disallowed? */
                if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
                        if (reject_reason) {
@@ -1745,7 +1753,6 @@ _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *me
                                struct samr_Password *lmNewHash, 
                                struct samr_Password *ntNewHash,
                                BOOL user_change,
-                               BOOL restrictions,
                                enum samr_RejectReason *reject_reason,
                                struct samr_DomInfo1 **_dominfo) 
 {
@@ -1787,7 +1794,6 @@ _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *me
                                       msg, new_pass, 
                                       lmNewHash, ntNewHash,
                                       user_change, /* This is a password set, not change */
-                                      restrictions, /* run restriction tests */
                                       reject_reason, _dominfo);
        if (!NT_STATUS_IS_OK(nt_status)) {
                ldb_transaction_cancel(ctx);
index 35b37b3af6c070372b19e513d091c0cbe34162be..7d257fac063e6ff47fe825fb2470a3cc055a54b7 100644 (file)
@@ -528,10 +528,6 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        uint32_t context_id;
        const struct dcesrv_interface *iface;
 
-       if (call->pkt.u.bind.assoc_group_id != 0) {
-               return dcesrv_bind_nak(call, 0);        
-       }
-
        if (call->pkt.u.bind.num_contexts < 1 ||
            call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
                return dcesrv_bind_nak(call, 0);
index 5a2fd7a07fdd5a4c49610a9f7a9898b515402c2c..2198dc5ebc303d34cd87c09350d5e88d68420687 100644 (file)
@@ -312,7 +312,6 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
                                           NULL, /* Don't have plaintext */
                                           NULL, &r->in.new_password,
                                           False, /* This is not considered a password change */
-                                          False, /* don't restrict this password change (match w2k3) */
                                           NULL, NULL);
        return nt_status;
 }
@@ -360,7 +359,6 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
                                           new_pass, /* we have plaintext */
                                           NULL, NULL,
                                           False, /* This is not considered a password change */
-                                          False, /* don't restrict this password change (match w2k3) */
                                           NULL, NULL);
        return nt_status;
 }
index fac0015f0e67c8d897e59de7a44bea20bc7dd9a9..e3cb70ad175d4354327f0796fb4e9e9713e520c5 100644 (file)
@@ -62,12 +62,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALL
                   present */
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
-       if (!r->in.cross1_present || !r->in.nt_cross) {
-               return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
-       }
-       if (!r->in.cross2_present || !r->in.lm_cross) {
-               return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
-       }
 
        /* To change a password we need to open as system */
        sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
@@ -112,18 +106,24 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALL
                return NT_STATUS_WRONG_PASSWORD;
        }
        
-       /* check the nt cross hash */
-       D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
-       if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
-               return NT_STATUS_WRONG_PASSWORD;
+       /* The NT Cross is not required by Win2k3 R2, but if present
+          check the nt cross hash */
+       if (r->in.cross1_present && r->in.nt_cross) {
+               D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
+               if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
+                       ldb_transaction_cancel(sam_ctx);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
        }
 
-       /* check the lm cross hash */
-       D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
-       if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
-               return NT_STATUS_WRONG_PASSWORD;
+       /* The LM Cross is not required by Win2k3 R2, but if present
+          check the lm cross hash */
+       if (r->in.cross2_present && r->in.lm_cross) {
+               D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
+               if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
+                       ldb_transaction_cancel(sam_ctx);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
        }
 
        msg = ldb_msg_new(mem_ctx);
@@ -144,7 +144,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALL
                                    a_state->account_dn, a_state->domain_state->domain_dn,
                                    msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
                                    True, /* this is a user password change */
-                                   True, /* run restriction tests */
                                    NULL,
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
@@ -196,7 +195,11 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        struct samr_Password lm_verifier;
 
        if (pwbuf == NULL) {
-               return NT_STATUS_WRONG_PASSWORD;
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (r->in.hash == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        /* To change a password we need to open as system */
@@ -245,7 +248,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        }
 
        /* check LM verifier */
-       if (lm_pwd == NULL || r->in.hash == NULL) {
+       if (lm_pwd == NULL) {
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
@@ -276,7 +279,6 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                    mod, new_pass, 
                                    NULL, NULL,
                                    True, /* this is a user password change */
-                                   True, /* run restriction tests */
                                    NULL, 
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
@@ -430,7 +432,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                                    mod, new_pass, 
                                    NULL, NULL,
                                    True, /* this is a user password change */
-                                   True, /* run restriction tests */
                                    &reason, 
                                    &dominfo);
        if (!NT_STATUS_IS_OK(status)) {
@@ -539,7 +540,6 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
                                  msg, new_pass, 
                                  NULL, NULL,
                                  False, /* This is a password set, not change */
-                                 True, /* run restriction tests */
                                  NULL, NULL);
 }
 
@@ -593,7 +593,6 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
                                  msg, new_pass, 
                                  NULL, NULL,
                                  False, /* This is a password set, not change */
-                                 True, /* run restriction tests */
                                  NULL, NULL);
 }
 
index 8e765fe027f97881bd197abb9638e950a717d0e1..a07a39e07863b15b63a991f4f3f180abcc77a40d 100644 (file)
@@ -616,7 +616,8 @@ static BOOL test_SetUserPass_23(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
 
 static BOOL test_SetUserPassEx(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 
-                              struct policy_handle *handle, char **password)
+                              struct policy_handle *handle, bool makeshort, 
+                              char **password)
 {
        NTSTATUS status;
        struct samr_SetUserInfo s;
@@ -635,7 +636,11 @@ static BOOL test_SetUserPassEx(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        if (NT_STATUS_IS_OK(status)) {
                policy_min_pw_len = pwp.out.info.min_password_length;
        }
-       newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+       if (makeshort && policy_min_pw_len) {
+               newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len - 1);
+       } else {
+               newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+       }
 
        s.in.user_handle = handle;
        s.in.info = &u;
@@ -682,7 +687,7 @@ static BOOL test_SetUserPassEx(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
        status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+               printf("SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n",
                       s.in.level, nt_errstr(status));
                ret = False;
        } else {
@@ -1110,6 +1115,8 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
        r.in.user_handle = &user_handle;
        r.in.lm_present = 1;
+       /* Break the LM hash */
+       hash1.hash[0]++;
        r.in.old_lm_crypted = &hash1;
        r.in.new_lm_crypted = &hash2;
        r.in.nt_present = 1;
@@ -1117,38 +1124,43 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        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;
+       r.in.cross2_present = 1;
+       r.in.lm_cross = &hash6;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status));
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash, got %s\n", nt_errstr(status));
                ret = False;
        }
 
-       
+       /* Unbreak the LM hash */
+       hash1.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 NT hash */
+       hash3.hash[0]--;
        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.cross1_present = 1;
+       r.in.nt_cross = &hash5;
        r.in.cross2_present = 1;
        r.in.lm_cross = &hash6;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status));
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash, got %s\n", nt_errstr(status));
                ret = False;
        }
 
+       /* Unbreak the NT hash */
+       hash3.hash[0]--;
+
        r.in.user_handle = &user_handle;
        r.in.lm_present = 1;
-       /* Break the LM hash */
-       hash1.hash[0]++;
        r.in.old_lm_crypted = &hash1;
        r.in.new_lm_crypted = &hash2;
        r.in.nt_present = 1;
@@ -1157,39 +1169,50 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        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;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash, got %s\n", nt_errstr(status));
+               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM cross-hash, got %s\n", nt_errstr(status));
                ret = False;
        }
 
-       /* Unbreak the LM hash */
-       hash1.hash[0]--;
+       /* 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;
-       /* Break the NT hash */
-       hash3.hash[0]--;
        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;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash, got %s\n", nt_errstr(status));
+               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT cross-hash, got %s\n", nt_errstr(status));
                ret = False;
        }
 
-       /* Unbreak the NT hash */
-       hash3.hash[0]--;
+       /* 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;
@@ -1200,19 +1223,34 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        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;
+       r.in.cross2_present = 0;
+       r.in.lm_cross = NULL;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM cross-hash, got %s\n", nt_errstr(status));
+       if (NT_STATUS_IS_OK(status)) {
+               changed = True;
+               *password = newpass;
+       } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+               printf("ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(status));
                ret = False;
        }
 
-       /* Unbreak the LM cross */
-       hash6.hash[0]--;
+       oldpass = newpass;
+       newpass = samr_rand_pass(mem_ctx, 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;
@@ -1221,21 +1259,28 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        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.cross1_present = 0;
+       r.in.nt_cross = NULL;
        r.in.cross2_present = 1;
        r.in.lm_cross = &hash6;
 
        status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT cross-hash, got %s\n", nt_errstr(status));
+       if (NT_STATUS_IS_OK(status)) {
+               changed = True;
+               *password = newpass;
+       } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+               printf("ChangePasswordUser failed: expected NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status));
                ret = False;
        }
 
-       /* Unbreak the NT cross */
-       hash5.hash[0]--;
+       oldpass = newpass;
+       newpass = samr_rand_pass(mem_ctx, 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);
@@ -1282,12 +1327,15 @@ static BOOL test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
        if (changed) {
                status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+                       printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+               } else if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
                        printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(status));
                        ret = False;
                }
        }
 
+       
        if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
                ret = False;
        }
@@ -1394,8 +1442,20 @@ static BOOL test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_c
        status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
 
        if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
-           && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n",
+           && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+               printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n",
+                       nt_errstr(status));
+               ret = False;
+       }
+
+       /* This shouldn't be a valid name */
+       account_bad.string = TEST_ACCOUNT_NAME "XX";
+       r.in.account = &account_bad;
+
+       status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+               printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n",
                        nt_errstr(status));
                ret = False;
        }
@@ -1403,6 +1463,8 @@ static BOOL test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_c
        /* 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;
 
        status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
 
@@ -1412,6 +1474,20 @@ static BOOL test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_c
                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;
+
+       status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+               printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n",
+                       nt_errstr(status));
+               ret = False;
+       }
+
        E_deshash(oldpass, old_lm_hash);
        E_deshash(newpass, new_lm_hash);
 
@@ -1440,7 +1516,8 @@ static BOOL test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_c
 
 static BOOL test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 
                                     const char *acct_name,
-                                    struct policy_handle *handle, char **password)
+                                    char **password,
+                                    char *newpass, bool allow_password_restriction)
 {
        NTSTATUS status;
        struct samr_ChangePasswordUser2 r;
@@ -1449,20 +1526,17 @@ static BOOL test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        struct samr_CryptPassword nt_pass, lm_pass;
        struct samr_Password nt_verifier, lm_verifier;
        char *oldpass;
-       char *newpass;
        uint8_t old_nt_hash[16], new_nt_hash[16];
        uint8_t old_lm_hash[16], new_lm_hash[16];
 
        struct samr_GetDomPwInfo dom_pw_info;
-       int policy_min_pw_len = 0;
 
        struct lsa_String domain_name;
 
-
        domain_name.string = "";
        dom_pw_info.in.domain_name = &domain_name;
 
-       printf("Testing ChangePasswordUser2\n");
+       printf("Testing ChangePasswordUser2 on %s\n", acct_name);
 
        if (!*password) {
                printf("Failing ChangePasswordUser3 as old password was NULL.  Previous test failed?\n");
@@ -1470,12 +1544,15 @@ static BOOL test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        }
        oldpass = *password;
 
-       status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info);
-       if (NT_STATUS_IS_OK(status)) {
-               policy_min_pw_len = dom_pw_info.out.info.min_password_length;
-       }
+       if (!newpass) {
+               int policy_min_pw_len = 0;
+               status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info);
+               if (NT_STATUS_IS_OK(status)) {
+                       policy_min_pw_len = dom_pw_info.out.info.min_password_length;
+               }
 
-       newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+               newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+       } 
 
        server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
        init_lsa_String(&account, acct_name);
@@ -1503,7 +1580,7 @@ static BOOL test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        r.in.lm_verifier = &lm_verifier;
 
        status = dcerpc_samr_ChangePasswordUser2(p, mem_ctx, &r);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+       if (allow_password_restriction && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
                printf("ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
        } else if (!NT_STATUS_IS_OK(status)) {
                printf("ChangePasswordUser2 failed - %s\n", nt_errstr(status));
@@ -1659,9 +1736,11 @@ BOOL test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
        status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
 
-       if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && 
-          r.out.dominfo && r.out.reject && handle_reject_reason) {
-
+       if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+           && r.out.dominfo
+           && r.out.reject
+           && handle_reject_reason
+           && (!null_nttime(last_password_change) || !r.out.dominfo->min_password_age)) {
                if (r.out.dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) {
 
                        if (r.out.reject && (r.out.reject->reason != SAMR_REJECT_OTHER)) {
@@ -1724,6 +1803,14 @@ BOOL test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
                }
 
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+               if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) {
+                       printf("expected SAMR_REJECT_OTHER (%d), got %d\n", 
+                              SAMR_REJECT_OTHER, r.out.reject->reason);
+                       return False;
+               }
+               /* Perhaps the server has a 'min password age' set? */
+
        } else if (!NT_STATUS_IS_OK(status)) {
                printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status));
                ret = False;
@@ -1920,6 +2007,36 @@ static BOOL test_user_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                }
                break;
        case TORTURE_SAMR_PASSWORDS:
+               if (base_acct_flags & (ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST)) {
+                       char simple_pass[9];
+                       char *v = generate_random_str(mem_ctx, 1);
+                       
+                       ZERO_STRUCT(simple_pass);
+                       memset(simple_pass, *v, sizeof(simple_pass) - 1);
+
+                       printf("Testing machine account password policy rules\n");
+
+                       /* Workstation trust accounts don't seem to need to honour password quality policy */
+                       if (!test_SetUserPassEx(p, user_ctx, user_handle, true, &password)) {
+                               ret = False;
+                       }
+
+                       if (!test_ChangePasswordUser2(p, user_ctx, base_acct_name, &password, simple_pass, False)) {
+                               ret = False;
+                       }
+
+                       /* reset again, to allow another 'user' password change */
+                       if (!test_SetUserPassEx(p, user_ctx, user_handle, true, &password)) {
+                               ret = False;
+                       }
+
+                       /* Try a 'short' password */
+                       if (!test_ChangePasswordUser2(p, user_ctx, base_acct_name, &password, samr_rand_pass(mem_ctx, 4), False)) {
+                               ret = False;
+                       }
+                       
+               }
+               
                for (i = 0; password_fields[i]; i++) {
                        if (!test_SetUserPass_23(p, user_ctx, user_handle, password_fields[i], &password)) {
                                ret = False;
@@ -1942,13 +2059,14 @@ static BOOL test_user_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                        }
                }               
 
-               if (!test_SetUserPassEx(p, user_ctx, user_handle, &password)) {
+               if (!test_SetUserPassEx(p, user_ctx, user_handle, false, &password)) {
                        ret = False;
                }       
 
                if (!test_ChangePassword(p, user_ctx, base_acct_name, domain_handle, &password)) {
                        ret = False;
                }       
+
                break;
        case TORTURE_SAMR_OTHER:
                /* We just need the account to exist */
@@ -2203,7 +2321,7 @@ static BOOL test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                ret = False;
        }
 
-       if (!test_ChangePasswordUser2(p, mem_ctx, acct_name, domain_handle, password)) {
+       if (!test_ChangePasswordUser2(p, mem_ctx, acct_name, password, 0, True)) {
                ret = False;
        }
 
@@ -2235,6 +2353,7 @@ static BOOL test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                struct samr_SetDomainInfo s;
                uint16_t len_old, len;
                uint32_t pwd_prop_old;
+               int64_t min_pwd_age_old;
                NTSTATUS status;
 
                len = 5;
@@ -2259,6 +2378,9 @@ static BOOL test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                /* 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;
+
                printf("testing samr_SetDomainInfo level 1\n");
                status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
                if (!NT_STATUS_IS_OK(status)) {
@@ -2273,6 +2395,7 @@ static BOOL test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
                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;
                
                printf("testing samr_SetDomainInfo level 1\n");
                status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);