s4-smbtorture: exclude oem_information checking for s3 in RPC-SAMR.
[ira/wip.git] / source4 / torture / rpc / samr.c
index 3bc98b6315ef1972ff1f7deda3f1a9c798c4fdef..b5eb0e24344fc2e5c629a3bcf9b48f01facd967a 100644 (file)
@@ -48,7 +48,10 @@ enum torture_samr_choice {
        TORTURE_SAMR_PASSWORDS_PWDLASTSET,
        TORTURE_SAMR_USER_ATTRIBUTES,
        TORTURE_SAMR_USER_PRIVILEGES,
-       TORTURE_SAMR_OTHER
+       TORTURE_SAMR_OTHER,
+       TORTURE_SAMR_MANY_ACCOUNTS,
+       TORTURE_SAMR_MANY_GROUPS,
+       TORTURE_SAMR_MANY_ALIASES
 };
 
 static bool test_QueryUserInfo(struct dcerpc_pipe *p,
@@ -2670,21 +2673,20 @@ static bool test_QueryUserInfo_pwdlastset(struct dcerpc_pipe *p,
        return true;
 }
 
-static bool test_SamLogon_Creds(struct dcerpc_pipe *p, struct torture_context *tctx,
-                               struct cli_credentials *machine_credentials,
-                               struct cli_credentials *test_credentials,
-                               struct netlogon_creds_CredentialState *creds,
-                               NTSTATUS expected_result)
+static bool test_SamLogon(struct torture_context *tctx,
+                         struct dcerpc_pipe *p,
+                         struct cli_credentials *test_credentials,
+                         NTSTATUS expected_result)
 {
        NTSTATUS status;
-       struct netr_LogonSamLogon r;
-       struct netr_Authenticator auth, auth2;
+       struct netr_LogonSamLogonEx r;
        union netr_LogonLevel logon;
        union netr_Validation validation;
        uint8_t authoritative;
        struct netr_NetworkInfo ninfo;
        DATA_BLOB names_blob, chal, lm_resp, nt_resp;
        int flags = CLI_CRED_NTLM_AUTH;
+       uint32_t samlogon_flags = 0;
 
        if (lp_client_lanman_auth(tctx->lp_ctx)) {
                flags |= CLI_CRED_LANMAN_AUTH;
@@ -2703,8 +2705,8 @@ static bool test_SamLogon_Creds(struct dcerpc_pipe *p, struct torture_context *t
        chal = data_blob_const(ninfo.challenge,
                               sizeof(ninfo.challenge));
 
-       names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(machine_credentials),
-                                               cli_credentials_get_domain(machine_credentials));
+       names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials),
+                                               cli_credentials_get_domain(test_credentials));
 
        status = cli_credentials_get_ntlm_response(test_credentials, tctx,
                                                   &flags,
@@ -2725,56 +2727,38 @@ static bool test_SamLogon_Creds(struct dcerpc_pipe *p, struct torture_context *t
                MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
        ninfo.identity_info.logon_id_low = 0;
        ninfo.identity_info.logon_id_high = 0;
-       ninfo.identity_info.workstation.string = cli_credentials_get_workstation(machine_credentials);
+       ninfo.identity_info.workstation.string = cli_credentials_get_workstation(test_credentials);
 
        logon.network = &ninfo;
 
        r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-       r.in.computer_name = cli_credentials_get_workstation(machine_credentials);
-       r.in.credential = &auth;
-       r.in.return_authenticator = &auth2;
-       r.in.logon_level = 2;
+       r.in.computer_name = cli_credentials_get_workstation(test_credentials);
+       r.in.logon_level = NetlogonNetworkInformation;
        r.in.logon = &logon;
+       r.in.flags = &samlogon_flags;
+       r.out.flags = &samlogon_flags;
        r.out.validation = &validation;
        r.out.authoritative = &authoritative;
 
        d_printf("Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string);
 
-       ZERO_STRUCT(auth2);
-       netlogon_creds_client_authenticator(creds, &auth);
-
-       r.in.validation_level = 2;
+       r.in.validation_level = 6;
 
-       status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+       status = dcerpc_netr_LogonSamLogonEx(p, tctx, &r);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+               r.in.validation_level = 3;
+               status = dcerpc_netr_LogonSamLogonEx(p, tctx, &r);
+       }
        if (!NT_STATUS_IS_OK(status)) {
-               torture_assert_ntstatus_equal(tctx, status, expected_result, "LogonSamLogon failed");
+               torture_assert_ntstatus_equal(tctx, status, expected_result, "LogonSamLogonEx failed");
                return true;
        } else {
-               torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+               torture_assert_ntstatus_ok(tctx, status, "LogonSamLogonEx failed");
        }
 
-       torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred),
-                       "Credential chaining failed");
-
        return true;
 }
 
-static bool test_SamLogon(struct torture_context *tctx,
-                         struct dcerpc_pipe *p,
-                         struct cli_credentials *machine_credentials,
-                         struct cli_credentials *test_credentials,
-                         NTSTATUS expected_result)
-{
-       struct netlogon_creds_CredentialState *creds;
-
-       if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
-               return false;
-       }
-
-       return test_SamLogon_Creds(p, tctx, machine_credentials, test_credentials,
-                                  creds, expected_result);
-}
-
 static bool test_SamLogon_with_creds(struct torture_context *tctx,
                                     struct dcerpc_pipe *p,
                                     struct cli_credentials *machine_creds,
@@ -2788,19 +2772,18 @@ static bool test_SamLogon_with_creds(struct torture_context *tctx,
        test_credentials = cli_credentials_init(tctx);
 
        cli_credentials_set_workstation(test_credentials,
-                                       TEST_ACCOUNT_NAME_PWD, CRED_SPECIFIED);
+                                       cli_credentials_get_workstation(machine_creds), CRED_SPECIFIED);
        cli_credentials_set_domain(test_credentials,
-                                  lp_workgroup(tctx->lp_ctx), CRED_SPECIFIED);
+                                  cli_credentials_get_domain(machine_creds), CRED_SPECIFIED);
        cli_credentials_set_username(test_credentials,
                                     acct_name, CRED_SPECIFIED);
        cli_credentials_set_password(test_credentials,
                                     password, CRED_SPECIFIED);
-       cli_credentials_set_secure_channel_type(test_credentials, SEC_CHAN_BDC);
 
-       printf("testing samlogon as %s@%s password: %s\n",
-               acct_name, TEST_ACCOUNT_NAME_PWD, password);
+       printf("testing samlogon as %s password: %s\n",
+               acct_name, password);
 
-       if (!test_SamLogon(tctx, p, machine_creds, test_credentials,
+       if (!test_SamLogon(tctx, p, test_credentials,
                           expected_samlogon_result)) {
                torture_warning(tctx, "new password did not work\n");
                ret = false;
@@ -2883,11 +2866,12 @@ static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p,
                                        struct cli_credentials *machine_credentials)
 {
        int s = 0, q = 0, f = 0, l = 0, z = 0;
+       struct dcerpc_binding *b;
        bool ret = true;
-       int delay = 500000;
+       int delay = 50000;
        bool set_levels[] = { false, true };
        bool query_levels[] = { false, true };
-       uint32_t levels[] = { 18, 21, 23, 24, 25, 26 };
+       uint32_t levels[] = { 18, 21, 26, 23, 24, 25 }; /* Second half only used when TEST_ALL_LEVELS defined */
        uint32_t nonzeros[] = { 1, 24 };
        uint32_t fields_present[] = {
                0,
@@ -2907,24 +2891,46 @@ static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p,
        struct dcerpc_pipe *np = NULL;
 
        if (torture_setting_bool(tctx, "samba3", false)) {
-               delay = 1000000;
+               delay = 999999;
                printf("Samba3 has second granularity, setting delay to: %d\n",
                        delay);
        }
 
-       status = torture_rpc_connection(tctx, &np, &ndr_table_netlogon);
+       status = torture_rpc_binding(tctx, &b);
        if (!NT_STATUS_IS_OK(status)) {
-               return false;
+               ret = false;
+               return ret;
+       }
+
+       /* We have to use schannel, otherwise the SamLogonEx fails
+        * with INTERNAL_ERROR */
+
+       b->flags &= ~DCERPC_AUTH_OPTIONS;
+       b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128;
+
+       status = dcerpc_pipe_connect_b(tctx, &np, b,
+                                      &ndr_table_netlogon,
+                                      machine_credentials, tctx->ev, tctx->lp_ctx);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("RPC pipe connect as domain member failed: %s\n", nt_errstr(status));
+               ret = false;
+               return ret;
        }
 
        /* set to 1 to enable testing for all possible opcode
           (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2)
           combinations */
 #if 0
+#define TEST_ALL_LEVELS 1
 #define TEST_SET_LEVELS 1
 #define TEST_QUERY_LEVELS 1
 #endif
+#ifdef TEST_ALL_LEVELS
        for (l=0; l<ARRAY_SIZE(levels); l++) {
+#else
+       for (l=0; l<(ARRAY_SIZE(levels))/2; l++) {
+#endif
        for (z=0; z<ARRAY_SIZE(nonzeros); z++) {
        for (f=0; f<ARRAY_SIZE(fields_present); f++) {
 #ifdef TEST_SET_LEVELS
@@ -3834,8 +3840,9 @@ static bool test_alias_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
                ret = false;
        }
 
-       if (torture_setting_bool(tctx, "samba4", false)) {
-               printf("skipping MultipleMembers Alias tests against Samba4\n");
+       if (torture_setting_bool(tctx, "samba3", false) ||
+           torture_setting_bool(tctx, "samba4", false)) {
+               printf("skipping MultipleMembers Alias tests against Samba\n");
                return ret;
        }
 
@@ -4370,7 +4377,7 @@ static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx
                { ACB_SVRTRUST, TEST_MACHINENAME, NT_STATUS_OK },
                { ACB_SVRTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
                { ACB_SVRTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
-               { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_OK },
+               { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_ACCESS_DENIED },
                { ACB_DOMTRUST | ACB_DISABLED, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER },
                { ACB_DOMTRUST | ACB_PWNOEXP, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER },
                { 0, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER },
@@ -5585,7 +5592,9 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context *
                        if (strcmp(info->general.oem_information.string, domain_comment) != 0) {
                                printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
                                       levels[i], info->general.oem_information.string, domain_comment);
-                               ret = false;
+                               if (!torture_setting_bool(tctx, "samba3", false)) {
+                                       ret = false;
+                               }
                        }
                        if (!info->general.primary.string) {
                                printf("QueryDomainInfo level %u returned no PDC name\n",
@@ -5602,7 +5611,9 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context *
                        if (strcmp(info->oem.oem_information.string, domain_comment) != 0) {
                                printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
                                       levels[i], info->oem.oem_information.string, domain_comment);
-                               ret = false;
+                               if (!torture_setting_bool(tctx, "samba3", false)) {
+                                       ret = false;
+                               }
                        }
                        break;
                case 6:
@@ -5616,7 +5627,9 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context *
                        if (strcmp(info->general2.general.oem_information.string, domain_comment) != 0) {
                                printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n",
                                       levels[i], info->general2.general.oem_information.string, domain_comment);
-                               ret = false;
+                               if (!torture_setting_bool(tctx, "samba3", false)) {
+                                       ret = false;
+                               }
                        }
                        break;
                }
@@ -5920,7 +5933,7 @@ static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *t
 
        if (torture_setting_bool(tctx, "samba4", false) ||
            torture_setting_bool(tctx, "samba3", false)) {
-               torture_comment(tctx, "skipping SetMemberAttributesOfGroup test against Samba4\n");
+               torture_comment(tctx, "skipping SetMemberAttributesOfGroup test against Samba\n");
        } else {
                /* this one is quite strange. I am using random inputs in the
                   hope of triggering an error that might give us a clue */
@@ -6041,7 +6054,262 @@ static bool test_RemoveMemberFromForeignDomain(struct dcerpc_pipe *p,
        return true;
 }
 
+static bool test_EnumDomainUsers(struct dcerpc_pipe *p,
+                                struct torture_context *tctx,
+                                struct policy_handle *domain_handle,
+                                uint32_t *total_num_entries_p)
+{
+       NTSTATUS status;
+       struct samr_EnumDomainUsers r;
+       uint32_t resume_handle = 0;
+       uint32_t num_entries = 0;
+       uint32_t total_num_entries = 0;
+       struct samr_SamArray *sam;
+
+       r.in.domain_handle = domain_handle;
+       r.in.acct_flags = 0;
+       r.in.max_size = (uint32_t)-1;
+       r.in.resume_handle = &resume_handle;
+
+       r.out.sam = &sam;
+       r.out.num_entries = &num_entries;
+       r.out.resume_handle = &resume_handle;
+
+       printf("Testing EnumDomainUsers\n");
+
+       do {
+               status = dcerpc_samr_EnumDomainUsers(p, tctx, &r);
+               if (NT_STATUS_IS_ERR(status)) {
+                       torture_assert_ntstatus_ok(tctx, status,
+                               "failed to enumerate users");
+               }
+
+               total_num_entries += num_entries;
+       } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+       if (total_num_entries_p) {
+               *total_num_entries_p = total_num_entries;
+       }
+
+       return true;
+}
+
+static bool test_EnumDomainGroups(struct dcerpc_pipe *p,
+                                 struct torture_context *tctx,
+                                 struct policy_handle *domain_handle,
+                                 uint32_t *total_num_entries_p)
+{
+       NTSTATUS status;
+       struct samr_EnumDomainGroups r;
+       uint32_t resume_handle = 0;
+       uint32_t num_entries = 0;
+       uint32_t total_num_entries = 0;
+       struct samr_SamArray *sam;
+
+       r.in.domain_handle = domain_handle;
+       r.in.max_size = (uint32_t)-1;
+       r.in.resume_handle = &resume_handle;
+
+       r.out.sam = &sam;
+       r.out.num_entries = &num_entries;
+       r.out.resume_handle = &resume_handle;
+
+       printf("Testing EnumDomainGroups\n");
+
+       do {
+               status = dcerpc_samr_EnumDomainGroups(p, tctx, &r);
+               if (NT_STATUS_IS_ERR(status)) {
+                       torture_assert_ntstatus_ok(tctx, status,
+                               "failed to enumerate groups");
+               }
+
+               total_num_entries += num_entries;
+       } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+       if (total_num_entries_p) {
+               *total_num_entries_p = total_num_entries;
+       }
+
+       return true;
+}
+
+static bool test_EnumDomainAliases(struct dcerpc_pipe *p,
+                                  struct torture_context *tctx,
+                                  struct policy_handle *domain_handle,
+                                  uint32_t *total_num_entries_p)
+{
+       NTSTATUS status;
+       struct samr_EnumDomainAliases r;
+       uint32_t resume_handle = 0;
+       uint32_t num_entries = 0;
+       uint32_t total_num_entries = 0;
+       struct samr_SamArray *sam;
+
+       r.in.domain_handle = domain_handle;
+       r.in.max_size = (uint32_t)-1;
+       r.in.resume_handle = &resume_handle;
+
+       r.out.sam = &sam;
+       r.out.num_entries = &num_entries;
+       r.out.resume_handle = &resume_handle;
+
+       printf("Testing EnumDomainAliases\n");
+
+       do {
+               status = dcerpc_samr_EnumDomainAliases(p, tctx, &r);
+               if (NT_STATUS_IS_ERR(status)) {
+                       torture_assert_ntstatus_ok(tctx, status,
+                               "failed to enumerate aliases");
+               }
+
+               total_num_entries += num_entries;
+       } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+       if (total_num_entries_p) {
+               *total_num_entries_p = total_num_entries;
+       }
+
+       return true;
+}
+
+static bool test_ManyObjects(struct dcerpc_pipe *p,
+                            struct torture_context *tctx,
+                            struct policy_handle *domain_handle,
+                            struct dom_sid *domain_sid,
+                            enum torture_samr_choice which_ops)
+{
+       uint32_t num_total = 1500;
+       uint32_t num_enum = 0;
+       uint32_t num_disp = 0;
+       uint32_t num_created = 0;
+       uint32_t num_anounced = 0;
+       bool ret = true;
+       NTSTATUS status;
+       uint32_t i;
+
+       struct policy_handle *handles = talloc_zero_array(tctx, struct policy_handle, num_total);
+
+       /* query */
+
+       {
+               struct samr_QueryDomainInfo2 r;
+               union samr_DomainInfo *info;
+               r.in.domain_handle = domain_handle;
+               r.in.level = 2;
+               r.out.info = &info;
+
+               status = dcerpc_samr_QueryDomainInfo2(p, tctx, &r);
+               torture_assert_ntstatus_ok(tctx, status,
+                       "failed to query domain info");
+
+               switch (which_ops) {
+               case TORTURE_SAMR_MANY_ACCOUNTS:
+                       num_anounced = info->general.num_users;
+                       break;
+               case TORTURE_SAMR_MANY_GROUPS:
+                       num_anounced = info->general.num_groups;
+                       break;
+               case TORTURE_SAMR_MANY_ALIASES:
+                       num_anounced = info->general.num_aliases;
+                       break;
+               default:
+                       return false;
+               }
+       }
+
+       /* create */
+
+       for (i=0; i < num_total; i++) {
+
+               const char *name = NULL;
+
+               switch (which_ops) {
+               case TORTURE_SAMR_MANY_ACCOUNTS:
+                       name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i);
+                       ret &= test_CreateUser(p, tctx, domain_handle, name, &handles[i], domain_sid, 0, NULL, false);
+                       break;
+               case TORTURE_SAMR_MANY_GROUPS:
+                       name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i);
+                       ret &= test_CreateDomainGroup(p, tctx, domain_handle, name, &handles[i], domain_sid, false);
+                       break;
+               case TORTURE_SAMR_MANY_ALIASES:
+                       name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i);
+                       ret &= test_CreateAlias(p, tctx, domain_handle, name, &handles[i], domain_sid, false);
+                       break;
+               default:
+                       return false;
+               }
+               if (!policy_handle_empty(&handles[i])) {
+                       num_created++;
+               }
+       }
+
+       /* enum */
+
+       switch (which_ops) {
+       case TORTURE_SAMR_MANY_ACCOUNTS:
+               ret &= test_EnumDomainUsers(p, tctx, domain_handle, &num_enum);
+               break;
+       case TORTURE_SAMR_MANY_GROUPS:
+               ret &= test_EnumDomainGroups(p, tctx, domain_handle, &num_enum);
+               break;
+       case TORTURE_SAMR_MANY_ALIASES:
+               ret &= test_EnumDomainAliases(p, tctx, domain_handle, &num_enum);
+               break;
+       default:
+               return false;
+       }
+
+       /* TODO: dispinfo */
+
+       switch (which_ops) {
+       case TORTURE_SAMR_MANY_ACCOUNTS:
+               break;
+       case TORTURE_SAMR_MANY_GROUPS:
+               break;
+       case TORTURE_SAMR_MANY_ALIASES:
+               break;
+       default:
+               return false;
+       }
+
 
+       /* delete */
+
+       for (i=0; i < num_total; i++) {
+
+               if (policy_handle_empty(&handles[i])) {
+                       continue;
+               }
+
+               switch (which_ops) {
+               case TORTURE_SAMR_MANY_ACCOUNTS:
+                       ret &= test_DeleteUser(p, tctx, &handles[i]);
+                       break;
+               case TORTURE_SAMR_MANY_GROUPS:
+                       ret &= test_DeleteDomainGroup(p, tctx, &handles[i]);
+                       break;
+               case TORTURE_SAMR_MANY_ALIASES:
+                       ret &= test_DeleteAlias(p, tctx, &handles[i]);
+                       break;
+               default:
+                       return false;
+               }
+       }
+
+       talloc_free(handles);
+
+#if 0
+       torture_assert_int_equal(tctx, num_disp, num_anounced + num_created,
+               "unexpected number of results returned in dispinfo call");
+#endif
+       if (which_ops == TORTURE_SAMR_MANY_ACCOUNTS && num_enum != num_anounced + num_created) {
+               torture_comment(tctx,
+                               "unexpected number of results (%u) returned in enum call, expected %u\n",
+                               num_enum, num_anounced + num_created);
+       }
+       return ret;
+}
 
 static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx,
                         struct policy_handle *handle);
@@ -6076,12 +6344,20 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
 
        /* run the domain tests with the main handle closed - this tests
           the servers reference counting */
-       ret &= test_samr_handle_Close(p, tctx, handle);
+       torture_assert(tctx, test_samr_handle_Close(p, tctx, handle), "Failed to close SAMR handle");
 
        switch (which_ops) {
-       case TORTURE_SAMR_USER_ATTRIBUTES:
-       case TORTURE_SAMR_USER_PRIVILEGES:
        case TORTURE_SAMR_PASSWORDS:
+       case TORTURE_SAMR_USER_PRIVILEGES:
+               if (!torture_setting_bool(tctx, "samba3", false)) {
+                       ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, NULL);
+               }
+               ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true);
+               if (!ret) {
+                       printf("Testing PASSWORDS or PRIVILAGES on domain %s failed!\n", dom_sid_string(tctx, sid));
+               }
+               break;
+       case TORTURE_SAMR_USER_ATTRIBUTES:
                if (!torture_setting_bool(tctx, "samba3", false)) {
                        ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, NULL);
                }
@@ -6089,7 +6365,7 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
                /* This test needs 'complex' users to validate */
                ret &= test_QueryDisplayInfo(p, tctx, &domain_handle);
                if (!ret) {
-                       printf("Testing PASSWORDS or ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid));
+                       printf("Testing ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid));
                }
                break;
        case TORTURE_SAMR_PASSWORDS_PWDLASTSET:
@@ -6101,6 +6377,14 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
                        printf("Testing PASSWORDS PWDLASTSET on domain %s failed!\n", dom_sid_string(tctx, sid));
                }
                break;
+       case TORTURE_SAMR_MANY_ACCOUNTS:
+       case TORTURE_SAMR_MANY_GROUPS:
+       case TORTURE_SAMR_MANY_ALIASES:
+               ret &= test_ManyObjects(p, tctx, &domain_handle, sid, which_ops);
+               if (!ret) {
+                       printf("Testing MANY-{ACCOUNTS,GROUPS,ALIASES} on domain %s failed!\n", dom_sid_string(tctx, sid));
+               }
+               break;
        case TORTURE_SAMR_OTHER:
                ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true);
                if (!ret) {
@@ -6153,10 +6437,10 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
                ret = false;
        }
 
-       ret &= test_samr_handle_Close(p, tctx, &domain_handle);
+       torture_assert(tctx, test_samr_handle_Close(p, tctx, &domain_handle), "Failed to close SAMR domain handle");
 
+       torture_assert(tctx, test_Connect(p, tctx, handle), "Faile to re-connect SAMR handle");
        /* reconnect the main handle */
-       ret &= test_Connect(p, tctx, handle);
 
        if (!ret) {
                printf("Testing domain %s failed!\n", dom_sid_string(tctx, sid));
@@ -6529,3 +6813,97 @@ struct torture_suite *torture_rpc_samr_user_privileges(TALLOC_CTX *mem_ctx)
 
        return suite;
 }
+
+static bool torture_rpc_samr_many_accounts(struct torture_context *torture,
+                                          struct dcerpc_pipe *p2,
+                                          struct cli_credentials *machine_credentials)
+{
+       NTSTATUS status;
+       struct dcerpc_pipe *p;
+       bool ret = true;
+       struct policy_handle handle;
+
+       status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+
+       ret &= test_Connect(p, torture, &handle);
+
+       ret &= test_EnumDomains(p, torture, &handle,
+                               TORTURE_SAMR_MANY_ACCOUNTS,
+                               machine_credentials);
+
+       ret &= test_samr_handle_Close(p, torture, &handle);
+
+       return ret;
+}
+
+static bool torture_rpc_samr_many_groups(struct torture_context *torture,
+                                        struct dcerpc_pipe *p2,
+                                        struct cli_credentials *machine_credentials)
+{
+       NTSTATUS status;
+       struct dcerpc_pipe *p;
+       bool ret = true;
+       struct policy_handle handle;
+
+       status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+
+       ret &= test_Connect(p, torture, &handle);
+
+       ret &= test_EnumDomains(p, torture, &handle,
+                               TORTURE_SAMR_MANY_GROUPS,
+                               machine_credentials);
+
+       ret &= test_samr_handle_Close(p, torture, &handle);
+
+       return ret;
+}
+
+static bool torture_rpc_samr_many_aliases(struct torture_context *torture,
+                                         struct dcerpc_pipe *p2,
+                                         struct cli_credentials *machine_credentials)
+{
+       NTSTATUS status;
+       struct dcerpc_pipe *p;
+       bool ret = true;
+       struct policy_handle handle;
+
+       status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+
+       ret &= test_Connect(p, torture, &handle);
+
+       ret &= test_EnumDomains(p, torture, &handle,
+                               TORTURE_SAMR_MANY_ALIASES,
+                               machine_credentials);
+
+       ret &= test_samr_handle_Close(p, torture, &handle);
+
+       return ret;
+}
+
+struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR-LARGE-DC");
+       struct torture_rpc_tcase *tcase;
+
+       tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "samr",
+                                                         &ndr_table_samr,
+                                                         TEST_ACCOUNT_NAME);
+
+       torture_rpc_tcase_add_test_creds(tcase, "many_aliases",
+                                        torture_rpc_samr_many_aliases);
+       torture_rpc_tcase_add_test_creds(tcase, "many_groups",
+                                        torture_rpc_samr_many_groups);
+       torture_rpc_tcase_add_test_creds(tcase, "many_accounts",
+                                        torture_rpc_samr_many_accounts);
+
+       return suite;
+}