diff options
Diffstat (limited to '')
-rw-r--r-- | source4/torture/rpc/samr_accessmask.c | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c new file mode 100644 index 0000000..1ed0d67 --- /dev/null +++ b/source4/torture/rpc/samr_accessmask.c @@ -0,0 +1,1197 @@ +/* + Unix SMB/CIFS implementation. + test suite for accessmasks on the SAMR pipe + + Copyright (C) Ronnie Sahlberg 2007 + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "libcli/security/security.h" + + +/* test user created to test the ACLs associated to SAMR objects */ +#define TEST_USER_NAME "samr_testuser" +#define TEST_MACHINENAME "samrtestmach" + +static NTSTATUS torture_samr_Close(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Close cl; + + cl.in.handle = h; + cl.out.handle = h; + status = dcerpc_samr_Close_r(b, tctx, &cl); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return cl.out.result; +} + +static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t mask, struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + uint32_t level_out = 0; + + info.info1.client_version = 0; + info.info1.supported_features = 0; + r5.in.system_name = ""; + r5.in.level_in = 1; + r5.in.info_in = &info; + r5.out.info_out = &info; + r5.out.level_out = &level_out; + r5.out.connect_handle = h; + r5.in.access_mask = mask; + + status = dcerpc_samr_Connect5_r(b, tctx, &r5); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return r5.out.result; +} + +/* check which bits in accessmask allows us to connect to the server */ +static bool test_samr_accessmask_Connect5(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct policy_handle h; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in accessmask allows us to connect\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5 with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &h); + mask <<= 1; + + switch (i) { + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 23: + case 26: + case 27: + printf(" expecting to fail"); + /* of only one of these bits are set we expect to + fail by default + */ + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, b, &h); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to EnumDomains() + by default we must specify at least one of : + SAMR/EnumDomains + Maximum + GenericAll + GenericRead + in the access mask to Connect5() in order to be allowed to perform + EnumDomains() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_EnumDomains ed; + struct policy_handle ch; + int i; + uint32_t mask; + uint32_t resume_handle = 0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in Connect5 accessmask allows us to EnumDomains\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/EnumDomains with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 4: /* SAMR/EnumDomains */ + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 31: /* GenericRead */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed), + "EnumDomains failed"); + if (!NT_STATUS_IS_OK(ed.out.result)) { + printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed), + "EnumDomains failed"); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ed.out.result)) { + printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + + +/* + * test how ACLs affect how/if a user can connect to the SAMR service + * + * samr_SetSecurity() returns SUCCESS when changing the ACL for + * a policy handle got from Connect5() but the ACL is not changed on + * the server + */ +static bool test_samr_connect_user_acl(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle ch; + struct policy_handle uch; + struct samr_QuerySecurity qs; + struct samr_SetSecurity ss; + struct security_ace ace = {}; + struct security_descriptor *sd; + struct sec_desc_buf sdb, *sdbuf = NULL; + bool ret = true; + int sd_size; + struct dcerpc_pipe *test_p; + struct dcerpc_binding_handle *test_b; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("Testing ACLs to allow/prevent users to connect to SAMR"); + + /* connect to SAMR */ + status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + + /* get the current ACL for the SAMR policy handle */ + qs.in.handle = &ch; + qs.in.sec_info = SECINFO_DACL; + qs.out.sdbuf = &sdbuf; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs), + "QuerySecurity failed"); + if (!NT_STATUS_IS_OK(qs.out.result)) { + printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result)); + ret = false; + } + + /* how big is the security descriptor? */ + sd_size = sdbuf->sd_size; + + + /* add an ACE to the security descriptor to deny the user the + * 'connect to server' right + */ + sd = sdbuf->sd; + ace.type = SEC_ACE_TYPE_ACCESS_DENIED; + ace.flags = 0; + ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER; + ace.trustee = *test_sid; + status = security_descriptor_dacl_add(sd, &ace); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to add ACE to security descriptor\n"); + ret = false; + } + ss.in.handle = &ch; + ss.in.sec_info = SECINFO_DACL; + ss.in.sdbuf = &sdb; + sdb.sd = sd; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &ss), + "SetSecurity failed"); + if (!NT_STATUS_IS_OK(ss.out.result)) { + printf("SetSecurity failed - %s\n", nt_errstr(ss.out.result)); + ret = false; + } + + + /* Try to connect as the test user */ + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status)); + return false; + } + test_b = test_p->binding_handle; + + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_b, SEC_FLAG_MAXIMUM_ALLOWED, &uch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + /* disconnect the user */ + talloc_free(test_p); + + + /* read the sequrity descriptor back. it should not have changed + * even though samr_SetSecurity returned SUCCESS + */ + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs), + "QuerySecurity failed"); + if (!NT_STATUS_IS_OK(qs.out.result)) { + printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result)); + ret = false; + } + if (sd_size != sdbuf->sd_size) { + printf("security descriptor changed\n"); + ret = false; + } + + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (ret == true) { + printf(" OK\n"); + } + return ret; +} + +/* + * test if the ACLs are enforced for users. + * a normal testuser only gets the rights provided in the ACL for + * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER + * right. If the ACLs are checked when a user connects + * a testuser that requests the accessmask with only this bit set + * the connect should fail. + */ +static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle uch; + bool ret = true; + struct dcerpc_pipe *test_p; + struct dcerpc_binding_handle *test_b; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("Testing if ACLs are enforced for non domain admin users when connecting to SAMR"); + + + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status)); + return false; + } + test_b = test_p->binding_handle; + + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_b, SAMR_ACCESS_SHUTDOWN_SERVER, &uch); + if (NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + printf(" OK\n"); + + /* disconnect the user */ + talloc_free(test_p); + + return ret; +} + +/* check which bits in accessmask allows us to LookupDomain() + by default we must specify at least one of : + in the access mask to Connect5() in order to be allowed to perform + case 5: samr/opendomain + case 25: Maximum + case 28: GenericAll + case 29: GenericExecute + LookupDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; + struct policy_handle ch; + struct lsa_String dn; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in Connect5 accessmask allows us to LookupDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/LookupDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if (!NT_STATUS_IS_OK(ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to OpenDomain() + by default we must specify at least one of : + samr/opendomain + Maximum + GenericAll + GenericExecute + in the access mask to Connect5() in order to be allowed to perform + OpenDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain od; + struct policy_handle ch; + struct policy_handle dh; + struct lsa_String dn; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + + /* first we must grab the sid of the domain */ + status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if (!NT_STATUS_IS_OK(ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + + + printf("Testing which bits in Connect5 accessmask allows us to OpenDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/OpenDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + od.in.connect_handle = &ch; + od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od.in.sid = sid; + od.out.domain_handle = &dh; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &od), + "OpenDomain failed"); + if (!NT_STATUS_IS_OK(od.out.result)) { + printf("OpenDomain failed - %s\n", nt_errstr(od.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &dh); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +static bool test_samr_connect(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + void *testuser; + const char *testuser_passwd; + struct cli_credentials *test_credentials; + bool ret = true; + const struct dom_sid *test_sid; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "Skipping test against Samba 3"); + } + + /* create a test user */ + testuser = torture_create_testuser(tctx, TEST_USER_NAME, lpcfg_workgroup(tctx->lp_ctx), + ACB_NORMAL, &testuser_passwd); + if (!testuser) { + printf("Failed to create test user\n"); + return false; + } + test_credentials = cli_credentials_init(tctx); + cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, lpcfg_workgroup(tctx->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED); + test_sid = torture_join_user_sid(testuser); + + + /* test if ACLs can be changed for the policy handle + * returned by Connect5 + */ + if (!test_samr_connect_user_acl(tctx, b, test_credentials, test_sid)) { + ret = false; + } + + /* test if the ACLs that are reported from the Connect5 + * policy handle is enforced. + * i.e. an ordinary user only has the same rights as Everybody + * ReadControl + * Samr/OpenDomain + * Samr/EnumDomains + * Samr/ConnectToServer + * is granted and should therefore not be able to connect when + * requesting SAMR_ACCESS_SHUTDOWN_SERVER + */ + if (!test_samr_connect_user_acl_enforced(tctx, b, test_credentials, test_sid)) { + ret = false; + } + + /* remove the test user */ + torture_leave_domain(tctx, testuser); + + return ret; +} + +struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.accessmask"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", + &ndr_table_samr); + + torture_rpc_tcase_add_test(tcase, "connect", test_samr_connect); + + /* test which bits in the accessmask to Connect5 will allow + * us to call OpenDomain() */ + torture_rpc_tcase_add_test(tcase, "OpenDomain", + test_samr_accessmask_OpenDomain); + + /* test which bits in the accessmask to Connect5 will allow + * us to call LookupDomain() */ + torture_rpc_tcase_add_test(tcase, "LookupDomain", + test_samr_accessmask_LookupDomain); + + /* test which bits in the accessmask to Connect5 will allow + * us to call EnumDomains() */ + torture_rpc_tcase_add_test(tcase, "EnumDomains", + test_samr_accessmask_EnumDomains); + + /* test which bits in the accessmask to Connect5 + will allow us to connect to the server */ + torture_rpc_tcase_add_test(tcase, "Connect5", + test_samr_accessmask_Connect5); + + return suite; +} + +static bool test_LookupRids(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t rid) +{ + struct samr_LookupRids r; + struct lsa_Strings names; + struct samr_Ids types; + + torture_comment(tctx, "Testing LookupRids %d\n", rid); + + r.in.domain_handle = domain_handle; + r.in.num_rids = 1; + r.in.rids = &rid; + r.out.names = &names; + r.out.types = &types; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &r), + "failed to call samr_LookupRids"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call samr_LookupRids"); + + return true; +} + + +static bool test_user(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t access_mask, + struct samr_DispEntryGeneral *u) +{ + struct policy_handle user_handle; + + torture_comment(tctx, "Testing user %s (%d)\n", u->account_name.string, u->rid); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, u->rid), + "failed to call lookuprids"); + + { + struct samr_OpenUser r; + + r.in.domain_handle = domain_handle; + r.in.access_mask = access_mask; + r.in.rid = u->rid; + r.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), + "failed to open user"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open user"); + } + { + struct samr_QueryUserInfo r; + union samr_UserInfo *info; + uint32_t levels[] = { 16, 21 }; + int i; + + r.in.user_handle = &user_handle; + r.out.info = &info; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_comment(tctx, "Testing QueryUserInfo rid: %d level: %d\n", + u->rid, r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + talloc_asprintf(tctx, "failed to query user info level %d", r.in.level)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "failed to query user info level %d", r.in.level)); + } + } + { + struct samr_GetGroupsForUser r; + struct samr_RidWithAttributeArray *rids; + + r.in.user_handle = &user_handle; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r), + "failed to query groups for user"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query groups for user"); + } + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &user_handle), + "failed to close user handle"); + + return true; +} + +static bool test_samr_group(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t access_mask, + struct samr_SamEntry *g) +{ + struct policy_handle group_handle; + + torture_comment(tctx, "Testing group %s (%d)\n", g->name.string, g->idx); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, g->idx), + "failed to call lookuprids"); + { + struct samr_OpenGroup r; + + r.in.domain_handle = domain_handle; + r.in.access_mask = access_mask; + r.in.rid = g->idx; + r.out.group_handle = &group_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r), + "failed to open group"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open group"); + } + { + struct samr_QueryGroupMember r; + struct samr_RidAttrArray *rids; + + r.in.group_handle = &group_handle; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &r), + "failed to query group member"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query group member"); + + } + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &group_handle), + "failed to close group handle"); + + return true; +} + +static bool test_samr_alias(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + struct samr_SamEntry *a) +{ + torture_comment(tctx, "Testing alias %s (%d)\n", a->name.string, a->idx); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, a->idx), + "failed to call lookuprids"); + + { + struct samr_GetAliasMembership r; + struct lsa_SidArray sids; + struct samr_Ids rids; + + ZERO_STRUCT(sids); + + r.in.domain_handle = domain_handle; + r.in.sids = &sids; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), + "failed to get alias membership"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to get alias membership"); + } + + + return true; +} + +static bool test_samr_domain(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + const char *domain_name, + struct policy_handle *connect_handle, + struct policy_handle *domain_handle_p) +{ + struct policy_handle domain_handle; + struct dom_sid *domain_sid; + + if (!domain_name) { + struct samr_EnumDomains r; + uint32_t resume_handle; + struct samr_SamArray *sam; + uint32_t num_entries; + int i; + + r.in.connect_handle = connect_handle; + r.in.buf_size = 0xffff; + r.in.resume_handle = &resume_handle; + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), + "failed to enum domains"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to enum domains"); + + torture_assert_int_equal(tctx, num_entries, 2, + "unexpected number of domains"); + + torture_assert(tctx, sam, + "no domain pointer returned"); + + for (i=0; i < sam->count; i++) { + if (!strequal(sam->entries[i].name.string, "builtin")) { + domain_name = sam->entries[i].name.string; + break; + } + } + + torture_assert(tctx, domain_name, + "no domain found other than builtin found"); + } + + { + struct samr_LookupDomain r; + struct dom_sid2 *sid; + struct lsa_String name; + + name.string = talloc_strdup(tctx, domain_name); + + r.in.connect_handle = connect_handle; + r.in.domain_name = &name; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), + "failed to lookup domain"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to lookup domain"); + + domain_sid = dom_sid_dup(tctx, sid); + } + + { + struct samr_OpenDomain r; + + r.in.connect_handle = connect_handle; + r.in.access_mask = access_mask; + r.in.sid = domain_sid; + r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r), + "failed to open domain"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open domain"); + + } + + { + struct samr_QueryDomainInfo r; + union samr_DomainInfo *info; + uint32_t levels[] = { 1, 2, 8, 12 }; + int i; + + r.in.domain_handle = &domain_handle; + r.out.info = &info; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), + talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level)); + } + + } + + *domain_handle_p = domain_handle; + + return true; +} + +static void get_query_dispinfo_params(int loop_count, + uint32_t *max_entries, + uint32_t *buf_size) +{ + switch(loop_count) { + case 0: + *max_entries = 512; + *buf_size = 16383; + break; + case 1: + *max_entries = 1024; + *buf_size = 32766; + break; + case 2: + *max_entries = 2048; + *buf_size = 65532; + break; + case 3: + *max_entries = 4096; + *buf_size = 131064; + break; + default: /* loop_count >= 4 */ + *max_entries = 4096; + *buf_size = 131071; + break; + } +} + + +static bool test_samr_users(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_QueryDisplayInfo r; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; + int loop_count = 0; + + r.in.domain_handle = domain_handle; + r.in.level = 1; + r.in.start_idx = 0; + + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; + + do { + int i; + + r.in.max_entries = 0xffff; + r.in.buf_size = 0xffff; + + get_query_dispinfo_params(loop_count, + &r.in.max_entries, + &r.in.buf_size); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r), + "QueryDisplayInfo failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call QueryDisplayInfo"); + } + + for (i=0; i < info.info1.count; i++) { + torture_assert(tctx, + test_user(tctx, b, domain_handle, access_mask, &info.info1.entries[i]), + "failed to test user"); + } + loop_count++; + r.in.start_idx += info.info1.count; + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool test_samr_groups(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_EnumDomainGroups r; + uint32_t resume_handle = 0; + struct samr_SamArray *sam; + uint32_t num_entries; + + r.in.domain_handle = domain_handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 0xFFFF; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + do { + int i; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), + "EnumDomainGroups failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call EnumDomainGroups"); + } + + for (i=0; i < num_entries; i++) { + torture_assert(tctx, + test_samr_group(tctx, b, domain_handle, access_mask, &sam->entries[i]), + "failed to test group"); + } + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool test_samr_aliases(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_EnumDomainAliases r; + uint32_t resume_handle = 0; + struct samr_SamArray *sam; + uint32_t num_entries; + + r.in.domain_handle = domain_handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 0xFFFF; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + do { + int i; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), + "EnumDomainAliases failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call EnumDomainAliases"); + } + + for (i=0; i < num_entries; i++) { + torture_assert(tctx, + test_samr_alias(tctx, b, domain_handle, &sam->entries[i]), + "failed to test alias"); + } + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool torture_rpc_samr_workstation_query(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert_ntstatus_ok(tctx, + torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &connect_handle), + "failed to connect to samr server"); + + torture_assert(tctx, + test_samr_domain(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + lpcfg_workgroup(tctx->lp_ctx), + &connect_handle, &domain_handle), + "failed to test domain"); + + torture_assert(tctx, + test_samr_users(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test users"); + + torture_assert(tctx, + test_samr_groups(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test groups"); + + torture_assert(tctx, + test_samr_aliases(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test aliases"); + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &domain_handle), + "failed to close domain handle"); + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &connect_handle), + "failed to close connect handle"); + + return true; +} + +/* The purpose of this test is to verify that an account authenticated as a + * domain member workstation can query a DC for various remote read calls all + * opening objects while requesting SEC_FLAG_MAXIMUM_ALLOWED access rights on + * the object open calls. This is the behavior of winbind (and most of samba's + * client code) - gd */ + +struct torture_suite *torture_rpc_samr_workstation_auth(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.machine.auth"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_MACHINENAME); + + torture_rpc_tcase_add_test_creds(tcase, "workstation_query", + torture_rpc_samr_workstation_query); + + return suite; +} |