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, NULL, tctx->lp_ctx);
305 /* connect to SAMR as the user */
306 status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
307 if (!NT_STATUS_IS_OK(status)) {
308 printf("Connect5 failed - %s\n", nt_errstr(status));
311 /* disconnec the user */
313 if (!NT_STATUS_IS_OK(status)) {
318 /* read the sequrity descriptor back. it should not have changed
319 * eventhough samr_SetSecurity returned SUCCESS
321 status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
322 if (!NT_STATUS_IS_OK(status)) {
323 printf("QuerySecurity failed - %s\n", nt_errstr(status));
326 if (sd_size != sdbuf->sd_size) {
327 printf("security descriptor changed\n");
332 status = torture_samr_Close(tctx, p, &ch);
333 if (!NT_STATUS_IS_OK(status)) {
334 printf("Close failed - %s\n", nt_errstr(status));
345 * test if the ACLs are enforced for users.
346 * a normal testuser only gets the rights provided in hte ACL for
347 * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER
348 * right. If the ACLs are checked when a user connects
349 * a testuser that requests the accessmask with only this bit set
350 * the connect should fail.
352 static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx,
353 struct dcerpc_pipe *p,
354 struct cli_credentials *test_credentials,
355 const struct dom_sid *test_sid)
359 struct policy_handle uch;
361 struct dcerpc_pipe *test_p;
362 const char *binding = torture_setting_string(tctx, "binding", NULL);
364 printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
367 status = dcerpc_pipe_connect(tctx,
368 &test_p, binding, &ndr_table_samr,
369 test_credentials, NULL, tctx->lp_ctx);
371 /* connect to SAMR as the user */
372 status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
373 if (NT_STATUS_IS_OK(status)) {
374 printf("Connect5 failed - %s\n", nt_errstr(status));
379 /* disconnec the user */
385 /* check which bits in accessmask allows us to LookupDomain()
386 by default we must specify at least one of :
387 in the access mask to Connect5() in order to be allowed to perform
388 case 5: samr/opendomain
391 case 29: GenericExecute
392 LookupDomain() on the policy handle returned from Connect5()
394 static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx,
395 struct dcerpc_pipe *p)
398 struct samr_LookupDomain ld;
399 struct dom_sid2 *sid = NULL;
400 struct policy_handle ch;
401 struct lsa_String dn;
405 printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
408 printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
409 status = torture_samr_Connect5(tctx, p, mask, &ch);
414 case 25: /* Maximum */
415 case 28: /* GenericAll */
416 case 29: /* GenericExecute */
417 /* these bits set are expected to succeed by default */
418 if (!NT_STATUS_IS_OK(status)) {
419 printf("Connect5 failed - %s\n", nt_errstr(status));
423 ld.in.connect_handle = &ch;
424 ld.in.domain_name = &dn;
426 dn.string = lp_workgroup(tctx->lp_ctx);
428 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
429 if (!NT_STATUS_IS_OK(status)) {
430 printf("LookupDomain failed - %s\n", nt_errstr(status));
434 status = torture_samr_Close(tctx, p, &ch);
435 if (!NT_STATUS_IS_OK(status)) {
436 printf("Close failed - %s\n", nt_errstr(status));
441 printf(" expecting to fail");
443 if (!NT_STATUS_IS_OK(status)) {
448 ld.in.connect_handle = &ch;
449 ld.in.domain_name = &dn;
450 dn.string = lp_workgroup(tctx->lp_ctx);
452 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
453 if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
454 printf("LookupDomain failed - %s\n", nt_errstr(status));
458 status = torture_samr_Close(tctx, p, &ch);
459 if (!NT_STATUS_IS_OK(status)) {
460 printf("Close failed - %s\n", nt_errstr(status));
471 /* check which bits in accessmask allows us to OpenDomain()
472 by default we must specify at least one of :
477 in the access mask to Connect5() in order to be allowed to perform
478 OpenDomain() on the policy handle returned from Connect5()
480 static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx,
481 struct dcerpc_pipe *p)
484 struct samr_LookupDomain ld;
485 struct dom_sid2 *sid = NULL;
486 struct samr_OpenDomain od;
487 struct policy_handle ch;
488 struct policy_handle dh;
489 struct lsa_String dn;
494 /* first we must grab the sid of the domain */
495 status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
496 if (!NT_STATUS_IS_OK(status)) {
497 printf("Connect5 failed - %s\n", nt_errstr(status));
501 ld.in.connect_handle = &ch;
502 ld.in.domain_name = &dn;
504 dn.string = lp_workgroup(tctx->lp_ctx);
505 status = dcerpc_samr_LookupDomain(p, tctx, &ld);
506 if (!NT_STATUS_IS_OK(status)) {
507 printf("LookupDomain failed - %s\n", nt_errstr(status));
513 printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
516 printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
517 status = torture_samr_Connect5(tctx, p, mask, &ch);
522 case 25: /* Maximum */
523 case 28: /* GenericAll */
524 case 29: /* GenericExecute */
525 /* these bits set are expected to succeed by default */
526 if (!NT_STATUS_IS_OK(status)) {
527 printf("Connect5 failed - %s\n", nt_errstr(status));
531 od.in.connect_handle = &ch;
532 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
533 od.in.sid = *ld.out.sid;
534 od.out.domain_handle = &dh;
536 status = dcerpc_samr_OpenDomain(p, tctx, &od);
537 if (!NT_STATUS_IS_OK(status)) {
538 printf("OpenDomain failed - %s\n", nt_errstr(status));
542 status = torture_samr_Close(tctx, p, &dh);
543 if (!NT_STATUS_IS_OK(status)) {
544 printf("Close failed - %s\n", nt_errstr(status));
548 status = torture_samr_Close(tctx, p, &ch);
549 if (!NT_STATUS_IS_OK(status)) {
550 printf("Close failed - %s\n", nt_errstr(status));
555 printf(" expecting to fail");
557 if (!NT_STATUS_IS_OK(status)) {
562 status = torture_samr_Close(tctx, p, &ch);
563 if (!NT_STATUS_IS_OK(status)) {
564 printf("Close failed - %s\n", nt_errstr(status));
575 static bool test_samr_connect(struct torture_context *tctx,
576 struct dcerpc_pipe *p)
579 const char *testuser_passwd;
580 struct cli_credentials *test_credentials;
582 const struct dom_sid *test_sid;
584 /* create a test user */
585 testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx),
586 ACB_NORMAL, &testuser_passwd);
588 printf("Failed to create test user\n");
591 test_credentials = cli_credentials_init(tctx);
592 cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
593 cli_credentials_set_domain(test_credentials, lp_workgroup(tctx->lp_ctx),
595 cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED);
596 cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
597 test_sid = torture_join_user_sid(testuser);
600 /* test which bits in the accessmask to Connect5
601 will allow us to connect to the server
603 if (!test_samr_accessmask_Connect5(tctx, p)) {
608 /* test which bits in the accessmask to Connect5 will allow
609 * us to call EnumDomains()
611 if (!test_samr_accessmask_EnumDomains(tctx, p)) {
615 /* test which bits in the accessmask to Connect5 will allow
616 * us to call LookupDomain()
618 if (!test_samr_accessmask_LookupDomain(tctx, p)) {
623 /* test which bits in the accessmask to Connect5 will allow
624 * us to call OpenDomain()
626 if (!test_samr_accessmask_OpenDomain(tctx, p)) {
631 /* test if ACLs can be changed for the policy handle
632 * returned by Connect5
634 if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
638 /* test if the ACLs that are reported from the Connect5
639 * policy handle is enforced.
640 * i.e. an ordinary user only has the same rights as Everybody
644 * Samr/ConnectToServer
645 * is granted and should therefore not be able to connect when
646 * requesting SAMR_ACCESS_SHUTDOWN_SERVER
648 if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
654 /* remove the test user */
655 torture_leave_domain(tctx, testuser);
660 struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
662 struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
663 struct torture_rpc_tcase *tcase;
665 tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
668 torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);