r20149: Remove the smb.conf distinction between PDC and BDC. Now the correct
[jra/samba/.git] / source4 / torture / rpc / samr.c
index 79924b1ac73655400c7447b9b713b96e6261e61d..28dd03e803c4d4cb0c1c0902b830f4a3143a0b25 100644 (file)
@@ -25,7 +25,6 @@
 #include "system/time.h"
 #include "librpc/gen_ndr/lsa.h"
 #include "librpc/gen_ndr/ndr_samr_c.h"
-#include "smb.h"
 #include "lib/crypto/crypto.h"
 #include "libcli/auth/libcli_auth.h"
 #include "libcli/security/security.h"
@@ -160,7 +159,7 @@ static BOOL test_QuerySecurity(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        s.in.sec_info = 7;
        s.in.sdbuf = r.out.sdbuf;
 
-       if (lp_parm_bool(-1, "target", "samba4", False)) {
+       if (lp_parm_bool(-1, "torture", "samba4", False)) {
                printf("skipping SetSecurity test against Samba4\n");
                return True;
        }
@@ -338,6 +337,22 @@ static BOOL test_SetUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        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);
@@ -368,7 +383,7 @@ static BOOL test_SetUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        TEST_USERINFO_INT(21, logon_hours.bits[3], 21, logon_hours.bits[3], 4, 
                          SAMR_FIELD_LOGON_HOURS);
 
-       if (lp_parm_bool(-1, "target", "samba4", False)) {
+       if (lp_parm_bool(-1, "torture", "samba4", False)) {
                printf("skipping Set Account Flag tests against Samba4\n");
                return ret;
        }
@@ -454,6 +469,16 @@ static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len)
        return s;
 }
 
+/*
+  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_str(mem_ctx, len);
+       printf("Generated password '%s'\n", s);
+       return s;
+}
+
 static BOOL test_SetUserPass(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 
                             struct policy_handle *handle, char **password)
 {
@@ -1207,7 +1232,10 @@ static BOOL test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 BOOL test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 
                              const char *account_string,
                              int policy_min_pw_len,
-                             char **password)
+                             char **password,
+                             const char *newpass,
+                             NTTIME last_password_change,
+                             BOOL handle_reject_reason)
 {
        NTSTATUS status;
        struct samr_ChangePasswordUser3 r;
@@ -1216,12 +1244,22 @@ BOOL test_ChangePasswordUser3(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 = samr_rand_pass(mem_ctx, policy_min_pw_len);     
        uint8_t old_nt_hash[16], new_nt_hash[16];
        uint8_t old_lm_hash[16], new_lm_hash[16];
+       NTTIME t;
 
        printf("Testing ChangePasswordUser3\n");
 
+       if (newpass == NULL) {
+               if (policy_min_pw_len == 0) {
+                       newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+               } else {
+                       newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len);
+               }
+       } else {
+               printf("Using password '%s'\n", newpass);
+       }
+
        if (!*password) {
                printf("Failing ChangePasswordUser3 as old password was NULL.  Previous test failed?\n");
                return False;
@@ -1299,25 +1337,80 @@ BOOL test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
        r.in.lm_verifier = &lm_verifier;
        r.in.password3 = NULL;
 
+       unix_to_nt_time(&t, time(NULL));
+
        status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) 
-           && !policy_min_pw_len) {
-               if (r.out.dominfo) {
-                       policy_min_pw_len = r.out.dominfo->min_password_length;
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && 
+          r.out.dominfo && r.out.reject && handle_reject_reason) {
+
+               if (r.out.dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) {
+
+                       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;
+                       }
                }
-               if (policy_min_pw_len) /* try again with the right min password length */ {
-                       ret = test_ChangePasswordUser3(p, mem_ctx, account_string, policy_min_pw_len, password);
-               } else {
-                       printf("ChangePasswordUser3 failed (no min length known) - %s\n", nt_errstr(status));
-                       ret = False;
+
+               /* We tested the order of precendence which is as follows:
+               
+               * pwd min_age 
+               * pwd length
+               * pwd complexity
+               * pwd history
+
+               Guenther */
+
+               if ((r.out.dominfo->min_password_age > 0) && !null_nttime(last_password_change) && 
+                          (last_password_change + r.out.dominfo->min_password_age > t)) {
+
+                       if (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;
+                       }
+
+               } else if ((r.out.dominfo->min_password_length > 0) && 
+                          (strlen(newpass) < r.out.dominfo->min_password_length)) {
+
+                       if (r.out.reject->reason != SAMR_REJECT_TOO_SHORT) {
+                               printf("expected SAMR_REJECT_TOO_SHORT (%d), got %d\n", 
+                                       SAMR_REJECT_TOO_SHORT, r.out.reject->reason);
+                               return False;
+                       }
+
+               } else if (r.out.dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+
+                       if (r.out.reject->reason != SAMR_REJECT_COMPLEXITY) {
+                               printf("expected SAMR_REJECT_COMPLEXITY (%d), got %d\n", 
+                                       SAMR_REJECT_COMPLEXITY, r.out.reject->reason);
+                               return False;
+                       }
+
+               } else if ((r.out.dominfo->password_history_length > 0) && 
+                           strequal(oldpass, newpass)) {
+
+                       if (r.out.reject->reason != SAMR_REJECT_IN_HISTORY) {
+                               printf("expected SAMR_REJECT_IN_HISTORY (%d), got %d\n", 
+                                       SAMR_REJECT_IN_HISTORY, r.out.reject->reason);
+                               return False;
+                       }
+               }
+
+               if (r.out.reject->reason == SAMR_REJECT_TOO_SHORT) {
+                       /* retry with adjusted size */
+                       return test_ChangePasswordUser3(p, mem_ctx, account_string, 
+                                                       r.out.dominfo->min_password_length, 
+                                                       password, NULL, 0, False); 
+
                }
-       } else if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
-               printf("ChangePasswordUser3 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+
        } else if (!NT_STATUS_IS_OK(status)) {
                printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status));
                ret = False;
        } else {
-               *password = newpass;
+               *password = talloc_strdup(mem_ctx, newpass);
        }
 
        return ret;
@@ -1515,7 +1608,7 @@ static BOOL test_user_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                        }       
                
                        /* check it was set right */
-                       if (!test_ChangePasswordUser3(p, user_ctx, base_acct_name, 0, &password)) {
+                       if (!test_ChangePasswordUser3(p, user_ctx, base_acct_name, 0, &password, NULL, 0, False)) {
                                ret = False;
                        }
                }               
@@ -1526,7 +1619,7 @@ static BOOL test_user_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                        }       
                
                        /* check it was set right */
-                       if (!test_ChangePasswordUser3(p, user_ctx, base_acct_name, 0, &password)) {
+                       if (!test_ChangePasswordUser3(p, user_ctx, base_acct_name, 0, &password, NULL, 0, False)) {
                                ret = False;
                        }
                }               
@@ -1569,7 +1662,7 @@ static BOOL test_alias_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                ret = False;
        }
 
-       if (lp_parm_bool(-1, "target", "samba4", False)) {
+       if (lp_parm_bool(-1, "torture", "samba4", False)) {
                printf("skipping MultipleMembers Alias tests against Samba4\n");
                return ret;
        }
@@ -1800,13 +1893,126 @@ static BOOL test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                ret = False;
        }
 
+       /* test what happens when setting the old password again */
+       if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, *password, 0, True)) {
+               ret = False;
+       }
+
+       {
+               char simple_pass[9];
+               char *v = generate_random_str(mem_ctx, 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, mem_ctx, acct_name, 0, password, simple_pass, 0, True)) {
+                       ret = False;
+               }
+       }
+
+       /* set samr_SetDomainInfo level 1 with min_length 5 */
+       {
+               struct samr_QueryDomainInfo r;
+               struct samr_SetDomainInfo s;
+               uint16_t len_old, len;
+               NTSTATUS status;
+
+               len = 3;
+
+               r.in.domain_handle = domain_handle;
+               r.in.level = 1;
+
+               printf("testing samr_QueryDomainInfo level 1\n");
+               status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return False;
+               }
+
+               s.in.domain_handle = domain_handle;
+               s.in.level = 1;
+               s.in.info = r.out.info;
+
+               len_old = s.in.info->info1.min_password_length;
+               s.in.info->info1.min_password_length = len;
+
+               printf("testing samr_SetDomainInfo level 1\n");
+               status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return False;
+               }
+
+               printf("calling test_ChangePasswordUser3 with too short password\n");
+
+               if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, len - 1, password, NULL, 0, True)) {
+                       ret = False;
+               }
+
+               s.in.info->info1.min_password_length = len_old;
+               
+               printf("testing samr_SetDomainInfo level 1\n");
+               status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return False;
+               }
+
+       }
+
+       {
+               NTSTATUS status;
+               struct samr_OpenUser r;
+               struct samr_QueryUserInfo q;
+               struct samr_LookupNames n;
+               struct policy_handle user_handle;
+
+               n.in.domain_handle = domain_handle;
+               n.in.num_names = 1;
+               n.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
+               n.in.names[0].string = acct_name; 
+
+               status = dcerpc_samr_LookupNames(p, mem_ctx, &n);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("LookupNames failed - %s\n", nt_errstr(status));
+                       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;
+
+               status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("OpenUser(%u) failed - %s\n", n.out.rids.ids[0], nt_errstr(status));
+                       return False;
+               }
+
+               q.in.user_handle = &user_handle;
+               q.in.level = 5;
+
+               status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("QueryUserInfo failed - %s\n", nt_errstr(status));
+                       return False;
+               }
+
+               printf("calling test_ChangePasswordUser3 with too early password change\n");
+
+               if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 
+                                             q.out.info->info5.last_password_change, True)) {
+                       ret = False;
+               }
+       }
+
+       return True;
+
        /* we change passwords twice - this has the effect of verifying
           they were changed correctly for the final call */
-       if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password)) {
+       if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, True)) {
                ret = False;
        }
 
-       if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password)) {
+       if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, True)) {
                ret = False;
        }
 
@@ -2781,6 +2987,16 @@ static BOOL test_QueryDomainInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                                       levels[i], r.out.info->info2.comment.string, domain_comment);
                                ret = False;
                        }
+                       if (!r.out.info->info2.primary.string) {
+                               printf("QueryDomainInfo level %u returned no PDC name\n",
+                                      levels[i]);
+                               ret = False;
+                       } else if (r.out.info->info2.role == SAMR_ROLE_DOMAIN_PDC) {
+                               if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), r.out.info->info2.primary.string) != 0) {
+                                       printf("QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n",
+                                              levels[i], r.out.info->info2.primary.string, dcerpc_server_name(p));
+                               }
+                       }
                        break;
                case 4:
                        if (strcmp(r.out.info->info4.comment.string, domain_comment) != 0) {
@@ -2789,6 +3005,13 @@ static BOOL test_QueryDomainInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                                ret = False;
                        }
                        break;
+               case 6:
+                       if (!r.out.info->info6.primary.string) {
+                               printf("QueryDomainInfo level %u returned no PDC name\n",
+                                      levels[i]);
+                               ret = False;
+                       }
+                       break;
                case 11:
                        if (strcmp(r.out.info->info11.info2.comment.string, domain_comment) != 0) {
                                printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n",
@@ -3108,7 +3331,7 @@ static BOOL test_AddGroupMember(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                return False;
        }
 
-       if (lp_parm_bool(-1, "target", "samba4", False)) {
+       if (lp_parm_bool(-1, "torture", "samba4", False)) {
                printf("skipping SetMemberAttributesOfGroup test against Samba4\n");
        } else {
                /* this one is quite strange. I am using random inputs in the
@@ -3296,7 +3519,7 @@ static BOOL test_OpenDomain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
                ret &= test_QueryDisplayInfo3(p, mem_ctx, &domain_handle);
                ret &= test_QueryDisplayInfo_continue(p, mem_ctx, &domain_handle);
                
-               if (lp_parm_bool(-1, "target", "samba4", False)) {
+               if (lp_parm_bool(-1, "torture", "samba4", False)) {
                        printf("skipping GetDisplayEnumerationIndex test against Samba4\n");
                } else {
                        ret &= test_GetDisplayEnumerationIndex(p, mem_ctx, &domain_handle);