fb560befe98e6f4f8079ff9317dcad630e8ff7a7
[jra/samba/.git] / source4 / torture / rpc / samr_accessmask.c
1 /* 
2    Unix SMB/CIFS implementation.
3    test suite for accessmasks on the SAMR pipe
4
5    Copyright (C) Ronnie Sahlberg 2007
6    
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.
11    
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.
16    
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/>.
19 */
20
21 #include "includes.h"
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"
28
29
30 /* test user created to test the ACLs associated to SAMR objects */
31 #define TEST_USER_NAME "samr_testuser"
32
33
34 static NTSTATUS torture_samr_Close(struct torture_context *tctx,
35                 struct dcerpc_pipe *p, 
36                 struct policy_handle *h)
37 {
38         NTSTATUS status;
39         struct samr_Close cl;
40
41         cl.in.handle  = h;
42         cl.out.handle = h;
43         status = dcerpc_samr_Close(p, tctx, &cl);
44
45         return status;
46 }
47
48 static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
49                 struct dcerpc_pipe *p, 
50                 uint32_t mask, struct policy_handle *h)
51 {
52         NTSTATUS status;
53         struct samr_Connect5 r5;
54         union samr_ConnectInfo info;
55         uint32_t level_out = 0;
56
57         info.info1.client_version = 0;
58         info.info1.unknown2 = 0;
59         r5.in.system_name = "";
60         r5.in.level_in = 1;
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;
66
67         status = dcerpc_samr_Connect5(p, tctx, &r5);
68
69         return status;
70 }
71
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)
75 {
76         NTSTATUS status;
77         struct policy_handle h;
78         int i;
79         uint32_t mask;
80
81         printf("testing which bits in accessmask allows us to connect\n");
82         mask = 1;
83         for (i=0;i<33;i++) {    
84                 printf("testing Connect5 with access mask 0x%08x", mask);
85                 status = torture_samr_Connect5(tctx, p, mask, &h);
86                 mask <<= 1;
87
88                 switch (i) {
89                 case 6:
90                 case 7:
91                 case 8:
92                 case 9:
93                 case 10:
94                 case 11:
95                 case 12:
96                 case 13:
97                 case 14:
98                 case 15:
99                 case 20:
100                 case 21:
101                 case 22:
102                 case 23:
103                 case 26:
104                 case 27:
105                         printf(" expecting to fail");
106                         /* of only one of these bits are set we expect to
107                            fail by default
108                         */
109                         if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
110                                 printf("Connect5 failed - %s\n", nt_errstr(status));
111                                 return false;
112                         }
113                         break;
114                 default:
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));
118                                 return false;
119                         }
120
121                         status = torture_samr_Close(tctx, p, &h);
122                         if (!NT_STATUS_IS_OK(status)) {
123                                 printf("Close failed - %s\n", nt_errstr(status));
124                                 return false;
125                         }
126                         break;
127                 }
128                 printf(" OK\n");
129         }
130
131         return true;
132 }
133
134 /* check which bits in accessmask allows us to EnumDomains()
135    by default we must specify at least one of :
136         SAMR/EnumDomains
137         Maximum
138         GenericAll
139         GenericRead
140    in the access mask to Connect5() in order to be allowed to perform
141    EnumDomains() on the policy handle returned from Connect5()
142 */
143 static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, 
144                                                    struct dcerpc_pipe *p)
145 {
146         NTSTATUS status;
147         struct samr_EnumDomains ed;
148         struct policy_handle ch;
149         int i;
150         uint32_t mask;
151         uint32_t resume_handle = 0;
152         struct samr_SamArray *sam = NULL;
153         uint32_t num_entries = 0;
154
155         printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n");
156         mask = 1;
157         for (i=0;i<33;i++) {    
158                 printf("testing Connect5/EnumDomains with access mask 0x%08x", mask);
159                 status = torture_samr_Connect5(tctx, p, mask, &ch);
160                 mask <<= 1;
161
162                 switch (i) {
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));
170                                 return false;
171                         }
172
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;
178                         ed.out.sam = &sam;
179
180                         status = dcerpc_samr_EnumDomains(p, tctx, &ed);
181                         if (!NT_STATUS_IS_OK(status)) {
182                                 printf("EnumDomains failed - %s\n", nt_errstr(status));
183                                 return false;
184                         }
185
186                         status = torture_samr_Close(tctx, p, &ch);
187                         if (!NT_STATUS_IS_OK(status)) {
188                                 printf("Close failed - %s\n", nt_errstr(status));
189                                 return false;
190                         }
191                         break;
192                 default:
193                         printf(" expecting to fail");
194
195                         if (!NT_STATUS_IS_OK(status)) {
196                                 printf(" OK\n");
197                                 continue;
198                         }
199
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;
205                         ed.out.sam = &sam;
206
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));
210                                 return false;
211                         }
212
213                         status = torture_samr_Close(tctx, p, &ch);
214                         if (!NT_STATUS_IS_OK(status)) {
215                                 printf("Close failed - %s\n", nt_errstr(status));
216                                 return false;
217                         }
218                         break;
219                 }
220                 printf(" OK\n");
221         }
222
223         return true;
224 }
225
226
227 /*
228  * test how ACLs affect how/if a user can connect to the SAMR service 
229  *
230  * samr_SetSecurity() returns SUCCESS when changing the ACL for
231  * a policy handle got from Connect5()   but the ACL is not changed on
232  * the server
233  */
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)
238
239 {
240         NTSTATUS status;
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;
248         bool ret = true;
249         int sd_size;
250         struct dcerpc_pipe *test_p;
251         const char *binding = torture_setting_string(tctx, "binding", NULL);
252
253         printf("testing ACLs to allow/prevent users to connect to SAMR");
254
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));
259                 return false;
260         }
261
262         
263         /* get the current ACL for the SAMR policy handle */
264         qs.in.handle = &ch;
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));
270                 ret = false;
271         }
272
273         /* how big is the security descriptor? */
274         sd_size = sdbuf->sd_size;
275
276
277         /* add an ACE to the security descriptor to deny the user the
278          * 'connect to server' right
279          */
280         sd = sdbuf->sd;
281         ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
282         ace.flags = 0;
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");
288                 ret = false;
289         }
290         ss.in.handle = &ch;
291         ss.in.sec_info = SECINFO_DACL;
292         ss.in.sdbuf = &sdb;
293         sdb.sd = sd;
294         status = dcerpc_samr_SetSecurity(p, tctx, &ss);
295         if (!NT_STATUS_IS_OK(status)) {
296                 printf("SetSecurity failed - %s\n", nt_errstr(status));
297                 ret = false;
298         }
299
300
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));
307                 return false;
308         }
309
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));
314                 return false;
315         }
316         /* disconnec the user */
317         talloc_free(test_p);
318
319
320         /* read the sequrity descriptor back. it should not have changed 
321          * eventhough samr_SetSecurity returned SUCCESS
322          */
323         status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
324         if (!NT_STATUS_IS_OK(status)) {
325                 printf("QuerySecurity failed - %s\n", nt_errstr(status));
326                 ret = false;
327         }
328         if (sd_size != sdbuf->sd_size) {
329                 printf("security descriptor changed\n");
330                 ret = false;
331         }
332
333
334         status = torture_samr_Close(tctx, p, &ch);
335         if (!NT_STATUS_IS_OK(status)) {
336                 printf("Close failed - %s\n", nt_errstr(status));
337                 ret = false;
338         }
339
340         if (ret == true) {
341                 printf(" OK\n");
342         }
343         return ret;
344 }
345
346 /*
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.
353  */
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)
358
359 {
360         NTSTATUS status;
361         struct policy_handle uch;
362         bool ret = true;
363         struct dcerpc_pipe *test_p;
364         const char *binding = torture_setting_string(tctx, "binding", NULL);
365
366         printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
367
368
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));
374                 return false;
375         }
376
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));
381                 return false;
382         }
383         printf(" OK\n");
384
385         /* disconnec the user */
386         talloc_free(test_p);
387
388         return ret;
389 }
390
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
395                 case 25: Maximum 
396                 case 28: GenericAll
397                 case 29: GenericExecute
398    LookupDomain() on the policy handle returned from Connect5()
399 */
400 static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, 
401                                                    struct dcerpc_pipe *p)
402 {
403         NTSTATUS status;
404         struct samr_LookupDomain ld;
405         struct dom_sid2 *sid = NULL;
406         struct policy_handle ch;
407         struct lsa_String dn;
408         int i;
409         uint32_t mask;
410
411         printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
412         mask = 1;
413         for (i=0;i<33;i++) {    
414                 printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
415                 status = torture_samr_Connect5(tctx, p, mask, &ch);
416                 mask <<= 1;
417
418                 switch (i) {
419                 case 5:  
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));
426                                 return false;
427                         }
428
429                         ld.in.connect_handle = &ch;
430                         ld.in.domain_name    = &dn;
431                         ld.out.sid           = &sid;
432                         dn.string            = lp_workgroup(tctx->lp_ctx);
433
434                         status = dcerpc_samr_LookupDomain(p, tctx, &ld);
435                         if (!NT_STATUS_IS_OK(status)) {
436                                 printf("LookupDomain failed - %s\n", nt_errstr(status));
437                                 return false;
438                         }
439
440                         status = torture_samr_Close(tctx, p, &ch);
441                         if (!NT_STATUS_IS_OK(status)) {
442                                 printf("Close failed - %s\n", nt_errstr(status));
443                                 return false;
444                         }
445                         break;
446                 default:
447                         printf(" expecting to fail");
448
449                         if (!NT_STATUS_IS_OK(status)) {
450                                 printf(" OK\n");
451                                 continue;
452                         }
453
454                         ld.in.connect_handle = &ch;
455                         ld.in.domain_name    = &dn;
456                         ld.out.sid           = &sid;
457                         dn.string            = lp_workgroup(tctx->lp_ctx);
458
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));
462                                 return false;
463                         }
464
465                         status = torture_samr_Close(tctx, p, &ch);
466                         if (!NT_STATUS_IS_OK(status)) {
467                                 printf("Close failed - %s\n", nt_errstr(status));
468                                 return false;
469                         }
470                         break;
471                 }
472                 printf(" OK\n");
473         }
474
475         return true;
476 }
477
478 /* check which bits in accessmask allows us to OpenDomain()
479    by default we must specify at least one of :
480         samr/opendomain
481         Maximum 
482         GenericAll
483         GenericExecute
484    in the access mask to Connect5() in order to be allowed to perform
485    OpenDomain() on the policy handle returned from Connect5()
486 */
487 static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, 
488                                                    struct dcerpc_pipe *p)
489 {
490         NTSTATUS status;
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;
497         int i;
498         uint32_t mask;
499
500
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));
505                 return false;
506         }
507
508         ld.in.connect_handle = &ch;
509         ld.in.domain_name    = &dn;
510         ld.out.sid           = &sid;
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));
515                 return false;
516         }
517
518
519
520         printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
521         mask = 1;
522         for (i=0;i<33;i++) {    
523                 printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
524                 status = torture_samr_Connect5(tctx, p, mask, &ch);
525                 mask <<= 1;
526
527                 switch (i) {
528                 case 5:  
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));
535                                 return false;
536                         }
537
538                         od.in.connect_handle = &ch;
539                         od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
540                         od.in.sid = sid;
541                         od.out.domain_handle = &dh;
542
543                         status = dcerpc_samr_OpenDomain(p, tctx, &od);
544                         if (!NT_STATUS_IS_OK(status)) {
545                                 printf("OpenDomain failed - %s\n", nt_errstr(status));
546                                 return false;
547                         }
548
549                         status = torture_samr_Close(tctx, p, &dh);
550                         if (!NT_STATUS_IS_OK(status)) {
551                                 printf("Close failed - %s\n", nt_errstr(status));
552                                 return false;
553                         }
554
555                         status = torture_samr_Close(tctx, p, &ch);
556                         if (!NT_STATUS_IS_OK(status)) {
557                                 printf("Close failed - %s\n", nt_errstr(status));
558                                 return false;
559                         }
560                         break;
561                 default:
562                         printf(" expecting to fail");
563
564                         if (!NT_STATUS_IS_OK(status)) {
565                                 printf(" OK\n");
566                                 continue;
567                         }
568
569                         status = torture_samr_Close(tctx, p, &ch);
570                         if (!NT_STATUS_IS_OK(status)) {
571                                 printf("Close failed - %s\n", nt_errstr(status));
572                                 return false;
573                         }
574                         break;
575                 }
576                 printf(" OK\n");
577         }
578
579         return true;
580 }
581
582 static bool test_samr_connect(struct torture_context *tctx, 
583                                                    struct dcerpc_pipe *p)
584 {
585         void *testuser;
586         const char *testuser_passwd;
587         struct cli_credentials *test_credentials;
588         bool ret = true;
589         const struct dom_sid *test_sid;
590
591         /* create a test user */
592         testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx), 
593                                            ACB_NORMAL, &testuser_passwd);
594         if (!testuser) {
595                 printf("Failed to create test user\n");
596                 return false;
597         }
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), 
601                                    CRED_SPECIFIED);
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);
605
606
607         /* test which bits in the accessmask to Connect5 
608            will allow us to connect to the server 
609         */
610         if (!test_samr_accessmask_Connect5(tctx, p)) {
611                 ret = false;
612         }
613
614
615         /* test which bits in the accessmask to Connect5 will allow
616          * us to call EnumDomains() 
617          */
618         if (!test_samr_accessmask_EnumDomains(tctx, p)) {
619                 ret = false;
620         }
621
622         /* test which bits in the accessmask to Connect5 will allow
623          * us to call LookupDomain()
624          */
625         if (!test_samr_accessmask_LookupDomain(tctx, p)) {
626                 ret = false;
627         }
628
629
630         /* test which bits in the accessmask to Connect5 will allow
631          * us to call OpenDomain()
632          */
633         if (!test_samr_accessmask_OpenDomain(tctx, p)) {
634                 ret = false;
635         }
636
637         if (!torture_setting_bool(tctx, "samba3", false)) {
638
639         /* test if ACLs can be changed for the policy handle
640          * returned by Connect5
641          */
642         if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
643                 ret = false;
644         }
645
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
649          *   ReadControl
650          *   Samr/OpenDomain
651          *   Samr/EnumDomains
652          *   Samr/ConnectToServer
653          * is granted and should therefore not be able to connect when
654          * requesting SAMR_ACCESS_SHUTDOWN_SERVER
655          */
656         if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
657                 ret = false;
658         }
659
660         }
661
662         /* remove the test user */
663         torture_leave_domain(tctx, testuser);
664
665         return ret;
666 }
667
668 struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
669 {
670         struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
671         struct torture_rpc_tcase *tcase;
672
673         tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", 
674                                                                                           &ndr_table_samr);
675         
676         torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);
677
678         return suite;
679 }