From: Ronnie Sahlberg Date: Thu, 20 Sep 2007 22:57:57 +0000 (+0000) Subject: r25263: add a test to check the accessmasks and ACLs on policy handles returned on the X-Git-Tag: samba-4.0.0alpha6~801^3~1883 X-Git-Url: http://git.samba.org/samba.git/?p=kai%2Fsamba-autobuild%2F.git;a=commitdiff_plain;h=52857d83c1fd841f150edd1dbef51dd734f0e39a r25263: add a test to check the accessmasks and ACLs on policy handles returned on the SAMR pipe. also test which bits in the accessmasks grant which right to perform operations on the returned policy handle (This used to be commit cb5bc801362a4a81a690e641146c70a7af8e6096) --- diff --git a/source4/samba4-skip b/source4/samba4-skip index 2e62bbcf242..0f3de9e13ef 100644 --- a/source4/samba4-skip +++ b/source4/samba4-skip @@ -10,6 +10,7 @@ BASE-SCAN-MAXFID RAW-BENCH-OPLOCK RAW-HOLD-OPLOCK RAW-PING-PONG +RPC-SAMR_ACCESSMASK RAW-SCAN-EAMAX RAW-QFILEINFO-IPC BASE-UTABLE diff --git a/source4/torture/config.mk b/source4/torture/config.mk index 887bb3cf869..aee84e81fbe 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -132,6 +132,7 @@ OBJ_FILES = \ rpc/spoolss_notify.o \ rpc/unixinfo.o \ rpc/samr.o \ + rpc/samr_accessmask.o \ rpc/wkssvc.o \ rpc/srvsvc.o \ rpc/svcctl.o \ diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c index 16c97028e6b..6891783a824 100644 --- a/source4/torture/rpc/rpc.c +++ b/source4/torture/rpc/rpc.c @@ -399,6 +399,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_simple_test(suite, "SCHANNEL2", torture_rpc_schannel2); torture_suite_add_suite(suite, torture_rpc_srvsvc(suite)); torture_suite_add_suite(suite, torture_rpc_svcctl(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite)); torture_suite_add_suite(suite, torture_rpc_epmapper(suite)); torture_suite_add_suite(suite, torture_rpc_initshutdown(suite)); torture_suite_add_suite(suite, torture_rpc_oxidresolve(suite)); diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c new file mode 100644 index 00000000000..c50a67d74c9 --- /dev/null +++ b/source4/torture/rpc/samr_accessmask.c @@ -0,0 +1,599 @@ +/* + Unix SMB/CIFS implementation. + test suite for accessmasks on the SAMR pipe + + Copyright (C) Ronnie Sahlberg 2007 + + 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 . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "torture/rpc/rpc.h" +#include "param/param.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* test user created to test the ACLs associated to SAMR objects */ +#define TEST_USER_NAME "samr_testuser" + + +static NTSTATUS torture_samr_Close(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Close cl; + + cl.in.handle = h; + cl.out.handle = h; + status = dcerpc_samr_Close(p, tctx, &cl); + + return status; +} + +static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t mask, struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + + info.info1.unknown1 = 0; + info.info1.unknown2 = 0; + r5.in.system_name = ""; + r5.in.level = 1; + r5.in.info = &info; + r5.out.info = &info; + r5.out.connect_handle = h; + r5.in.access_mask = mask; + + status = dcerpc_samr_Connect5(p, tctx, &r5); + + return status; +} + +/* 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; + + 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, p, 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, p, &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; + + 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, p, 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; + + status = dcerpc_samr_EnumDomains(p, tctx, &ed); + if (!NT_STATUS_IS_OK(status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &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; + + status = dcerpc_samr_EnumDomains(p, tctx, &ed); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("EnumDomains failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &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_pipe *p, + 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; + bool ret = True; + int sd_size; + struct dcerpc_pipe *test_p; + 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, p, 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; + status = dcerpc_samr_QuerySecurity(p, tctx, &qs); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + ret = False; + } + + /* how big is the security descriptor? */ + sd_size = qs.out.sdbuf->sd_size; + + + /* add an ACE to the security descriptor to deny the user the + * 'connect to server' right + */ + sd = qs.out.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; + status = dcerpc_samr_SetSecurity(p, tctx, &ss); + if (!NT_STATUS_IS_OK(status)) { + printf("SetSecurity failed - %s\n", nt_errstr(status)); + ret = False; + } + + + /* Try to connect as the test user */ + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, NULL); + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return False; + } + /* disconnec the user */ + talloc_free(test_p); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + + /* read the sequrity descriptor back. it should not have changed + * eventhough samr_SetSecurity returned SUCCESS + */ + status = dcerpc_samr_QuerySecurity(p, tctx, &qs); + if (!NT_STATUS_IS_OK(status)) { + printf("QuerySecurity failed - %s\n", nt_errstr(status)); + ret = False; + } + if (sd_size != qs.out.sdbuf->sd_size) { + printf("security descriptor changed\n"); + ret = False; + } + + + status = torture_samr_Close(tctx, p, &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; +} + +/* 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 policy_handle ch; + struct lsa_String dn; + int i; + uint32_t mask; + + 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, p, 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; + dn.string = lp_workgroup(); + + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &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; + dn.string = lp_workgroup(); + + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &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 samr_OpenDomain od; + struct policy_handle ch; + struct policy_handle dh; + struct lsa_String dn; + int i; + uint32_t mask; + + + /* first we must grab the sid of the domain */ + status = torture_samr_Connect5(tctx, p, 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; + dn.string = lp_workgroup(); + status = dcerpc_samr_LookupDomain(p, tctx, &ld); + if (!NT_STATUS_IS_OK(status)) { + printf("LookupDomain failed - %s\n", nt_errstr(status)); + 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, p, 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 = ld.out.sid; + od.out.domain_handle = &dh; + + status = dcerpc_samr_OpenDomain(p, tctx, &od); + if (!NT_STATUS_IS_OK(status)) { + printf("OpenDomain failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &dh); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return False; + } + + status = torture_samr_Close(tctx, p, &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, p, &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; + + /* create a test user */ + testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(), 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, lp_workgroup(), 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 which bits in the accessmask to Connect5 + will allow us to connect to the server + */ + if (!test_samr_accessmask_Connect5(tctx, p)) { + ret = False; + } + + + /* test which bits in the accessmask to Connect5 will allow + * us to call EnumDomains() + */ + if (!test_samr_accessmask_EnumDomains(tctx, p)) { + ret = False; + } + + /* test which bits in the accessmask to Connect5 will allow + * us to call LookupDomain() + */ + if (!test_samr_accessmask_LookupDomain(tctx, p)) { + ret = False; + } + + + /* test which bits in the accessmask to Connect5 will allow + * us to call OpenDomain() + */ + if (!test_samr_accessmask_OpenDomain(tctx, p)) { + ret = False; + } + + + /* test if ACLs can be changed for the policy handle + * returned by Connect5 + */ + if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) { + ret = False; + } + + /* remove the test user */ + torture_leave_domain(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); + + return suite; +}