diff options
-rw-r--r-- | source4/samba4-skip | 1 | ||||
-rw-r--r-- | source4/torture/config.mk | 1 | ||||
-rw-r--r-- | source4/torture/rpc/rpc.c | 1 | ||||
-rw-r--r-- | source4/torture/rpc/samr_accessmask.c | 599 |
4 files changed, 602 insertions, 0 deletions
diff --git a/source4/samba4-skip b/source4/samba4-skip index 2e62bbcf24..0f3de9e13e 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 887bb3cf86..aee84e81fb 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 16c97028e6..6891783a82 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 0000000000..c50a67d74c --- /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 <http://www.gnu.org/licenses/>. +*/ + +#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; +} |