b87a27ef867b7e54b3aa1be2e20c631a72a0419f
[ira/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
27 static TALLOC_CTX *ctx;
28
29 enum acl_mode { SMB_ACL_DELETE,
30                 SMB_ACL_MODIFY,
31                 SMB_ACL_ADD,
32                 SMB_ACL_SET,
33                 SMB_ACL_VIEW };
34
35 struct perm_value {
36         const char *perm;
37         uint32 mask;
38 };
39
40 /* These values discovered by inspection */
41
42 static const struct perm_value special_values[] = {
43         { "R", SEC_RIGHTS_FILE_READ },
44         { "W", SEC_RIGHTS_FILE_WRITE },
45         { "X", SEC_RIGHTS_FILE_EXECUTE },
46         { "D", SEC_STD_DELETE },
47         { "P", SEC_STD_WRITE_DAC },
48         { "O", SEC_STD_WRITE_OWNER },
49         { NULL, 0 },
50 };
51
52 #define SEC_RIGHTS_DIR_CHANGE ( SEC_RIGHTS_DIR_READ|SEC_STD_DELETE|\
53                                 SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE )
54
55 static const struct perm_value standard_values[] = {
56         { "READ",   SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE },
57         { "CHANGE", SEC_RIGHTS_DIR_CHANGE },
58         { "FULL",   SEC_RIGHTS_DIR_ALL },
59         { NULL, 0 },
60 };
61
62 /********************************************************************
63  print an ACE on a FILE
64 ********************************************************************/
65
66 static void print_ace(FILE *f, SEC_ACE *ace)
67 {
68         const struct perm_value *v;
69         int do_print = 0;
70         uint32 got_mask;
71
72         fprintf(f, "%s:", sid_string_tos(&ace->trustee));
73
74         /* Ace type */
75
76         if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
77                 fprintf(f, "ALLOWED");
78         } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
79                 fprintf(f, "DENIED");
80         } else {
81                 fprintf(f, "%d", ace->type);
82         }
83
84         /* Not sure what flags can be set in a file ACL */
85
86         fprintf(f, "/%d/", ace->flags);
87
88         /* Standard permissions */
89
90         for (v = standard_values; v->perm; v++) {
91                 if (ace->access_mask == v->mask) {
92                         fprintf(f, "%s", v->perm);
93                         return;
94                 }
95         }
96
97         /* Special permissions.  Print out a hex value if we have
98            leftover bits in the mask. */
99
100         got_mask = ace->access_mask;
101
102  again:
103         for (v = special_values; v->perm; v++) {
104                 if ((ace->access_mask & v->mask) == v->mask) {
105                         if (do_print) {
106                                 fprintf(f, "%s", v->perm);
107                         }
108                         got_mask &= ~v->mask;
109                 }
110         }
111
112         if (!do_print) {
113                 if (got_mask != 0) {
114                         fprintf(f, "0x%08x", ace->access_mask);
115                 } else {
116                         do_print = 1;
117                         goto again;
118                 }
119         }
120 }
121
122 /********************************************************************
123  print an ascii version of a security descriptor on a FILE handle
124 ********************************************************************/
125
126 static void sec_desc_print(FILE *f, SEC_DESC *sd)
127 {
128         uint32 i;
129
130         fprintf(f, "REVISION:%d\n", sd->revision);
131
132         /* Print owner and group sid */
133
134         fprintf(f, "OWNER:%s\n", sid_string_tos(sd->owner_sid));
135
136         fprintf(f, "GROUP:%s\n", sid_string_tos(sd->group_sid));
137
138         /* Print aces */
139         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
140                 SEC_ACE *ace = &sd->dacl->aces[i];
141                 fprintf(f, "ACL:");
142                 print_ace(f, ace);
143                 fprintf(f, "\n");
144         }
145 }
146
147 /********************************************************************
148  parse an ACE in the same format as print_ace()
149 ********************************************************************/
150
151 static bool parse_ace(SEC_ACE *ace, const char *orig_str)
152 {
153         char *p;
154         const char *cp;
155         char *tok;
156         unsigned int atype = 0;
157         unsigned int aflags = 0;
158         unsigned int amask = 0;
159         DOM_SID sid;
160         uint32_t mask;
161         const struct perm_value *v;
162         char *str = SMB_STRDUP(orig_str);
163         TALLOC_CTX *frame = talloc_stackframe();
164
165         if (!str) {
166                 TALLOC_FREE(frame);
167                 return False;
168         }
169
170         ZERO_STRUCTP(ace);
171         p = strchr_m(str,':');
172         if (!p) {
173                 fprintf(stderr, "ACE '%s': missing ':'.\n", orig_str);
174                 SAFE_FREE(str);
175                 TALLOC_FREE(frame);
176                 return False;
177         }
178         *p = '\0';
179         p++;
180         /* Try to parse numeric form */
181
182         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
183             string_to_sid(&sid, str)) {
184                 goto done;
185         }
186
187         /* Try to parse text form */
188
189         if (!string_to_sid(&sid, str)) {
190                 fprintf(stderr, "ACE '%s': failed to convert '%s' to SID\n",
191                         orig_str, str);
192                 SAFE_FREE(str);
193                 TALLOC_FREE(frame);
194                 return False;
195         }
196
197         cp = p;
198         if (!next_token_talloc(frame, &cp, &tok, "/")) {
199                 fprintf(stderr, "ACE '%s': failed to find '/' character.\n",
200                         orig_str);
201                 SAFE_FREE(str);
202                 TALLOC_FREE(frame);
203                 return False;
204         }
205
206         if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
207                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
208         } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
209                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
210         } else {
211                 fprintf(stderr, "ACE '%s': missing 'ALLOWED' or 'DENIED' "
212                         "entry at '%s'\n", orig_str, tok);
213                 SAFE_FREE(str);
214                 TALLOC_FREE(frame);
215                 return False;
216         }
217
218         /* Only numeric form accepted for flags at present */
219         /* no flags on share permissions */
220
221         if (!(next_token_talloc(frame, &cp, &tok, "/") &&
222               sscanf(tok, "%i", &aflags) && aflags == 0)) {
223                 fprintf(stderr, "ACE '%s': bad integer flags entry at '%s'\n",
224                         orig_str, tok);
225                 SAFE_FREE(str);
226                 TALLOC_FREE(frame);
227                 return False;
228         }
229
230         if (!next_token_talloc(frame, &cp, &tok, "/")) {
231                 fprintf(stderr, "ACE '%s': missing / at '%s'\n",
232                         orig_str, tok);
233                 SAFE_FREE(str);
234                 TALLOC_FREE(frame);
235                 return False;
236         }
237
238         if (strncmp(tok, "0x", 2) == 0) {
239                 if (sscanf(tok, "%i", &amask) != 1) {
240                         fprintf(stderr, "ACE '%s': bad hex number at '%s'\n",
241                                 orig_str, tok);
242                         TALLOC_FREE(frame);
243                         SAFE_FREE(str);
244                         return False;
245                 }
246                 goto done;
247         }
248
249         for (v = standard_values; v->perm; v++) {
250                 if (strcmp(tok, v->perm) == 0) {
251                         amask = v->mask;
252                         goto done;
253                 }
254         }
255
256         p = tok;
257
258         while(*p) {
259                 bool found = False;
260
261                 for (v = special_values; v->perm; v++) {
262                         if (v->perm[0] == *p) {
263                                 amask |= v->mask;
264                                 found = True;
265                         }
266                 }
267
268                 if (!found) {
269                         fprintf(stderr, "ACE '%s': bad permission value at "
270                                 "'%s'\n", orig_str, p);
271                         TALLOC_FREE(frame);
272                         SAFE_FREE(str);
273                         return False;
274                 }
275                 p++;
276         }
277
278         if (*p) {
279                 TALLOC_FREE(frame);
280                 SAFE_FREE(str);
281                 return False;
282         }
283
284  done:
285         mask = amask;
286         init_sec_ace(ace, &sid, atype, mask, aflags);
287         SAFE_FREE(str);
288         TALLOC_FREE(frame);
289         return True;
290 }
291
292
293 /********************************************************************
294 ********************************************************************/
295
296 static SEC_DESC* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size )
297 {
298         SEC_DESC *sd = NULL;
299         SEC_ACE *ace;
300         SEC_ACL *acl;
301         int num_ace;
302         const char *pacl;
303         int i;
304
305         if ( !szACL )
306                 return NULL;
307
308         pacl = szACL;
309         num_ace = count_chars( pacl, ',' ) + 1;
310
311         if ( !(ace = TALLOC_ZERO_ARRAY( mem_ctx, SEC_ACE, num_ace )) )
312                 return NULL;
313
314         for ( i=0; i<num_ace; i++ ) {
315                 char *end_acl = strchr_m( pacl, ',' );
316                 fstring acl_string;
317
318                 strncpy( acl_string, pacl, MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1) );
319                 acl_string[MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1)] = '\0';
320
321                 if ( !parse_ace( &ace[i], acl_string ) )
322                         return NULL;
323
324                 pacl = end_acl;
325                 pacl++;
326         }
327
328         if ( !(acl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) )
329                 return NULL;
330
331         sd = make_sec_desc( mem_ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
332                 NULL, NULL, NULL, acl, sd_size);
333
334         return sd;
335 }
336
337 /* add an ACE to a list of ACEs in a SEC_ACL */
338 static bool add_ace(TALLOC_CTX *mem_ctx, SEC_ACL **the_acl, SEC_ACE *ace)
339 {
340         SEC_ACL *new_ace;
341         SEC_ACE *aces;
342         if (! *the_acl) {
343                 return (((*the_acl) = make_sec_acl(mem_ctx, 3, 1, ace)) != NULL);
344         }
345
346         if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) {
347                 return False;
348         }
349         memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
350         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
351         new_ace = make_sec_acl(mem_ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
352         SAFE_FREE(aces);
353         (*the_acl) = new_ace;
354         return True;
355 }
356
357 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
358    However NT4 gives a "The information may have been modified by a
359    computer running Windows NT 5.0" if denied ACEs do not appear before
360    allowed ACEs. */
361
362 static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
363 {
364         if (sec_ace_equal(ace1, ace2))
365                 return 0;
366
367         if (ace1->type != ace2->type)
368                 return ace2->type - ace1->type;
369
370         if (sid_compare(&ace1->trustee, &ace2->trustee))
371                 return sid_compare(&ace1->trustee, &ace2->trustee);
372
373         if (ace1->flags != ace2->flags)
374                 return ace1->flags - ace2->flags;
375
376         if (ace1->access_mask != ace2->access_mask)
377                 return ace1->access_mask - ace2->access_mask;
378
379         if (ace1->size != ace2->size)
380                 return ace1->size - ace2->size;
381
382         return memcmp(ace1, ace2, sizeof(SEC_ACE));
383 }
384
385 static void sort_acl(SEC_ACL *the_acl)
386 {
387         uint32 i;
388         if (!the_acl) return;
389
390         qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare);
391
392         for (i=1;i<the_acl->num_aces;) {
393                 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
394                         int j;
395                         for (j=i; j<the_acl->num_aces-1; j++) {
396                                 the_acl->aces[j] = the_acl->aces[j+1];
397                         }
398                         the_acl->num_aces--;
399                 } else {
400                         i++;
401                 }
402         }
403 }
404
405
406 static int change_share_sec(TALLOC_CTX *mem_ctx, const char *sharename, char *the_acl, enum acl_mode mode)
407 {
408         SEC_DESC *sd = NULL;
409         SEC_DESC *old = NULL;
410         size_t sd_size = 0;
411         uint32 i, j;
412
413         if (mode != SMB_ACL_SET) {
414             if (!(old = get_share_security( mem_ctx, sharename, &sd_size )) ) {
415                 fprintf(stderr, "Unable to retrieve permissions for share [%s]\n", sharename);
416                 return -1;
417             }
418         }
419
420         if ( (mode != SMB_ACL_VIEW) && !(sd = parse_acl_string(mem_ctx, the_acl, &sd_size )) ) {
421                 fprintf( stderr, "Failed to parse acl\n");
422                 return -1;
423         }
424
425         switch (mode) {
426         case SMB_ACL_VIEW:
427                 sec_desc_print( stdout, old);
428                 return 0;
429         case SMB_ACL_DELETE:
430             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
431                 bool found = False;
432
433                 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
434                     if (sec_ace_equal(&sd->dacl->aces[i], &old->dacl->aces[j])) {
435                         uint32 k;
436                         for (k=j; k<old->dacl->num_aces-1;k++) {
437                             old->dacl->aces[k] = old->dacl->aces[k+1];
438                         }
439                         old->dacl->num_aces--;
440                         found = True;
441                         break;
442                     }
443                 }
444
445                 if (!found) {
446                 printf("ACL for ACE:");
447                 print_ace(stdout, &sd->dacl->aces[i]);
448                 printf(" not found\n");
449                 }
450             }
451
452             break;
453         case SMB_ACL_MODIFY:
454             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
455                 bool found = False;
456
457                 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
458                     if (sid_equal(&sd->dacl->aces[i].trustee,
459                         &old->dacl->aces[j].trustee)) {
460                         old->dacl->aces[j] = sd->dacl->aces[i];
461                         found = True;
462                     }
463                 }
464
465                 if (!found) {
466                     printf("ACL for SID %s not found\n",
467                            sid_string_tos(&sd->dacl->aces[i].trustee));
468                 }
469             }
470
471             if (sd->owner_sid) {
472                 old->owner_sid = sd->owner_sid;
473             }
474
475             if (sd->group_sid) {
476                 old->group_sid = sd->group_sid;
477             }
478             break;
479         case SMB_ACL_ADD:
480             for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
481                 add_ace(mem_ctx, &old->dacl, &sd->dacl->aces[i]);
482             }
483             break;
484         case SMB_ACL_SET:
485             old = sd;
486             break;
487         }
488
489         /* Denied ACE entries must come before allowed ones */
490         sort_acl(old->dacl);
491
492         if ( !set_share_security( sharename, old ) ) {
493             fprintf( stderr, "Failed to store acl for share [%s]\n", sharename );
494             return 2;
495         }
496         return 0;
497 }
498
499 /********************************************************************
500   main program
501 ********************************************************************/
502
503 int main(int argc, const char *argv[])
504 {
505         int opt;
506         int retval = 0;
507         enum acl_mode mode = SMB_ACL_SET;
508         static char *the_acl = NULL;
509         fstring sharename;
510         bool force_acl = False;
511         int snum;
512         poptContext pc;
513         bool initialize_sid = False;
514         struct poptOption long_options[] = {
515                 POPT_AUTOHELP
516                 { "remove", 'r', POPT_ARG_STRING, &the_acl, 'r', "Delete an ACE", "ACL" },
517                 { "modify", 'm', POPT_ARG_STRING, &the_acl, 'm', "Modify an acl", "ACL" },
518                 { "add", 'a', POPT_ARG_STRING, &the_acl, 'a', "Add an ACE", "ACL" },
519                 { "replace", 'R', POPT_ARG_STRING, &the_acl, 'R', "Set share mission ACL", "ACLS" },
520                 { "view", 'v', POPT_ARG_NONE, NULL, 'v', "View current share permissions" },
521                 { "machine-sid", 'M', POPT_ARG_NONE, NULL, 'M', "Initialize the machine SID" },
522                 { "force", 'F', POPT_ARG_NONE, NULL, 'F', "Force storing the ACL", "ACLS" },
523                 POPT_COMMON_SAMBA
524                 { NULL }
525         };
526
527         if ( !(ctx = talloc_stackframe()) ) {
528                 fprintf( stderr, "Failed to initialize talloc context!\n");
529                 return -1;
530         }
531
532         /* set default debug level to 1 regardless of what smb.conf sets */
533         setup_logging( "sharesec", True );
534         DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
535         dbf = x_stderr;
536         x_setbuf( x_stderr, NULL );
537
538         pc = poptGetContext("sharesec", argc, argv, long_options, 0);
539
540         poptSetOtherOptionHelp(pc, "sharename\n");
541
542         while ((opt = poptGetNextOpt(pc)) != -1) {
543                 switch (opt) {
544                 case 'r':
545                         the_acl = smb_xstrdup(poptGetOptArg(pc));
546                         mode = SMB_ACL_DELETE;
547                         break;
548
549                 case 'm':
550                         the_acl = smb_xstrdup(poptGetOptArg(pc));
551                         mode = SMB_ACL_MODIFY;
552                         break;
553
554                 case 'a':
555                         the_acl = smb_xstrdup(poptGetOptArg(pc));
556                         mode = SMB_ACL_ADD;
557                         break;
558                 case 'R':
559                         the_acl = smb_xstrdup(poptGetOptArg(pc));
560                         mode = SMB_ACL_SET;
561                         break;
562
563                 case 'v':
564                         mode = SMB_ACL_VIEW;
565                         break;
566
567                 case 'F':
568                         force_acl = True;
569                         break;
570
571                 case 'M':
572                         initialize_sid = True;
573                         break;
574                 }
575         }
576
577         setlinebuf(stdout);
578
579         load_case_tables();
580
581         lp_load( get_dyn_CONFIGFILE(), False, False, False, True );
582
583         /* check for initializing secrets.tdb first */
584
585         if ( initialize_sid ) {
586                 DOM_SID *sid = get_global_sam_sid();
587
588                 if ( !sid ) {
589                         fprintf( stderr, "Failed to retrieve Machine SID!\n");
590                         return 3;
591                 }
592
593                 printf ("%s\n", sid_string_tos( sid ) );
594                 return 0;
595         }
596
597         if ( mode == SMB_ACL_VIEW && force_acl ) {
598                 fprintf( stderr, "Invalid combination of -F and -v\n");
599                 return -1;
600         }
601
602         /* get the sharename */
603
604         if(!poptPeekArg(pc)) {
605                 poptPrintUsage(pc, stderr, 0);
606                 return -1;
607         }
608
609         fstrcpy(sharename, poptGetArg(pc));
610
611         snum = lp_servicenumber( sharename );
612
613         if ( snum == -1 && !force_acl ) {
614                 fprintf( stderr, "Invalid sharename: %s\n", sharename);
615                 return -1;
616         }
617
618         retval = change_share_sec(ctx, sharename, the_acl, mode);
619
620         talloc_destroy(ctx);
621
622         return retval;
623 }