Fixed memory leaks in lsa_XX calls. Fixed memory leaks in smbcacls. Merged
[amitay/samba.git] / source3 / utils / smbcacls.c
1 /* 
2    Unix SMB/Netbios implementation.
3    ACL get/set utility
4    Version 3.0
5    
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter      2000
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 static fstring password;
27 static fstring username;
28 static fstring server;
29 static int got_pass;
30 static int test_args;
31
32 /* numeric is set when the user wants numeric SIDs and ACEs rather
33    than going via LSA calls to resolve them */
34 static int numeric;
35
36 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD};
37
38 struct perm_value {
39         char *perm;
40         uint32 mask;
41 };
42
43 /* These values discovered by inspection */
44
45 static struct perm_value special_values[] = {
46         { "R", 0x00120089 },
47         { "W", 0x00120116 },
48         { "X", 0x001200a0 },
49         { "D", 0x00010000 },
50         { "P", 0x00040000 },
51         { "O", 0x00080000 },
52         { NULL, 0 },
53 };
54
55 static struct perm_value standard_values[] = {
56         { "READ",   0x001200a9 },
57         { "CHANGE", 0x001301bf },
58         { "FULL",   0x001f01ff },
59         { NULL, 0 },
60 };
61
62 struct cli_state lsa_cli;
63 POLICY_HND pol;
64 struct ntuser_creds creds;
65 BOOL got_policy_hnd;
66
67 /* Open cli connection and policy handle */
68
69 static BOOL open_policy_hnd(void)
70 {
71         creds.pwd.null_pwd = 1;
72
73         /* Initialise cli LSA connection */
74
75         if (!lsa_cli.initialised && 
76             !cli_lsa_initialise(&lsa_cli, server, &creds)) {
77                 return False;
78         }
79
80         /* Open policy handle */
81
82         if (!got_policy_hnd) {
83                 if (cli_lsa_open_policy(&lsa_cli, True, 
84                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &pol)
85                     != NT_STATUS_NOPROBLEMO) {
86                         return False;
87                 }
88
89                 got_policy_hnd = True;
90         }
91         
92         return True;
93 }
94
95 /* convert a SID to a string, either numeric or username/group */
96 static void SidToString(fstring str, DOM_SID *sid)
97 {
98         char **names = NULL;
99         uint32 *types = NULL;
100         int num_names;
101
102         sid_to_string(str, sid);
103
104         if (numeric) return;
105
106         /* Ask LSA to convert the sid to a name */
107
108         if (open_policy_hnd() &&
109             cli_lsa_lookup_sids(&lsa_cli, &pol, 1, sid, &names, &types, 
110                                 &num_names) == NT_STATUS_NOPROBLEMO) {
111
112                 /* Converted OK */
113
114                 fstrcpy(str, names[0]);
115
116                 safe_free(names[0]);
117                 safe_free(names);
118                 safe_free(types);
119         }
120 }
121
122 /* convert a string to a SID, either numeric or username/group */
123 static BOOL StringToSid(DOM_SID *sid, char *str)
124 {
125         uint32 *types = NULL;
126         DOM_SID *sids = NULL;
127         int num_sids;
128         BOOL result = True;
129         
130         /* Short cut */
131
132         if (strncmp(str, "S-", 2) == 0) {
133                 result = string_to_sid(sid, str);
134                 goto done;
135         }
136
137         if (open_policy_hnd() &&
138             cli_lsa_lookup_names(&lsa_cli, &pol, 1, &str, &sids, &types, 
139                                  &num_sids) != NT_STATUS_NOPROBLEMO) {
140                 result = string_to_sid(sid, str);
141                 goto done;
142         }
143
144         sid_copy(sid, &sids[0]);
145
146         safe_free(sids);
147         safe_free(types);
148
149  done:
150
151         return result;
152 }
153
154
155 /* print an ACE on a FILE, using either numeric or ascii representation */
156 static void print_ace(FILE *f, SEC_ACE *ace)
157 {
158         struct perm_value *v;
159         fstring sidstr;
160         int do_print = 0;
161         uint32 got_mask;
162
163         SidToString(sidstr, &ace->sid);
164
165         fprintf(f, "%s:", sidstr);
166
167         if (numeric) {
168                 fprintf(f, "%d/%d/0x%08x\n", 
169                         ace->type, ace->flags, ace->info.mask);
170                 return;
171         }
172
173         /* Ace type */
174
175         if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
176                 fprintf(f, "ALLOWED");
177         } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
178                 fprintf(f, "DENIED");
179         } else {
180                 fprintf(f, "%d", ace->type);
181         }
182
183         /* Not sure what flags can be set in a file ACL */
184
185         fprintf(f, "/%d/", ace->flags);
186
187         /* Standard permissions */
188
189         for (v = standard_values; v->perm; v++) {
190                 if (ace->info.mask == v->mask) {
191                         fprintf(f, "%s\n", v->perm);
192                         return;
193                 }
194         }
195
196         /* Special permissions.  Print out a hex value if we have
197            leftover bits in the mask. */
198
199         got_mask = ace->info.mask;
200
201  again:
202         for (v = special_values; v->perm; v++) {
203                 if ((ace->info.mask & v->mask) == v->mask) {
204                         if (do_print) {
205                                 fprintf(f, "%s", v->perm);
206                         }
207                         got_mask &= ~v->mask;
208                 }
209         }
210
211         if (!do_print) {
212                 if (got_mask != 0) {
213                         fprintf(f, "0x%08x", ace->info.mask);
214                 } else {
215                         do_print = 1;
216                         goto again;
217                 }
218         }
219
220         fprintf(f, "\n");
221 }
222
223
224 /* parse an ACE in the same format as print_ace() */
225 static BOOL parse_ace(SEC_ACE *ace, char *str)
226 {
227         char *p;
228         fstring tok;
229         unsigned atype, aflags, amask;
230         DOM_SID sid;
231         SEC_ACCESS mask;
232         struct perm_value *v;
233
234         ZERO_STRUCTP(ace);
235         p = strchr(str,':');
236         if (!p) return False;
237         *p = '\0';
238         p++;
239
240         /* Try to parse numeric form */
241
242         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
243             StringToSid(&sid, str)) {
244                 goto done;
245         }
246
247         /* Try to parse text form */
248
249         if (!StringToSid(&sid, str)) {
250                 return False;
251         }
252
253         if (!next_token(&p, tok, "/", sizeof(fstring))) {
254                 return False;
255         }
256
257         if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
258                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
259         } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
260                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
261         } else {
262                 return False;
263         }
264
265         /* Only numeric form accepted for flags at present */
266
267         if (!(next_token(NULL, tok, "/", sizeof(fstring)) &&
268               sscanf(tok, "%i", &aflags))) {
269                 return False;
270         }
271
272         if (!next_token(NULL, tok, "/", sizeof(fstring))) {
273                 return False;
274         }
275
276         if (strncmp(tok, "0x", 2) == 0) {
277                 if (sscanf(tok, "%i", &amask) != 1) {
278                         return False;
279                 }
280                 goto done;
281         }
282
283         for (v = standard_values; v->perm; v++) {
284                 if (strcmp(tok, v->perm) == 0) {
285                         amask = v->mask;
286                         goto done;
287                 }
288         }
289
290         p = tok;
291
292         while(*p) {
293                 BOOL found = False;
294
295                 for (v = special_values; v->perm; v++) {
296                         if (v->perm[0] == *p) {
297                                 amask |= v->mask;
298                                 found = True;
299                         }
300                 }
301
302                 if (!found) return False;
303                 p++;
304         }
305
306         if (*p) {
307                 return False;
308         }
309
310  done:
311         mask.mask = amask;
312         init_sec_ace(ace, &sid, atype, mask, aflags);
313         return True;
314 }
315
316 /* add an ACE to a list of ACEs in a SEC_ACL */
317 static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
318 {
319         SEC_ACL *new;
320         SEC_ACE *aces;
321         if (! *the_acl) {
322                 (*the_acl) = make_sec_acl(3, 1, ace);
323                 return True;
324         }
325
326         aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
327         memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
328         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
329         new = make_sec_acl((*the_acl)->revision,1+(*the_acl)->num_aces, aces);
330         free_sec_acl(the_acl);
331         free(aces);
332         (*the_acl) = new;
333         return True;
334 }
335
336 /* parse a ascii version of a security descriptor */
337 static SEC_DESC *sec_desc_parse(char *str)
338 {
339         char *p = str;
340         fstring tok;
341         SEC_DESC *ret;
342         unsigned sd_size;
343         DOM_SID *grp_sid=NULL, *owner_sid=NULL;
344         SEC_ACL *dacl=NULL;
345         int revision=1;
346
347         while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
348
349                 if (strncmp(tok,"REVISION:", 9) == 0) {
350                         revision = strtol(tok+9, NULL, 16);
351                         continue;
352                 }
353
354                 if (strncmp(tok,"OWNER:", 6) == 0) {
355                         owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
356                         if (!owner_sid ||
357                             !StringToSid(owner_sid, tok+6)) {
358                                 printf("Failed to parse owner sid\n");
359                                 return NULL;
360                         }
361                         continue;
362                 }
363
364                 if (strncmp(tok,"GROUP:", 6) == 0) {
365                         grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
366                         if (!grp_sid ||
367                             !StringToSid(grp_sid, tok+6)) {
368                                 printf("Failed to parse group sid\n");
369                                 return NULL;
370                         }
371                         continue;
372                 }
373
374                 if (strncmp(tok,"ACL:", 4) == 0) {
375                         SEC_ACE ace;
376                         if (!parse_ace(&ace, tok+4)) {
377                                 printf("Failed to parse ACL %s\n", tok);
378                                 return NULL;
379                         }
380                         if(!add_ace(&dacl, &ace)) {
381                                 printf("Failed to add ACL %s\n", tok);
382                                 return NULL;
383                         }
384                         continue;
385                 }
386
387                 printf("Failed to parse security descriptor\n");
388                 return NULL;
389         }
390
391         ret = make_sec_desc(revision, owner_sid, grp_sid, 
392                             NULL, dacl, &sd_size);
393
394         free_sec_acl(&dacl);
395
396         if (grp_sid) free(grp_sid);
397         if (owner_sid) free(owner_sid);
398
399         return ret;
400 }
401
402
403 /* print a ascii version of a security descriptor on a FILE handle */
404 static void sec_desc_print(FILE *f, SEC_DESC *sd)
405 {
406         fstring sidstr;
407         int i;
408
409         printf("REVISION:%d\n", sd->revision);
410
411         /* Print owner and group sid */
412
413         if (sd->owner_sid) {
414                 SidToString(sidstr, sd->owner_sid);
415         } else {
416                 fstrcpy(sidstr, "");
417         }
418
419         printf("OWNER:%s\n", sidstr);
420
421         if (sd->grp_sid) {
422                 SidToString(sidstr, sd->grp_sid);
423         } else {
424                 fstrcpy(sidstr, "");
425         }
426
427         fprintf(f, "GROUP:%s\n", sidstr);
428
429         /* Print aces */
430         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
431                 SEC_ACE *ace = &sd->dacl->ace[i];
432                 fprintf(f, "ACL:");
433                 print_ace(f, ace);
434         }
435
436 }
437
438
439 /***************************************************** 
440 dump the acls for a file
441 *******************************************************/
442 static void cacl_dump(struct cli_state *cli, char *filename)
443 {
444         int fnum;
445         SEC_DESC *sd;
446
447         if (test_args) return;
448
449         fnum = cli_nt_create(cli, filename, 0x20000);
450         if (fnum == -1) {
451                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
452                 return;
453         }
454
455         sd = cli_query_secdesc(cli, fnum);
456
457         if (!sd) {
458                 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
459                 return;
460         }
461
462         sec_desc_print(stdout, sd);
463
464         free_sec_desc(&sd);
465
466         cli_close(cli, fnum);
467 }
468
469 /***************************************************** 
470 set the ACLs on a file given an ascii description
471 *******************************************************/
472 static void cacl_set(struct cli_state *cli, char *filename, 
473                      char *the_acl, enum acl_mode mode)
474 {
475         int fnum;
476         SEC_DESC *sd, *old;
477         int i, j;
478         unsigned sd_size;
479
480         sd = sec_desc_parse(the_acl);
481
482         if (!sd) return;
483         if (test_args) return;
484
485         /* the desired access below is the only one I could find that works with
486            NT4, W2KP and Samba */
487         fnum = cli_nt_create(cli, filename, MAXIMUM_ALLOWED_ACCESS | 0x60000);
488         if (fnum == -1) {
489                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
490                 return;
491         }
492
493         old = cli_query_secdesc(cli, fnum);
494
495         /* the logic here is rather more complex than I would like */
496         switch (mode) {
497         case ACL_DELETE:
498                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
499                         BOOL found = False;
500
501                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
502                                 if (sec_ace_equal(&sd->dacl->ace[i],
503                                                   &old->dacl->ace[j])) {
504                                         if (j != old->dacl->num_aces-1) {
505                                                 old->dacl->ace[j] = old->dacl->ace[j+1];
506                                         }
507                                         old->dacl->num_aces--;
508                                         if (old->dacl->num_aces == 0) {
509                                                 free(old->dacl->ace);
510                                                 old->dacl->ace=NULL;
511                                                 free(old->dacl);
512                                                 old->dacl = NULL;
513                                                 old->off_dacl = 0;
514                                         }
515                                         found = True;
516                                         break;
517                                 }
518                         }
519
520                         if (!found) {
521                                 fstring str;
522
523                                 SidToString(str, &sd->dacl->ace[i].sid);
524                                 printf("ACL for SID %s not found\n", str);
525                         }
526                 }
527                 break;
528
529         case ACL_MODIFY:
530                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
531                         BOOL found = False;
532
533                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
534                                 if (sid_equal(&sd->dacl->ace[i].sid,
535                                               &old->dacl->ace[j].sid)) {
536                                         old->dacl->ace[j] = sd->dacl->ace[i];
537                                         found = True;
538                                 }
539                         }
540
541                         if (!found) {
542                                 fstring str;
543
544                                 SidToString(str, &sd->dacl->ace[i].sid);
545                                 printf("ACL for SID %s not found\n", str);
546                         }
547                 }
548
549                 break;
550
551         case ACL_ADD:
552                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
553                         add_ace(&old->dacl, &sd->dacl->ace[i]);
554                 }
555                 break;
556
557         case ACL_SET:
558                 free_sec_desc(&old);
559                 old = sd;
560                 break;
561         }
562
563         if (sd != old) {
564                 free_sec_desc(&sd);
565         }
566
567         sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid, 
568                            NULL, old->dacl, &sd_size);
569
570         if (!cli_set_secdesc(cli, fnum, sd)) {
571                 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
572         }
573
574         free_sec_desc(&sd);
575         free_sec_desc(&old);
576
577         cli_close(cli, fnum);
578 }
579
580
581 /***************************************************** 
582 return a connection to a server
583 *******************************************************/
584 struct cli_state *connect_one(char *share)
585 {
586         struct cli_state *c;
587         struct nmb_name called, calling;
588         char *server_n;
589         struct in_addr ip;
590         extern struct in_addr ipzero;
591         extern pstring global_myname;
592
593         fstrcpy(server,share+2);
594         share = strchr(server,'\\');
595         if (!share) return NULL;
596         *share = 0;
597         share++;
598
599         server_n = server;
600         
601         ip = ipzero;
602
603         make_nmb_name(&calling, global_myname, 0x0);
604         make_nmb_name(&called , server, 0x20);
605
606  again:
607         ip = ipzero;
608
609         /* have to open a new connection */
610         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
611             !cli_connect(c, server_n, &ip)) {
612                 DEBUG(0,("Connection to %s failed\n", server_n));
613                 cli_shutdown(c);
614                 safe_free(c);
615                 return NULL;
616         }
617
618         if (!cli_session_request(c, &calling, &called)) {
619                 DEBUG(0,("session request to %s failed\n", called.name));
620                 cli_shutdown(c);
621                 safe_free(c);
622                 if (strcmp(called.name, "*SMBSERVER")) {
623                         make_nmb_name(&called , "*SMBSERVER", 0x20);
624                         goto again;
625                 }
626                 return NULL;
627         }
628
629         DEBUG(4,(" session request ok\n"));
630
631         if (!cli_negprot(c)) {
632                 DEBUG(0,("protocol negotiation failed\n"));
633                 cli_shutdown(c);
634                 safe_free(c);
635                 return NULL;
636         }
637
638         if (!got_pass) {
639                 char *pass = getpass("Password: ");
640                 if (pass) {
641                         pstrcpy(password, pass);
642                 }
643         }
644
645         if (!cli_session_setup(c, username, 
646                                password, strlen(password),
647                                password, strlen(password),
648                                lp_workgroup())) {
649                 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
650                 cli_shutdown(c);
651                 safe_free(c);
652                 return NULL;
653         }
654
655         DEBUG(4,(" session setup ok\n"));
656
657         if (!cli_send_tconX(c, share, "?????",
658                             password, strlen(password)+1)) {
659                 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
660                 cli_shutdown(c);
661                 safe_free(c);
662                 return NULL;
663         }
664
665         DEBUG(4,(" tconx ok\n"));
666
667         return c;
668 }
669
670
671 static void usage(void)
672 {
673         printf(
674 "Usage: smbcacls //server1/share1 filename -U username [options]\n\
675 \n\
676 \t-D <acls>               delete an acl\n\
677 \t-M <acls>               modify an acl\n\
678 \t-A <acls>               add an acl\n\
679 \t-S <acls>               set acls\n\
680 \t-n                      don't resolve sids or masks to names\n\
681 \t-h                      print help\n\
682 \n\
683 The username can be of the form username%%password or\n\
684 workgroup\\username%%password.\n\n\
685 An acl is of the form ACL:<SID>:type/flags/mask\n\
686 You can string acls together with spaces, commas or newlines\n\
687 ");
688 }
689
690 /****************************************************************************
691   main program
692 ****************************************************************************/
693  int main(int argc,char *argv[])
694 {
695         char *share;
696         char *filename;
697         extern char *optarg;
698         extern int optind;
699         extern FILE *dbf;
700         int opt;
701         char *p;
702         int seed;
703         static pstring servicesf = CONFIGFILE;
704         struct cli_state *cli;
705         enum acl_mode mode;
706         char *the_acl = NULL;
707
708         setlinebuf(stdout);
709
710         dbf = stderr;
711
712         if (argc < 4 || argv[1][0] == '-') {
713                 usage();
714                 exit(1);
715         }
716
717         setup_logging(argv[0],True);
718
719         share = argv[1];
720         filename = argv[2];
721         all_string_sub(share,"/","\\",0);
722
723         argc -= 2;
724         argv += 2;
725
726         TimeInit();
727         charset_initialise();
728
729         lp_load(servicesf,True,False,False);
730         codepage_initialise(lp_client_code_page());
731         load_interfaces();
732
733         if (getenv("USER")) {
734                 pstrcpy(username,getenv("USER"));
735         }
736
737         seed = time(NULL);
738
739         while ((opt = getopt(argc, argv, "U:nhS:D:A:M:t")) != EOF) {
740                 switch (opt) {
741                 case 'U':
742                         pstrcpy(username,optarg);
743                         p = strchr(username,'%');
744                         if (p) {
745                                 *p = 0;
746                                 pstrcpy(password, p+1);
747                                 got_pass = 1;
748                         }
749                         break;
750
751                 case 'S':
752                         the_acl = optarg;
753                         mode = ACL_SET;
754                         break;
755
756                 case 'D':
757                         the_acl = optarg;
758                         mode = ACL_DELETE;
759                         break;
760
761                 case 'M':
762                         the_acl = optarg;
763                         mode = ACL_MODIFY;
764                         break;
765
766                 case 'A':
767                         the_acl = optarg;
768                         mode = ACL_ADD;
769                         break;
770
771                 case 'n':
772                         numeric = 1;
773                         break;
774
775                 case 't':
776                         test_args = 1;
777                         break;
778
779                 case 'h':
780                         usage();
781                         exit(1);
782
783                 default:
784                         printf("Unknown option %c (%d)\n", (char)opt, opt);
785                         exit(1);
786                 }
787         }
788
789         argc -= optind;
790         argv += optind;
791         
792         if (argc > 0) {
793                 usage();
794                 exit(1);
795         }
796
797         if (!test_args) {
798                 cli = connect_one(share);
799                 if (!cli) exit(1);
800         }
801
802         if (the_acl) {
803                 cacl_set(cli, filename, the_acl, mode);
804         } else {
805                 cacl_dump(cli, filename);
806         }
807
808         return(0);
809 }