s3/sharesec: use security_ace_equal instead of sec_ace_equal
[metze/samba/wip.git] / source3 / utils / sharesec.c
1 /*
2  *  Unix SMB/Netbios implementation.
3  *  Utility for managing share permissions
4  *
5  *  Copyright (C) Tim Potter                    2000
6  *  Copyright (C) Jeremy Allison                2000
7  *  Copyright (C) Jelmer Vernooij               2003
8  *  Copyright (C) Gerald (Jerry) Carter         2005.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #include "includes.h"
26 #include "popt_common.h"
27 #include "../libcli/security/security.h"
28 #include "../librpc/gen_ndr/ndr_security.h"
29 #include "passdb/machine_sid.h"
30
31 static TALLOC_CTX *ctx;
32
33 enum acl_mode { SMB_ACL_DELETE,
34                 SMB_ACL_MODIFY,
35                 SMB_ACL_ADD,
36                 SMB_ACL_SET,
37                 SMB_SD_DELETE,
38                 SMB_SD_SETSDDL,
39                 SMB_SD_VIEWSDDL,
40                 SMB_ACL_VIEW,
41                 SMB_ACL_VIEW_ALL };
42
43 struct perm_value {
44         const char *perm;
45         uint32 mask;
46 };
47
48 /* These values discovered by inspection */
49
50 static const struct perm_value special_values[] = {
51         { "R", SEC_RIGHTS_FILE_READ },
52         { "W", SEC_RIGHTS_FILE_WRITE },
53         { "X", SEC_RIGHTS_FILE_EXECUTE },
54         { "D", SEC_STD_DELETE },
55         { "P", SEC_STD_WRITE_DAC },
56         { "O", SEC_STD_WRITE_OWNER },
57         { NULL, 0 },
58 };
59
60 #define SEC_RIGHTS_DIR_CHANGE ( SEC_RIGHTS_DIR_READ|SEC_STD_DELETE|\
61                                 SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE )
62
63 static const struct perm_value standard_values[] = {
64         { "READ",   SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE },
65         { "CHANGE", SEC_RIGHTS_DIR_CHANGE },
66         { "FULL",   SEC_RIGHTS_DIR_ALL },
67         { NULL, 0 },
68 };
69
70 /********************************************************************
71  parse an ACE in the same format as print_ace()
72 ********************************************************************/
73
74 static bool parse_ace(struct security_ace *ace, const char *orig_str)
75 {
76         char *p;
77         const char *cp;
78         char *tok;
79         unsigned int atype = 0;
80         unsigned int aflags = 0;
81         unsigned int amask = 0;
82         struct dom_sid sid;
83         uint32_t mask;
84         const struct perm_value *v;
85         char *str = SMB_STRDUP(orig_str);
86         TALLOC_CTX *frame = talloc_stackframe();
87
88         if (!str) {
89                 TALLOC_FREE(frame);
90                 return False;
91         }
92
93         ZERO_STRUCTP(ace);
94         p = strchr_m(str,':');
95         if (!p) {
96                 fprintf(stderr, "ACE '%s': missing ':'.\n", orig_str);
97                 SAFE_FREE(str);
98                 TALLOC_FREE(frame);
99                 return False;
100         }
101         *p = '\0';
102         p++;
103         /* Try to parse numeric form */
104
105         if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
106             string_to_sid(&sid, str)) {
107                 goto done;
108         }
109
110         /* Try to parse text form */
111
112         if (!string_to_sid(&sid, str)) {
113                 fprintf(stderr, "ACE '%s': failed to convert '%s' to SID\n",
114                         orig_str, str);
115                 SAFE_FREE(str);
116                 TALLOC_FREE(frame);
117                 return False;
118         }
119
120         cp = p;
121         if (!next_token_talloc(frame, &cp, &tok, "/")) {
122                 fprintf(stderr, "ACE '%s': failed to find '/' character.\n",
123                         orig_str);
124                 SAFE_FREE(str);
125                 TALLOC_FREE(frame);
126                 return False;
127         }
128
129         if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
130                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
131         } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
132                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
133         } else {
134                 fprintf(stderr, "ACE '%s': missing 'ALLOWED' or 'DENIED' "
135                         "entry at '%s'\n", orig_str, tok);
136                 SAFE_FREE(str);
137                 TALLOC_FREE(frame);
138                 return False;
139         }
140
141         /* Only numeric form accepted for flags at present */
142         /* no flags on share permissions */
143
144         if (!(next_token_talloc(frame, &cp, &tok, "/") &&
145               sscanf(tok, "%u", &aflags) && aflags == 0)) {
146                 fprintf(stderr, "ACE '%s': bad integer flags entry at '%s'\n",
147                         orig_str, tok);
148                 SAFE_FREE(str);
149                 TALLOC_FREE(frame);
150                 return False;
151         }
152
153         if (!next_token_talloc(frame, &cp, &tok, "/")) {
154                 fprintf(stderr, "ACE '%s': missing / at '%s'\n",
155                         orig_str, tok);
156                 SAFE_FREE(str);
157                 TALLOC_FREE(frame);
158                 return False;
159         }
160
161         if (strncmp(tok, "0x", 2) == 0) {
162                 if (sscanf(tok, "%u", &amask) != 1) {
163                         fprintf(stderr, "ACE '%s': bad hex number at '%s'\n",
164                                 orig_str, tok);
165                         TALLOC_FREE(frame);
166                         SAFE_FREE(str);
167                         return False;
168                 }
169                 goto done;
170         }
171
172         for (v = standard_values; v->perm; v++) {
173                 if (strcmp(tok, v->perm) == 0) {
174                         amask = v->mask;
175                         goto done;
176                 }
177         }
178
179         p = tok;
180
181         while(*p) {
182                 bool found = False;
183
184                 for (v = special_values; v->perm; v++) {
185                         if (v->perm[0] == *p) {
186                                 amask |= v->mask;
187                                 found = True;
188                         }
189                 }
190
191                 if (!found) {
192                         fprintf(stderr, "ACE '%s': bad permission value at "
193                                 "'%s'\n", orig_str, p);
194                         TALLOC_FREE(frame);
195                         SAFE_FREE(str);
196                         return False;
197                 }
198                 p++;
199         }
200
201         if (*p) {
202                 TALLOC_FREE(frame);
203                 SAFE_FREE(str);
204                 return False;
205         }
206
207  done:
208         mask = amask;
209         init_sec_ace(ace, &sid, atype, mask, aflags);
210         SAFE_FREE(str);
211         TALLOC_FREE(frame);
212         return True;
213 }
214
215
216 /********************************************************************
217 ********************************************************************/
218
219 static struct security_descriptor* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size )
220 {
221         struct security_descriptor *sd = NULL;
222         struct security_ace *ace;
223         struct security_acl *theacl;
224         int num_ace;
225         const char *pacl;
226         int i;
227
228         if ( !szACL )
229                 return NULL;
230
231         pacl = szACL;
232         num_ace = count_chars( pacl, ',' ) + 1;
233
234         if ( !(ace = talloc_zero_array( mem_ctx, struct security_ace, num_ace )) )
235                 return NULL;
236
237         for ( i=0; i<num_ace; i++ ) {
238                 char *end_acl = strchr_m( pacl, ',' );
239                 fstring acl_string;
240
241                 strncpy( acl_string, pacl, MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1) );
242                 acl_string[MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1)] = '\0';
243
244                 if ( !parse_ace( &ace[i], acl_string ) )
245                         return NULL;
246
247                 pacl = end_acl;
248                 pacl++;
249         }
250
251         if ( !(theacl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) )
252                 return NULL;
253
254         sd = make_sec_desc( mem_ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE,
255                 NULL, NULL, NULL, theacl, sd_size);
256
257         return sd;
258 }
259
260 /* add an ACE to a list of ACEs in a struct security_acl */
261 static bool add_ace(TALLOC_CTX *mem_ctx, struct security_acl **the_acl, struct security_ace *ace)
262 {
263         struct security_acl *new_ace;
264         struct security_ace *aces;
265         if (! *the_acl) {
266                 return (((*the_acl) = make_sec_acl(mem_ctx, 3, 1, ace)) != NULL);
267         }
268
269         if (!(aces = SMB_CALLOC_ARRAY(struct security_ace, 1+(*the_acl)->num_aces))) {
270                 return False;
271         }
272         memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(struct
273         security_ace));
274         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(struct security_ace));
275         new_ace = make_sec_acl(mem_ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
276         SAFE_FREE(aces);
277         (*the_acl) = new_ace;
278         return True;
279 }
280
281 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
282    However NT4 gives a "The information may have been modified by a
283    computer running Windows NT 5.0" if denied ACEs do not appear before
284    allowed ACEs. */
285
286 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
287 {
288         if (security_ace_equal(ace1, ace2))
289                 return 0;
290
291         if (ace1->type != ace2->type)
292                 return ace2->type - ace1->type;
293
294         if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
295                 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
296
297         if (ace1->flags != ace2->flags)
298                 return ace1->flags - ace2->flags;
299
300         if (ace1->access_mask != ace2->access_mask)
301                 return ace1->access_mask - ace2->access_mask;
302
303         if (ace1->size != ace2->size)
304                 return ace1->size - ace2->size;
305
306         return memcmp(ace1, ace2, sizeof(struct security_ace));
307 }
308
309 static void sort_acl(struct security_acl *the_acl)
310 {
311         uint32 i;
312         if (!the_acl) return;
313
314         TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
315
316         for (i=1;i<the_acl->num_aces;) {
317                 if (security_ace_equal(&the_acl->aces[i-1],
318                                        &the_acl->aces[i])) {
319                         int j;
320                         for (j=i; j<the_acl->num_aces-1; j++) {
321                                 the_acl->aces[j] = the_acl->aces[j+1];
322                         }
323                         the_acl->num_aces--;
324                 } else {
325                         i++;
326                 }
327         }
328 }
329
330
331 static int change_share_sec(TALLOC_CTX *mem_ctx, const char *sharename, char *the_acl, enum acl_mode mode)
332 {
333         struct security_descriptor *sd = NULL;
334         struct security_descriptor *old = NULL;
335         size_t sd_size = 0;
336         uint32 i, j;
337         char *sd_str;
338
339         if (mode != SMB_ACL_SET && mode != SMB_SD_DELETE) {
340             if (!(old = get_share_security( mem_ctx, sharename, &sd_size )) ) {
341                 fprintf(stderr, "Unable to retrieve permissions for share "
342                         "[%s]\n", sharename);
343                 return -1;
344             }
345         }
346
347         if ( (mode != SMB_ACL_VIEW && mode != SMB_SD_DELETE) &&
348             !(sd = parse_acl_string(mem_ctx, the_acl, &sd_size )) ) {
349                 fprintf( stderr, "Failed to parse acl\n");
350                 return -1;
351         }
352
353         switch (mode) {
354         case SMB_ACL_VIEW_ALL:
355                 /* should not happen */
356                 return 0;
357         case SMB_ACL_VIEW:
358                 sd_str = ndr_print_struct_string(mem_ctx,
359                                 (ndr_print_fn_t)ndr_print_security_descriptor,
360                                 "", old);
361                 fprintf(stdout, "%s\n", sd_str);
362                 talloc_free(sd_str);
363                 return 0;
364         case SMB_ACL_DELETE:
365             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
366                 bool found = False;
367
368                 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
369                     if (security_ace_equal(&sd->dacl->aces[i],
370                                            &old->dacl->aces[j])) {
371                         uint32 k;
372                         for (k=j; k<old->dacl->num_aces-1;k++) {
373                             old->dacl->aces[k] = old->dacl->aces[k+1];
374                         }
375                         old->dacl->num_aces--;
376                         found = True;
377                         break;
378                     }
379                 }
380
381                 if (!found) {
382                         sd_str = ndr_print_struct_string(mem_ctx,
383                                         (ndr_print_fn_t)ndr_print_security_ace,
384                                         "", &sd->dacl->aces[i]);
385                         printf("ACL for ACE: %s not found\n", sd_str);
386                         talloc_free(sd_str);
387                 }
388             }
389             break;
390         case SMB_ACL_MODIFY:
391             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
392                 bool found = False;
393
394                 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
395                     if (dom_sid_equal(&sd->dacl->aces[i].trustee,
396                         &old->dacl->aces[j].trustee)) {
397                         old->dacl->aces[j] = sd->dacl->aces[i];
398                         found = True;
399                     }
400                 }
401
402                 if (!found) {
403                     printf("ACL for SID %s not found\n",
404                            sid_string_tos(&sd->dacl->aces[i].trustee));
405                 }
406             }
407
408             if (sd->owner_sid) {
409                 old->owner_sid = sd->owner_sid;
410             }
411
412             if (sd->group_sid) {
413                 old->group_sid = sd->group_sid;
414             }
415             break;
416         case SMB_ACL_ADD:
417             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
418                 add_ace(mem_ctx, &old->dacl, &sd->dacl->aces[i]);
419             }
420             break;
421         case SMB_ACL_SET:
422             old = sd;
423             break;
424         case SMB_SD_DELETE:
425             if (!delete_share_security(sharename)) {
426                 fprintf( stderr, "Failed to delete security descriptor for "
427                          "share [%s]\n", sharename );
428                 return -1;
429             }
430             return 0;
431         default:
432                 fprintf(stderr, "invalid command\n");
433                 return -1;
434         }
435
436         /* Denied ACE entries must come before allowed ones */
437         sort_acl(old->dacl);
438
439         if ( !set_share_security( sharename, old ) ) {
440             fprintf( stderr, "Failed to store acl for share [%s]\n", sharename );
441             return 2;
442         }
443         return 0;
444 }
445
446 static int set_sharesec_sddl(const char *sharename, const char *sddl)
447 {
448         struct security_descriptor *sd;
449         bool ret;
450
451         sd = sddl_decode(talloc_tos(), sddl, get_global_sam_sid());
452         if (sd == NULL) {
453                 fprintf(stderr, "Failed to parse acl\n");
454                 return -1;
455         }
456
457         ret = set_share_security(sharename, sd);
458         TALLOC_FREE(sd);
459         if (!ret) {
460                 fprintf(stderr, "Failed to store acl for share [%s]\n",
461                         sharename);
462                 return -1;
463         }
464
465         return 0;
466 }
467
468 static int view_sharesec_sddl(const char *sharename)
469 {
470         struct security_descriptor *sd;
471         size_t sd_size;
472         char *acl;
473
474         sd = get_share_security(talloc_tos(), sharename, &sd_size);
475         if (sd == NULL) {
476                 fprintf(stderr, "Unable to retrieve permissions for share "
477                         "[%s]\n", sharename);
478                 return -1;
479         }
480
481         acl = sddl_encode(talloc_tos(), sd, get_global_sam_sid());
482         TALLOC_FREE(sd);
483         if (acl == NULL) {
484                 fprintf(stderr, "Unable to sddl-encode permissions for share "
485                         "[%s]\n", sharename);
486                 return -1;
487         }
488         printf("%s\n", acl);
489         TALLOC_FREE(acl);
490         return 0;
491 }
492
493 /********************************************************************
494   main program
495 ********************************************************************/
496
497 enum {
498         OPT_VIEW_ALL = 1000,
499 };
500
501 int main(int argc, const char *argv[])
502 {
503         int opt;
504         int retval = 0;
505         enum acl_mode mode = SMB_ACL_SET;
506         static char *the_acl = NULL;
507         fstring sharename;
508         bool force_acl = False;
509         int snum;
510         poptContext pc;
511         bool initialize_sid = False;
512         struct poptOption long_options[] = {
513                 POPT_AUTOHELP
514                 { "remove", 'r', POPT_ARG_STRING, &the_acl, 'r', "Remove ACEs", "ACL" },
515                 { "modify", 'm', POPT_ARG_STRING, &the_acl, 'm', "Modify existing ACEs", "ACL" },
516                 { "add", 'a', POPT_ARG_STRING, &the_acl, 'a', "Add ACEs", "ACL" },
517                 { "replace", 'R', POPT_ARG_STRING, &the_acl, 'R', "Overwrite share permission ACL", "ACLS" },
518                 { "delete", 'D', POPT_ARG_NONE, NULL, 'D', "Delete the entire security descriptor" },
519                 { "setsddl", 'S', POPT_ARG_STRING, the_acl, 'S',
520                   "Set the SD in sddl format" },
521                 { "viewsddl", 'V', POPT_ARG_NONE, the_acl, 'V',
522                   "View the SD in sddl format" },
523                 { "view", 'v', POPT_ARG_NONE, NULL, 'v', "View current share permissions" },
524                 { "view-all", 0, POPT_ARG_NONE, NULL, OPT_VIEW_ALL,
525                   "View all current share permissions" },
526                 { "machine-sid", 'M', POPT_ARG_NONE, NULL, 'M', "Initialize the machine SID" },
527                 { "force", 'F', POPT_ARG_NONE, NULL, 'F', "Force storing the ACL", "ACLS" },
528                 POPT_COMMON_SAMBA
529                 { NULL }
530         };
531
532         if ( !(ctx = talloc_stackframe()) ) {
533                 fprintf( stderr, "Failed to initialize talloc context!\n");
534                 return -1;
535         }
536
537         /* set default debug level to 1 regardless of what smb.conf sets */
538         setup_logging( "sharesec", DEBUG_STDERR);
539
540         load_case_tables();
541
542         lp_set_cmdline("log level", "1");
543
544         pc = poptGetContext("sharesec", argc, argv, long_options, 0);
545
546         poptSetOtherOptionHelp(pc, "sharename\n");
547
548         while ((opt = poptGetNextOpt(pc)) != -1) {
549                 switch (opt) {
550                 case 'r':
551                         the_acl = smb_xstrdup(poptGetOptArg(pc));
552                         mode = SMB_ACL_DELETE;
553                         break;
554
555                 case 'm':
556                         the_acl = smb_xstrdup(poptGetOptArg(pc));
557                         mode = SMB_ACL_MODIFY;
558                         break;
559
560                 case 'a':
561                         the_acl = smb_xstrdup(poptGetOptArg(pc));
562                         mode = SMB_ACL_ADD;
563                         break;
564
565                 case 'R':
566                         the_acl = smb_xstrdup(poptGetOptArg(pc));
567                         mode = SMB_ACL_SET;
568                         break;
569
570                 case 'D':
571                         mode = SMB_SD_DELETE;
572                         break;
573
574                 case 'S':
575                         mode = SMB_SD_SETSDDL;
576                         the_acl = smb_xstrdup(poptGetOptArg(pc));
577                         break;
578
579                 case 'V':
580                         mode = SMB_SD_VIEWSDDL;
581                         break;
582
583                 case 'v':
584                         mode = SMB_ACL_VIEW;
585                         break;
586
587                 case 'F':
588                         force_acl = True;
589                         break;
590
591                 case 'M':
592                         initialize_sid = True;
593                         break;
594                 case OPT_VIEW_ALL:
595                         mode = SMB_ACL_VIEW_ALL;
596                         break;
597                 }
598         }
599
600         setlinebuf(stdout);
601
602         lp_load_with_registry_shares( get_dyn_CONFIGFILE(), False, False, False,
603                                       True );
604
605         /* check for initializing secrets.tdb first */
606
607         if ( initialize_sid ) {
608                 struct dom_sid *sid = get_global_sam_sid();
609
610                 if ( !sid ) {
611                         fprintf( stderr, "Failed to retrieve Machine SID!\n");
612                         return 3;
613                 }
614
615                 printf ("%s\n", sid_string_tos( sid ) );
616                 return 0;
617         }
618
619         if ( mode == SMB_ACL_VIEW && force_acl ) {
620                 fprintf( stderr, "Invalid combination of -F and -v\n");
621                 return -1;
622         }
623
624         if (mode == SMB_ACL_VIEW_ALL) {
625                 int i;
626
627                 for (i=0; i<lp_numservices(); i++) {
628                         TALLOC_CTX *frame = talloc_stackframe();
629                         const char *service = lp_servicename(frame, i);
630
631                         if (service == NULL) {
632                                 continue;
633                         }
634
635                         printf("[%s]\n", service);
636                         change_share_sec(frame, service, NULL, SMB_ACL_VIEW);
637                         printf("\n");
638                         TALLOC_FREE(frame);
639                 }
640                 goto done;
641         }
642
643         /* get the sharename */
644
645         if(!poptPeekArg(pc)) {
646                 poptPrintUsage(pc, stderr, 0);
647                 return -1;
648         }
649
650         fstrcpy(sharename, poptGetArg(pc));
651
652         snum = lp_servicenumber( sharename );
653
654         if ( snum == -1 && !force_acl ) {
655                 fprintf( stderr, "Invalid sharename: %s\n", sharename);
656                 return -1;
657         }
658
659         switch (mode) {
660         case SMB_SD_SETSDDL:
661                 retval = set_sharesec_sddl(sharename, the_acl);
662                 break;
663         case SMB_SD_VIEWSDDL:
664                 retval = view_sharesec_sddl(sharename);
665                 break;
666         default:
667                 retval = change_share_sec(ctx, sharename, the_acl, mode);
668                 break;
669         }
670
671 done:
672         talloc_destroy(ctx);
673
674         return retval;
675 }