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