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;
153 printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n");
156 printf("testing Connect5/EnumDomains with access mask 0x%08x", mask);
157 status = torture_samr_Connect5(tctx, p, mask, &ch);
161 case 4: /* SAMR/EnumDomains */
162 case 25: /* Maximum */
163 case 28: /* GenericAll */
164 case 31: /* GenericRead */
165 /* these bits set are expected to succeed by default */
166 if (!NT_STATUS_IS_OK(status)) {
167 printf("Connect5 failed - %s\n", nt_errstr(status));
171 ed.in.connect_handle = &ch;
172 ed.in.resume_handle = &resume_handle;
173 ed.in.buf_size = (uint32_t)-1;
174 ed.out.resume_handle = &resume_handle;
176 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
177 if (!NT_STATUS_IS_OK(status)) {
178 printf("EnumDomains failed - %s\n", nt_errstr(status));
182 status = torture_samr_Close(tctx, p, &ch);
183 if (!NT_STATUS_IS_OK(status)) {
184 printf("Close failed - %s\n", nt_errstr(status));
189 printf(" expecting to fail");
191 if (!NT_STATUS_IS_OK(status)) {
196 ed.in.connect_handle = &ch;
197 ed.in.resume_handle = &resume_handle;
198 ed.in.buf_size = (uint32_t)-1;
199 ed.out.resume_handle = &resume_handle;
201 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
202 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
203 printf("EnumDomains failed - %s\n", nt_errstr(status));
207 status = torture_samr_Close(tctx, p, &ch);
208 if (!NT_STATUS_IS_OK(status)) {
209 printf("Close failed - %s\n", nt_errstr(status));
222 * test how ACLs affect how/if a user can connect to the SAMR service
224 * samr_SetSecurity() returns SUCCESS when changing the ACL for
225 * a policy handle got from Connect5() but the ACL is not changed on
228 static bool test_samr_connect_user_acl(struct torture_context *tctx,
229 struct dcerpc_pipe *p,
230 struct cli_credentials *test_credentials,
231 const struct dom_sid *test_sid)
235 struct policy_handle ch;
236 struct policy_handle uch;
237 struct samr_QuerySecurity qs;
238 struct samr_SetSecurity ss;
239 struct security_ace ace;
240 struct security_descriptor *sd;
241 struct sec_desc_buf sdb, *sdbuf = NULL;
244 struct dcerpc_pipe *test_p;
245 const char *binding = torture_setting_string(tctx, "binding", NULL);
247 printf("testing ACLs to allow/prevent users to connect to SAMR");
249 /* connect to SAMR */
250 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
251 if (!NT_STATUS_IS_OK(status)) {
252 printf("Connect5 failed - %s\n", nt_errstr(status));
257 /* get the current ACL for the SAMR policy handle */
259 qs.in.sec_info = SECINFO_DACL;
260 qs.out.sdbuf = &sdbuf;
261 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
262 if (!NT_STATUS_IS_OK(status)) {
263 printf("QuerySecurity failed - %s\n", nt_errstr(status));
267 /* how big is the security descriptor? */
268 sd_size = sdbuf->sd_size;
271 /* add an ACE to the security descriptor to deny the user the
272 * 'connect to server' right
275 ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
277 ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER;
278 ace.trustee = *test_sid;
279 status = security_descriptor_dacl_add(sd, &ace);
280 if (!NT_STATUS_IS_OK(status)) {
281 printf("Failed to add ACE to security descriptor\n");
285 ss.in.sec_info = SECINFO_DACL;
288 status = dcerpc_samr_SetSecurity(p, tctx, &ss);
289 if (!NT_STATUS_IS_OK(status)) {
290 printf("SetSecurity failed - %s\n", nt_errstr(status));
295 /* Try to connect as the test user */
296 status = dcerpc_pipe_connect(tctx,
297 &test_p, binding, &ndr_table_samr,
298 test_credentials, NULL, tctx->lp_ctx);
299 /* connect to SAMR as the user */
300 status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
301 if (!NT_STATUS_IS_OK(status)) {
302 printf("Connect5 failed - %s\n", nt_errstr(status));
305 /* disconnec the user */
307 if (!NT_STATUS_IS_OK(status)) {
312 /* read the sequrity descriptor back. it should not have changed
313 * eventhough samr_SetSecurity returned SUCCESS
315 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
316 if (!NT_STATUS_IS_OK(status)) {
317 printf("QuerySecurity failed - %s\n", nt_errstr(status));
320 if (sd_size != sdbuf->sd_size) {
321 printf("security descriptor changed\n");
326 status = torture_samr_Close(tctx, p, &ch);
327 if (!NT_STATUS_IS_OK(status)) {
328 printf("Close failed - %s\n", nt_errstr(status));
339 * test if the ACLs are enforced for users.
340 * a normal testuser only gets the rights provided in hte ACL for
341 * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER
342 * right. If the ACLs are checked when a user connects
343 * a testuser that requests the accessmask with only this bit set
344 * the connect should fail.
346 static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx,
347 struct dcerpc_pipe *p,
348 struct cli_credentials *test_credentials,
349 const struct dom_sid *test_sid)
353 struct policy_handle uch;
355 struct dcerpc_pipe *test_p;
356 const char *binding = torture_setting_string(tctx, "binding", NULL);
358 printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
361 status = dcerpc_pipe_connect(tctx,
362 &test_p, binding, &ndr_table_samr,
363 test_credentials, NULL, tctx->lp_ctx);
365 /* connect to SAMR as the user */
366 status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
367 if (NT_STATUS_IS_OK(status)) {
368 printf("Connect5 failed - %s\n", nt_errstr(status));
373 /* disconnec the user */
379 /* check which bits in accessmask allows us to LookupDomain()
380 by default we must specify at least one of :
381 in the access mask to Connect5() in order to be allowed to perform
382 case 5: samr/opendomain
385 case 29: GenericExecute
386 LookupDomain() on the policy handle returned from Connect5()
388 static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx,
389 struct dcerpc_pipe *p)
392 struct samr_LookupDomain ld;
393 struct policy_handle ch;
394 struct lsa_String dn;
398 printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
401 printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
402 status = torture_samr_Connect5(tctx, p, mask, &ch);
407 case 25: /* Maximum */
408 case 28: /* GenericAll */
409 case 29: /* GenericExecute */
410 /* these bits set are expected to succeed by default */
411 if (!NT_STATUS_IS_OK(status)) {
412 printf("Connect5 failed - %s\n", nt_errstr(status));
416 ld.in.connect_handle = &ch;
417 ld.in.domain_name = &dn;
418 dn.string = lp_workgroup(tctx->lp_ctx);
420 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
421 if (!NT_STATUS_IS_OK(status)) {
422 printf("LookupDomain failed - %s\n", nt_errstr(status));
426 status = torture_samr_Close(tctx, p, &ch);
427 if (!NT_STATUS_IS_OK(status)) {
428 printf("Close failed - %s\n", nt_errstr(status));
433 printf(" expecting to fail");
435 if (!NT_STATUS_IS_OK(status)) {
440 ld.in.connect_handle = &ch;
441 ld.in.domain_name = &dn;
442 dn.string = lp_workgroup(tctx->lp_ctx);
444 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
445 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
446 printf("LookupDomain failed - %s\n", nt_errstr(status));
450 status = torture_samr_Close(tctx, p, &ch);
451 if (!NT_STATUS_IS_OK(status)) {
452 printf("Close failed - %s\n", nt_errstr(status));
463 /* check which bits in accessmask allows us to OpenDomain()
464 by default we must specify at least one of :
469 in the access mask to Connect5() in order to be allowed to perform
470 OpenDomain() on the policy handle returned from Connect5()
472 static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx,
473 struct dcerpc_pipe *p)
476 struct samr_LookupDomain ld;
477 struct samr_OpenDomain od;
478 struct policy_handle ch;
479 struct policy_handle dh;
480 struct lsa_String dn;
485 /* first we must grab the sid of the domain */
486 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
487 if (!NT_STATUS_IS_OK(status)) {
488 printf("Connect5 failed - %s\n", nt_errstr(status));
492 ld.in.connect_handle = &ch;
493 ld.in.domain_name = &dn;
494 dn.string = lp_workgroup(tctx->lp_ctx);
495 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
496 if (!NT_STATUS_IS_OK(status)) {
497 printf("LookupDomain failed - %s\n", nt_errstr(status));
503 printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
506 printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
507 status = torture_samr_Connect5(tctx, p, mask, &ch);
512 case 25: /* Maximum */
513 case 28: /* GenericAll */
514 case 29: /* GenericExecute */
515 /* these bits set are expected to succeed by default */
516 if (!NT_STATUS_IS_OK(status)) {
517 printf("Connect5 failed - %s\n", nt_errstr(status));
521 od.in.connect_handle = &ch;
522 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
523 od.in.sid = ld.out.sid;
524 od.out.domain_handle = &dh;
526 status = dcerpc_samr_OpenDomain(p, tctx, &od);
527 if (!NT_STATUS_IS_OK(status)) {
528 printf("OpenDomain failed - %s\n", nt_errstr(status));
532 status = torture_samr_Close(tctx, p, &dh);
533 if (!NT_STATUS_IS_OK(status)) {
534 printf("Close failed - %s\n", nt_errstr(status));
538 status = torture_samr_Close(tctx, p, &ch);
539 if (!NT_STATUS_IS_OK(status)) {
540 printf("Close failed - %s\n", nt_errstr(status));
545 printf(" expecting to fail");
547 if (!NT_STATUS_IS_OK(status)) {
552 status = torture_samr_Close(tctx, p, &ch);
553 if (!NT_STATUS_IS_OK(status)) {
554 printf("Close failed - %s\n", nt_errstr(status));
565 static bool test_samr_connect(struct torture_context *tctx,
566 struct dcerpc_pipe *p)
569 const char *testuser_passwd;
570 struct cli_credentials *test_credentials;
572 const struct dom_sid *test_sid;
574 /* create a test user */
575 testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx),
576 ACB_NORMAL, &testuser_passwd);
578 printf("Failed to create test user\n");
581 test_credentials = cli_credentials_init(tctx);
582 cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
583 cli_credentials_set_domain(test_credentials, lp_workgroup(tctx->lp_ctx),
585 cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED);
586 cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
587 test_sid = torture_join_user_sid(testuser);
590 /* test which bits in the accessmask to Connect5
591 will allow us to connect to the server
593 if (!test_samr_accessmask_Connect5(tctx, p)) {
598 /* test which bits in the accessmask to Connect5 will allow
599 * us to call EnumDomains()
601 if (!test_samr_accessmask_EnumDomains(tctx, p)) {
605 /* test which bits in the accessmask to Connect5 will allow
606 * us to call LookupDomain()
608 if (!test_samr_accessmask_LookupDomain(tctx, p)) {
613 /* test which bits in the accessmask to Connect5 will allow
614 * us to call OpenDomain()
616 if (!test_samr_accessmask_OpenDomain(tctx, p)) {
621 /* test if ACLs can be changed for the policy handle
622 * returned by Connect5
624 if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
628 /* test if the ACLs that are reported from the Connect5
629 * policy handle is enforced.
630 * i.e. an ordinary user only has the same rights as Everybody
634 * Samr/ConnectToServer
635 * is granted and should therefore not be able to connect when
636 * requesting SAMR_ACCESS_SHUTDOWN_SERVER
638 if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
644 /* remove the test user */
645 torture_leave_domain(tctx, testuser);
650 struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
652 struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
653 struct torture_rpc_tcase *tcase;
655 tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
658 torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);