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;
56 info.info1.unknown1 = 0;
57 info.info1.unknown2 = 0;
58 r5.in.system_name = "";
62 r5.out.connect_handle = h;
63 r5.in.access_mask = mask;
65 status = dcerpc_samr_Connect5(p, tctx, &r5);
70 /* check which bits in accessmask allows us to connect to the server */
71 static bool test_samr_accessmask_Connect5(struct torture_context *tctx,
72 struct dcerpc_pipe *p)
75 struct policy_handle h;
79 printf("testing which bits in accessmask allows us to connect\n");
82 printf("testing Connect5 with access mask 0x%08x", mask);
83 status = torture_samr_Connect5(tctx, p, mask, &h);
103 printf(" expecting to fail");
104 /* of only one of these bits are set we expect to
107 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
108 printf("Connect5 failed - %s\n", nt_errstr(status));
113 /* these bits set are expected to succeed by default */
114 if (!NT_STATUS_IS_OK(status)) {
115 printf("Connect5 failed - %s\n", nt_errstr(status));
119 status = torture_samr_Close(tctx, p, &h);
120 if (!NT_STATUS_IS_OK(status)) {
121 printf("Close failed - %s\n", nt_errstr(status));
132 /* check which bits in accessmask allows us to EnumDomains()
133 by default we must specify at least one of :
138 in the access mask to Connect5() in order to be allowed to perform
139 EnumDomains() on the policy handle returned from Connect5()
141 static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx,
142 struct dcerpc_pipe *p)
145 struct samr_EnumDomains ed;
146 struct policy_handle ch;
149 uint32_t resume_handle = 0;
151 printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n");
154 printf("testing Connect5/EnumDomains with access mask 0x%08x", mask);
155 status = torture_samr_Connect5(tctx, p, mask, &ch);
159 case 4: /* SAMR/EnumDomains */
160 case 25: /* Maximum */
161 case 28: /* GenericAll */
162 case 31: /* GenericRead */
163 /* these bits set are expected to succeed by default */
164 if (!NT_STATUS_IS_OK(status)) {
165 printf("Connect5 failed - %s\n", nt_errstr(status));
169 ed.in.connect_handle = &ch;
170 ed.in.resume_handle = &resume_handle;
171 ed.in.buf_size = (uint32_t)-1;
172 ed.out.resume_handle = &resume_handle;
174 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
175 if (!NT_STATUS_IS_OK(status)) {
176 printf("EnumDomains failed - %s\n", nt_errstr(status));
180 status = torture_samr_Close(tctx, p, &ch);
181 if (!NT_STATUS_IS_OK(status)) {
182 printf("Close failed - %s\n", nt_errstr(status));
187 printf(" expecting to fail");
189 if (!NT_STATUS_IS_OK(status)) {
194 ed.in.connect_handle = &ch;
195 ed.in.resume_handle = &resume_handle;
196 ed.in.buf_size = (uint32_t)-1;
197 ed.out.resume_handle = &resume_handle;
199 status = dcerpc_samr_EnumDomains(p, tctx, &ed);
200 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
201 printf("EnumDomains failed - %s\n", nt_errstr(status));
205 status = torture_samr_Close(tctx, p, &ch);
206 if (!NT_STATUS_IS_OK(status)) {
207 printf("Close failed - %s\n", nt_errstr(status));
220 * test how ACLs affect how/if a user can connect to the SAMR service
222 * samr_SetSecurity() returns SUCCESS when changing the ACL for
223 * a policy handle got from Connect5() but the ACL is not changed on
226 static bool test_samr_connect_user_acl(struct torture_context *tctx,
227 struct dcerpc_pipe *p,
228 struct cli_credentials *test_credentials,
229 const struct dom_sid *test_sid)
233 struct policy_handle ch;
234 struct policy_handle uch;
235 struct samr_QuerySecurity qs;
236 struct samr_SetSecurity ss;
237 struct security_ace ace;
238 struct security_descriptor *sd;
239 struct sec_desc_buf sdb;
242 struct dcerpc_pipe *test_p;
243 const char *binding = torture_setting_string(tctx, "binding", NULL);
245 printf("testing ACLs to allow/prevent users to connect to SAMR");
247 /* connect to SAMR */
248 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
249 if (!NT_STATUS_IS_OK(status)) {
250 printf("Connect5 failed - %s\n", nt_errstr(status));
255 /* get the current ACL for the SAMR policy handle */
257 qs.in.sec_info = SECINFO_DACL;
258 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
259 if (!NT_STATUS_IS_OK(status)) {
260 printf("QuerySecurity failed - %s\n", nt_errstr(status));
264 /* how big is the security descriptor? */
265 sd_size = qs.out.sdbuf->sd_size;
268 /* add an ACE to the security descriptor to deny the user the
269 * 'connect to server' right
271 sd = qs.out.sdbuf->sd;
272 ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
274 ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER;
275 ace.trustee = *test_sid;
276 status = security_descriptor_dacl_add(sd, &ace);
277 if (!NT_STATUS_IS_OK(status)) {
278 printf("Failed to add ACE to security descriptor\n");
282 ss.in.sec_info = SECINFO_DACL;
285 status = dcerpc_samr_SetSecurity(p, tctx, &ss);
286 if (!NT_STATUS_IS_OK(status)) {
287 printf("SetSecurity failed - %s\n", nt_errstr(status));
292 /* Try to connect as the test user */
293 status = dcerpc_pipe_connect(tctx,
294 &test_p, binding, &ndr_table_samr,
295 test_credentials, NULL);
296 /* connect to SAMR as the user */
297 status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
298 if (!NT_STATUS_IS_OK(status)) {
299 printf("Connect5 failed - %s\n", nt_errstr(status));
302 /* disconnec the user */
304 if (!NT_STATUS_IS_OK(status)) {
309 /* read the sequrity descriptor back. it should not have changed
310 * eventhough samr_SetSecurity returned SUCCESS
312 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
313 if (!NT_STATUS_IS_OK(status)) {
314 printf("QuerySecurity failed - %s\n", nt_errstr(status));
317 if (sd_size != qs.out.sdbuf->sd_size) {
318 printf("security descriptor changed\n");
323 status = torture_samr_Close(tctx, p, &ch);
324 if (!NT_STATUS_IS_OK(status)) {
325 printf("Close failed - %s\n", nt_errstr(status));
336 * test if the ACLs are enforced for users.
337 * a normal testuser only gets the rights provided in hte ACL for
338 * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER
339 * right. If the ACLs are checked when a user connects
340 * a testuser that requests the accessmask with only this bit set
341 * the connect should fail.
343 static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx,
344 struct dcerpc_pipe *p,
345 struct cli_credentials *test_credentials,
346 const struct dom_sid *test_sid)
350 struct policy_handle uch;
352 struct dcerpc_pipe *test_p;
353 const char *binding = torture_setting_string(tctx, "binding", NULL);
355 printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
358 status = dcerpc_pipe_connect(tctx,
359 &test_p, binding, &ndr_table_samr,
360 test_credentials, NULL);
362 /* connect to SAMR as the user */
363 status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
364 if (NT_STATUS_IS_OK(status)) {
365 printf("Connect5 failed - %s\n", nt_errstr(status));
370 /* disconnec the user */
376 /* check which bits in accessmask allows us to LookupDomain()
377 by default we must specify at least one of :
378 in the access mask to Connect5() in order to be allowed to perform
379 case 5: samr/opendomain
382 case 29: GenericExecute
383 LookupDomain() on the policy handle returned from Connect5()
385 static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx,
386 struct dcerpc_pipe *p)
389 struct samr_LookupDomain ld;
390 struct policy_handle ch;
391 struct lsa_String dn;
395 printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
398 printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
399 status = torture_samr_Connect5(tctx, p, mask, &ch);
404 case 25: /* Maximum */
405 case 28: /* GenericAll */
406 case 29: /* GenericExecute */
407 /* these bits set are expected to succeed by default */
408 if (!NT_STATUS_IS_OK(status)) {
409 printf("Connect5 failed - %s\n", nt_errstr(status));
413 ld.in.connect_handle = &ch;
414 ld.in.domain_name = &dn;
415 dn.string = lp_workgroup(global_loadparm);
417 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
418 if (!NT_STATUS_IS_OK(status)) {
419 printf("LookupDomain failed - %s\n", nt_errstr(status));
423 status = torture_samr_Close(tctx, p, &ch);
424 if (!NT_STATUS_IS_OK(status)) {
425 printf("Close failed - %s\n", nt_errstr(status));
430 printf(" expecting to fail");
432 if (!NT_STATUS_IS_OK(status)) {
437 ld.in.connect_handle = &ch;
438 ld.in.domain_name = &dn;
439 dn.string = lp_workgroup(global_loadparm);
441 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
442 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
443 printf("LookupDomain failed - %s\n", nt_errstr(status));
447 status = torture_samr_Close(tctx, p, &ch);
448 if (!NT_STATUS_IS_OK(status)) {
449 printf("Close failed - %s\n", nt_errstr(status));
460 /* check which bits in accessmask allows us to OpenDomain()
461 by default we must specify at least one of :
466 in the access mask to Connect5() in order to be allowed to perform
467 OpenDomain() on the policy handle returned from Connect5()
469 static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx,
470 struct dcerpc_pipe *p)
473 struct samr_LookupDomain ld;
474 struct samr_OpenDomain od;
475 struct policy_handle ch;
476 struct policy_handle dh;
477 struct lsa_String dn;
482 /* first we must grab the sid of the domain */
483 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
484 if (!NT_STATUS_IS_OK(status)) {
485 printf("Connect5 failed - %s\n", nt_errstr(status));
489 ld.in.connect_handle = &ch;
490 ld.in.domain_name = &dn;
491 dn.string = lp_workgroup(global_loadparm);
492 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
493 if (!NT_STATUS_IS_OK(status)) {
494 printf("LookupDomain failed - %s\n", nt_errstr(status));
500 printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
503 printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
504 status = torture_samr_Connect5(tctx, p, mask, &ch);
509 case 25: /* Maximum */
510 case 28: /* GenericAll */
511 case 29: /* GenericExecute */
512 /* these bits set are expected to succeed by default */
513 if (!NT_STATUS_IS_OK(status)) {
514 printf("Connect5 failed - %s\n", nt_errstr(status));
518 od.in.connect_handle = &ch;
519 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
520 od.in.sid = ld.out.sid;
521 od.out.domain_handle = &dh;
523 status = dcerpc_samr_OpenDomain(p, tctx, &od);
524 if (!NT_STATUS_IS_OK(status)) {
525 printf("OpenDomain failed - %s\n", nt_errstr(status));
529 status = torture_samr_Close(tctx, p, &dh);
530 if (!NT_STATUS_IS_OK(status)) {
531 printf("Close failed - %s\n", nt_errstr(status));
535 status = torture_samr_Close(tctx, p, &ch);
536 if (!NT_STATUS_IS_OK(status)) {
537 printf("Close failed - %s\n", nt_errstr(status));
542 printf(" expecting to fail");
544 if (!NT_STATUS_IS_OK(status)) {
549 status = torture_samr_Close(tctx, p, &ch);
550 if (!NT_STATUS_IS_OK(status)) {
551 printf("Close failed - %s\n", nt_errstr(status));
562 static bool test_samr_connect(struct torture_context *tctx,
563 struct dcerpc_pipe *p)
566 const char *testuser_passwd;
567 struct cli_credentials *test_credentials;
569 const struct dom_sid *test_sid;
571 /* create a test user */
572 testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(global_loadparm),
573 ACB_NORMAL, &testuser_passwd);
575 printf("Failed to create test user\n");
578 test_credentials = cli_credentials_init(tctx);
579 cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
580 cli_credentials_set_domain(test_credentials, lp_workgroup(global_loadparm),
582 cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED);
583 cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
584 test_sid = torture_join_user_sid(testuser);
587 /* test which bits in the accessmask to Connect5
588 will allow us to connect to the server
590 if (!test_samr_accessmask_Connect5(tctx, p)) {
595 /* test which bits in the accessmask to Connect5 will allow
596 * us to call EnumDomains()
598 if (!test_samr_accessmask_EnumDomains(tctx, p)) {
602 /* test which bits in the accessmask to Connect5 will allow
603 * us to call LookupDomain()
605 if (!test_samr_accessmask_LookupDomain(tctx, p)) {
610 /* test which bits in the accessmask to Connect5 will allow
611 * us to call OpenDomain()
613 if (!test_samr_accessmask_OpenDomain(tctx, p)) {
618 /* test if ACLs can be changed for the policy handle
619 * returned by Connect5
621 if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
625 /* test if the ACLs that are reported from the Connect5
626 * policy handle is enforced.
627 * i.e. an ordinary user only has the same rights as Everybody
631 * Samr/ConnectToServer
632 * is granted and should therefore not be able to connect when
633 * requesting SAMR_ACCESS_SHUTDOWN_SERVER
635 if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
641 /* remove the test user */
642 torture_leave_domain(testuser);
647 struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
649 struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
650 struct torture_rpc_tcase *tcase;
652 tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
655 torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);