2 Unix SMB/CIFS implementation.
3 test suite for accessmasks on the SAMR pipe
5 Copyright (C) Ronnie Sahlberg 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "torture/torture.h"
23 #include "librpc/gen_ndr/ndr_samr_c.h"
24 #include "torture/rpc/rpc.h"
25 #include "param/param.h"
26 #include "libcli/security/security.h"
27 #include "librpc/gen_ndr/ndr_security.h"
30 /* test user created to test the ACLs associated to SAMR objects */
31 #define TEST_USER_NAME "samr_testuser"
34 static NTSTATUS torture_samr_Close(struct torture_context *tctx,
35 struct dcerpc_pipe *p,
36 struct policy_handle *h)
43 status = dcerpc_samr_Close(p, tctx, &cl);
48 static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
49 struct dcerpc_pipe *p,
50 uint32_t mask, struct policy_handle *h)
53 struct samr_Connect5 r5;
54 union samr_ConnectInfo info;
55 uint32_t level_out = 0;
57 info.info1.client_version = 0;
58 info.info1.unknown2 = 0;
59 r5.in.system_name = "";
61 r5.in.info_in = &info;
62 r5.out.info_out = &info;
63 r5.out.level_out = &level_out;
64 r5.out.connect_handle = h;
65 r5.in.access_mask = mask;
67 status = dcerpc_samr_Connect5(p, tctx, &r5);
72 /* check which bits in accessmask allows us to connect to the server */
73 static bool test_samr_accessmask_Connect5(struct torture_context *tctx,
74 struct dcerpc_pipe *p)
77 struct policy_handle h;
81 printf("testing which bits in accessmask allows us to connect\n");
84 printf("testing Connect5 with access mask 0x%08x", mask);
85 status = torture_samr_Connect5(tctx, p, mask, &h);
105 printf(" expecting to fail");
106 /* of only one of these bits are set we expect to
109 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
110 printf("Connect5 failed - %s\n", nt_errstr(status));
115 /* these bits set are expected to succeed by default */
116 if (!NT_STATUS_IS_OK(status)) {
117 printf("Connect5 failed - %s\n", nt_errstr(status));
121 status = torture_samr_Close(tctx, p, &h);
122 if (!NT_STATUS_IS_OK(status)) {
123 printf("Close failed - %s\n", nt_errstr(status));
134 /* check which bits in accessmask allows us to EnumDomains()
135 by default we must specify at least one of :
140 in the access mask to Connect5() in order to be allowed to perform
141 EnumDomains() on the policy handle returned from Connect5()
143 static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx,
144 struct dcerpc_pipe *p)
147 struct samr_EnumDomains ed;
148 struct policy_handle ch;
151 uint32_t resume_handle = 0;
152 struct samr_SamArray *sam = NULL;
153 uint32_t num_entries = 0;
155 printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n");
158 printf("testing Connect5/EnumDomains with access mask 0x%08x", mask);
159 status = torture_samr_Connect5(tctx, p, mask, &ch);
163 case 4: /* SAMR/EnumDomains */
164 case 25: /* Maximum */
165 case 28: /* GenericAll */
166 case 31: /* GenericRead */
167 /* these bits set are expected to succeed by default */
168 if (!NT_STATUS_IS_OK(status)) {
169 printf("Connect5 failed - %s\n", nt_errstr(status));
173 ed.in.connect_handle = &ch;
174 ed.in.resume_handle = &resume_handle;
175 ed.in.buf_size = (uint32_t)-1;
176 ed.out.resume_handle = &resume_handle;
177 ed.out.num_entries = &num_entries;
180 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
181 if (!NT_STATUS_IS_OK(status)) {
182 printf("EnumDomains failed - %s\n", nt_errstr(status));
186 status = torture_samr_Close(tctx, p, &ch);
187 if (!NT_STATUS_IS_OK(status)) {
188 printf("Close failed - %s\n", nt_errstr(status));
193 printf(" expecting to fail");
195 if (!NT_STATUS_IS_OK(status)) {
200 ed.in.connect_handle = &ch;
201 ed.in.resume_handle = &resume_handle;
202 ed.in.buf_size = (uint32_t)-1;
203 ed.out.resume_handle = &resume_handle;
204 ed.out.num_entries = &num_entries;
207 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
208 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
209 printf("EnumDomains failed - %s\n", nt_errstr(status));
213 status = torture_samr_Close(tctx, p, &ch);
214 if (!NT_STATUS_IS_OK(status)) {
215 printf("Close failed - %s\n", nt_errstr(status));
228 * test how ACLs affect how/if a user can connect to the SAMR service
230 * samr_SetSecurity() returns SUCCESS when changing the ACL for
231 * a policy handle got from Connect5() but the ACL is not changed on
234 static bool test_samr_connect_user_acl(struct torture_context *tctx,
235 struct dcerpc_pipe *p,
236 struct cli_credentials *test_credentials,
237 const struct dom_sid *test_sid)
241 struct policy_handle ch;
242 struct policy_handle uch;
243 struct samr_QuerySecurity qs;
244 struct samr_SetSecurity ss;
245 struct security_ace ace;
246 struct security_descriptor *sd;
247 struct sec_desc_buf sdb, *sdbuf = NULL;
250 struct dcerpc_pipe *test_p;
251 const char *binding = torture_setting_string(tctx, "binding", NULL);
253 printf("testing ACLs to allow/prevent users to connect to SAMR");
255 /* connect to SAMR */
256 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
257 if (!NT_STATUS_IS_OK(status)) {
258 printf("Connect5 failed - %s\n", nt_errstr(status));
263 /* get the current ACL for the SAMR policy handle */
265 qs.in.sec_info = SECINFO_DACL;
266 qs.out.sdbuf = &sdbuf;
267 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
268 if (!NT_STATUS_IS_OK(status)) {
269 printf("QuerySecurity failed - %s\n", nt_errstr(status));
273 /* how big is the security descriptor? */
274 sd_size = sdbuf->sd_size;
277 /* add an ACE to the security descriptor to deny the user the
278 * 'connect to server' right
281 ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
283 ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER;
284 ace.trustee = *test_sid;
285 status = security_descriptor_dacl_add(sd, &ace);
286 if (!NT_STATUS_IS_OK(status)) {
287 printf("Failed to add ACE to security descriptor\n");
291 ss.in.sec_info = SECINFO_DACL;
294 status = dcerpc_samr_SetSecurity(p, tctx, &ss);
295 if (!NT_STATUS_IS_OK(status)) {
296 printf("SetSecurity failed - %s\n", nt_errstr(status));
301 /* Try to connect as the test user */
302 status = dcerpc_pipe_connect(tctx,
303 &test_p, binding, &ndr_table_samr,
304 test_credentials, tctx->ev, tctx->lp_ctx);
305 if (!NT_STATUS_IS_OK(status)) {
306 printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status));
310 /* connect to SAMR as the user */
311 status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
312 if (!NT_STATUS_IS_OK(status)) {
313 printf("Connect5 failed - %s\n", nt_errstr(status));
316 /* disconnec the user */
320 /* read the sequrity descriptor back. it should not have changed
321 * eventhough samr_SetSecurity returned SUCCESS
323 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
324 if (!NT_STATUS_IS_OK(status)) {
325 printf("QuerySecurity failed - %s\n", nt_errstr(status));
328 if (sd_size != sdbuf->sd_size) {
329 printf("security descriptor changed\n");
334 status = torture_samr_Close(tctx, p, &ch);
335 if (!NT_STATUS_IS_OK(status)) {
336 printf("Close failed - %s\n", nt_errstr(status));
347 * test if the ACLs are enforced for users.
348 * a normal testuser only gets the rights provided in hte ACL for
349 * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER
350 * right. If the ACLs are checked when a user connects
351 * a testuser that requests the accessmask with only this bit set
352 * the connect should fail.
354 static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx,
355 struct dcerpc_pipe *p,
356 struct cli_credentials *test_credentials,
357 const struct dom_sid *test_sid)
361 struct policy_handle uch;
363 struct dcerpc_pipe *test_p;
364 const char *binding = torture_setting_string(tctx, "binding", NULL);
366 printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
369 status = dcerpc_pipe_connect(tctx,
370 &test_p, binding, &ndr_table_samr,
371 test_credentials, tctx->ev, tctx->lp_ctx);
372 if (!NT_STATUS_IS_OK(status)) {
373 printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status));
377 /* connect to SAMR as the user */
378 status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
379 if (NT_STATUS_IS_OK(status)) {
380 printf("Connect5 failed - %s\n", nt_errstr(status));
385 /* disconnec the user */
391 /* check which bits in accessmask allows us to LookupDomain()
392 by default we must specify at least one of :
393 in the access mask to Connect5() in order to be allowed to perform
394 case 5: samr/opendomain
397 case 29: GenericExecute
398 LookupDomain() on the policy handle returned from Connect5()
400 static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx,
401 struct dcerpc_pipe *p)
404 struct samr_LookupDomain ld;
405 struct dom_sid2 *sid = NULL;
406 struct policy_handle ch;
407 struct lsa_String dn;
411 printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
414 printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
415 status = torture_samr_Connect5(tctx, p, mask, &ch);
420 case 25: /* Maximum */
421 case 28: /* GenericAll */
422 case 29: /* GenericExecute */
423 /* these bits set are expected to succeed by default */
424 if (!NT_STATUS_IS_OK(status)) {
425 printf("Connect5 failed - %s\n", nt_errstr(status));
429 ld.in.connect_handle = &ch;
430 ld.in.domain_name = &dn;
432 dn.string = lp_workgroup(tctx->lp_ctx);
434 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
435 if (!NT_STATUS_IS_OK(status)) {
436 printf("LookupDomain failed - %s\n", nt_errstr(status));
440 status = torture_samr_Close(tctx, p, &ch);
441 if (!NT_STATUS_IS_OK(status)) {
442 printf("Close failed - %s\n", nt_errstr(status));
447 printf(" expecting to fail");
449 if (!NT_STATUS_IS_OK(status)) {
454 ld.in.connect_handle = &ch;
455 ld.in.domain_name = &dn;
457 dn.string = lp_workgroup(tctx->lp_ctx);
459 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
460 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
461 printf("LookupDomain failed - %s\n", nt_errstr(status));
465 status = torture_samr_Close(tctx, p, &ch);
466 if (!NT_STATUS_IS_OK(status)) {
467 printf("Close failed - %s\n", nt_errstr(status));
478 /* check which bits in accessmask allows us to OpenDomain()
479 by default we must specify at least one of :
484 in the access mask to Connect5() in order to be allowed to perform
485 OpenDomain() on the policy handle returned from Connect5()
487 static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx,
488 struct dcerpc_pipe *p)
491 struct samr_LookupDomain ld;
492 struct dom_sid2 *sid = NULL;
493 struct samr_OpenDomain od;
494 struct policy_handle ch;
495 struct policy_handle dh;
496 struct lsa_String dn;
501 /* first we must grab the sid of the domain */
502 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
503 if (!NT_STATUS_IS_OK(status)) {
504 printf("Connect5 failed - %s\n", nt_errstr(status));
508 ld.in.connect_handle = &ch;
509 ld.in.domain_name = &dn;
511 dn.string = lp_workgroup(tctx->lp_ctx);
512 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
513 if (!NT_STATUS_IS_OK(status)) {
514 printf("LookupDomain failed - %s\n", nt_errstr(status));
520 printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
523 printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
524 status = torture_samr_Connect5(tctx, p, mask, &ch);
529 case 25: /* Maximum */
530 case 28: /* GenericAll */
531 case 29: /* GenericExecute */
532 /* these bits set are expected to succeed by default */
533 if (!NT_STATUS_IS_OK(status)) {
534 printf("Connect5 failed - %s\n", nt_errstr(status));
538 od.in.connect_handle = &ch;
539 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
541 od.out.domain_handle = &dh;
543 status = dcerpc_samr_OpenDomain(p, tctx, &od);
544 if (!NT_STATUS_IS_OK(status)) {
545 printf("OpenDomain failed - %s\n", nt_errstr(status));
549 status = torture_samr_Close(tctx, p, &dh);
550 if (!NT_STATUS_IS_OK(status)) {
551 printf("Close failed - %s\n", nt_errstr(status));
555 status = torture_samr_Close(tctx, p, &ch);
556 if (!NT_STATUS_IS_OK(status)) {
557 printf("Close failed - %s\n", nt_errstr(status));
562 printf(" expecting to fail");
564 if (!NT_STATUS_IS_OK(status)) {
569 status = torture_samr_Close(tctx, p, &ch);
570 if (!NT_STATUS_IS_OK(status)) {
571 printf("Close failed - %s\n", nt_errstr(status));
582 static bool test_samr_connect(struct torture_context *tctx,
583 struct dcerpc_pipe *p)
586 const char *testuser_passwd;
587 struct cli_credentials *test_credentials;
589 const struct dom_sid *test_sid;
591 /* create a test user */
592 testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx),
593 ACB_NORMAL, &testuser_passwd);
595 printf("Failed to create test user\n");
598 test_credentials = cli_credentials_init(tctx);
599 cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
600 cli_credentials_set_domain(test_credentials, lp_workgroup(tctx->lp_ctx),
602 cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED);
603 cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
604 test_sid = torture_join_user_sid(testuser);
607 /* test which bits in the accessmask to Connect5
608 will allow us to connect to the server
610 if (!test_samr_accessmask_Connect5(tctx, p)) {
615 /* test which bits in the accessmask to Connect5 will allow
616 * us to call EnumDomains()
618 if (!test_samr_accessmask_EnumDomains(tctx, p)) {
622 /* test which bits in the accessmask to Connect5 will allow
623 * us to call LookupDomain()
625 if (!test_samr_accessmask_LookupDomain(tctx, p)) {
630 /* test which bits in the accessmask to Connect5 will allow
631 * us to call OpenDomain()
633 if (!test_samr_accessmask_OpenDomain(tctx, p)) {
637 if (!torture_setting_bool(tctx, "samba3", false)) {
639 /* test if ACLs can be changed for the policy handle
640 * returned by Connect5
642 if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
646 /* test if the ACLs that are reported from the Connect5
647 * policy handle is enforced.
648 * i.e. an ordinary user only has the same rights as Everybody
652 * Samr/ConnectToServer
653 * is granted and should therefore not be able to connect when
654 * requesting SAMR_ACCESS_SHUTDOWN_SERVER
656 if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
662 /* remove the test user */
663 torture_leave_domain(tctx, testuser);
668 struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
670 struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
671 struct torture_rpc_tcase *tcase;
673 tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
676 torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);