signed/unsigned warning fixed
[kai/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    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #define NO_SYSLOG
24
25 #include "includes.h"
26
27 static fstring password;
28 static fstring username;
29 static int got_pass;
30
31 /* numeric is set when the user wants numeric SIDs and ACEs rather
32    than going via LSA calls to resolve them */
33 static int numeric;
34
35 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD};
36
37
38 /* convert a SID to a string, either numeric or username/group */
39 static void SidToString(fstring str, DOM_SID *sid)
40 {
41         if (numeric) {
42                 sid_to_string(str, sid);
43         } else {
44                 printf("need to add LSA lookups\n");
45                 sid_to_string(str, sid);
46         }
47 }
48
49 /* convert a string to a SID, either numeric or username/group */
50 static BOOL StringToSid(DOM_SID *sid, fstring str)
51 {
52         if (strncmp(str,"S-", 2) == 0) {
53                 return string_to_sid(sid, str);
54         } else {
55                 printf("need to add LSA lookups\n");
56                 return False;
57         }
58 }
59
60
61 /* print an ACE on a FILE, using either numeric or ascii representation */
62 static void print_ace(FILE *f, SEC_ACE *ace)
63 {
64         fstring sidstr;
65         char *perm;
66
67         SidToString(sidstr, &ace->sid);
68
69         fprintf(f, "%s:", sidstr);
70
71         if (numeric) {
72                 fprintf(f, "%x/%x/%08x\n", 
73                         ace->type, ace->flags, ace->info.mask);
74                 return;
75         }
76
77         /* this interpretation is almost certainly wrong, Tim, please
78            have a look at these */
79         if (ace->info.mask == 0x001f01ff) {
80                 perm = "F";
81         } else if (ace->info.mask == 0x001301bf) {
82                 perm = "C";
83         } else if (ace->info.mask == 0x001200a9) {
84                 perm = "R";
85         } else if (ace->info.mask == 0x00080000) {
86                 perm = "N";
87         } else {
88                 perm = "?";
89         }
90
91         fprintf(f,"%s\n", perm);
92 }
93
94
95 /* parse an ACE in the same format as print_ace() */
96 static BOOL parse_ace(SEC_ACE *ace, char *str)
97 {
98         char *p;
99         unsigned atype, aflags, amask;
100         DOM_SID sid;
101         SEC_ACCESS mask;
102         ZERO_STRUCTP(ace);
103         p = strchr(str,':');
104         if (!p) return False;
105         *p = 0;
106         if (sscanf(p+1, "%x/%x/%08x", 
107                    &atype, &aflags, &amask) != 3 ||
108             !StringToSid(&sid, str)) {
109                 return False;
110         }
111         mask.mask = amask;
112         init_sec_ace(ace, &sid, atype, mask, aflags);
113         return True;
114 }
115
116
117
118 /* add an ACE to a list of ACEs in a SEC_ACL */
119 static BOOL add_ace(SEC_ACL **acl, SEC_ACE *ace)
120 {
121         SEC_ACL *new;
122         SEC_ACE *aces;
123         if (! *acl) {
124                 (*acl) = make_sec_acl(3, 1, ace);
125                 return True;
126         }
127
128         aces = calloc(1+(*acl)->num_aces,sizeof(SEC_ACE));
129         memcpy(aces, (*acl)->ace, (*acl)->num_aces * sizeof(SEC_ACE));
130         memcpy(aces+(*acl)->num_aces, ace, sizeof(SEC_ACE));
131         new = make_sec_acl((*acl)->revision,1+(*acl)->num_aces, aces);
132         free_sec_acl(acl);
133         free(aces);
134         (*acl) = new;
135         return True;
136 }
137
138 /* parse a ascii version of a security descriptor */
139 static SEC_DESC *sec_desc_parse(char *str)
140 {
141         char *p = str;
142         fstring tok;
143         SEC_DESC *ret;
144         unsigned sd_size;
145         DOM_SID *grp_sid=NULL, *owner_sid=NULL;
146         SEC_ACL *dacl=NULL, *sacl=NULL;
147         int revision=1;
148         int type=0x8004;
149
150         while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
151
152                 if (strncmp(tok,"REVISION:", 9) == 0) {
153                         revision = strtol(tok+9, NULL, 16);
154                 }
155
156                 if (strncmp(tok,"TYPE:", 5) == 0) {
157                         type = strtol(tok+5, NULL, 16);
158                 }
159
160                 if (strncmp(tok,"OWNER:", 6) == 0) {
161                         owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
162                         if (!owner_sid ||
163                             !StringToSid(owner_sid, tok+6)) {
164                                 printf("Failed to parse owner sid\n");
165                                 return NULL;
166                         }
167                 }
168
169                 if (strncmp(tok,"GROUP:", 6) == 0) {
170                         grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
171                         if (!grp_sid ||
172                             !StringToSid(grp_sid, tok+6)) {
173                                 printf("Failed to parse group sid\n");
174                                 return NULL;
175                         }
176                 }
177
178                 if (strncmp(tok,"DACL:", 5) == 0) {
179                         SEC_ACE ace;
180                         if (!parse_ace(&ace, tok+5) || 
181                             !add_ace(&dacl, &ace)) {
182                                 printf("Failed to parse DACL\n");
183                                 return NULL;
184                         }
185                 }
186
187                 if (strncmp(tok,"SACL:", 5) == 0) {
188                         SEC_ACE ace;
189                         if (!parse_ace(&ace, tok+5) || 
190                             !add_ace(&sacl, &ace)) {
191                                 printf("Failed to parse SACL\n");
192                                 return NULL;
193                         }
194                 }
195         }
196
197         ret = make_sec_desc(revision, owner_sid, grp_sid, 
198                             sacl, dacl, &sd_size);
199
200         free_sec_acl(&sacl);
201         free_sec_acl(&dacl);
202         if (grp_sid) free(grp_sid);
203         if (owner_sid) free(owner_sid);
204
205         return ret;
206 }
207
208
209 /* print a ascii version of a security descriptor on a FILE handle */
210 static void sec_desc_print(FILE *f, SEC_DESC *sd)
211 {
212         fstring sidstr;
213         int i;
214
215         printf("REVISION:%x TYPE:%x\n", sd->revision, sd->type);
216
217         /* Print owner and group sid */
218
219         if (sd->owner_sid) {
220                 SidToString(sidstr, sd->owner_sid);
221         } else {
222                 fstrcpy(sidstr, "");
223         }
224
225         printf("OWNER:%s\n", sidstr);
226
227         if (sd->grp_sid) {
228                 SidToString(sidstr, sd->grp_sid);
229         } else {
230                 fstrcpy(sidstr, "");
231         }
232
233         fprintf(f, "GROUP:%s\n", sidstr);
234
235         /* Print aces */
236         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
237                 SEC_ACE *ace = &sd->dacl->ace[i];
238                 fprintf(f, "DACL:");
239                 print_ace(f, ace);
240         }
241         for (i = 0; sd->sacl && i < sd->sacl->num_aces; i++) {
242                 SEC_ACE *ace = &sd->sacl->ace[i];
243                 fprintf(f, "SACL:");
244                 print_ace(f, ace);
245         }
246
247 }
248
249
250 /***************************************************** 
251 dump the acls for a file
252 *******************************************************/
253 static void cacl_dump(struct cli_state *cli, char *filename)
254 {
255         int fnum;
256         SEC_DESC *sd;
257
258         fnum = cli_open(cli, filename, O_RDONLY, 0);
259         if (fnum == -1) {
260                 printf("Failed to open %s\n", filename);
261                 return;
262         }
263
264         sd = cli_query_secdesc(cli, fnum);
265
266         if (!sd) {
267                 printf("ERROR: secdesc query failed\n");
268                 return;
269         }
270
271         sec_desc_print(stdout, sd);
272
273         free_sec_desc(&sd);
274
275         cli_close(cli, fnum);
276 }
277
278 /***************************************************** 
279 set the ACLs on a file given an ascii description
280 *******************************************************/
281 static void cacl_set(struct cli_state *cli, char *filename, 
282                      char *acl, enum acl_mode mode)
283 {
284         int fnum;
285         SEC_DESC *sd, *old;
286         int i, j;
287         unsigned sd_size;
288
289         sd = sec_desc_parse(acl);
290         if (!sd) {
291                 printf("Failed to parse security descriptor\n");
292                 return;
293         }
294
295         fnum = cli_open(cli, filename, O_RDONLY, 0);
296         if (fnum == -1) {
297                 printf("Failed to open %s\n", filename);
298                 return;
299         }
300
301         old = cli_query_secdesc(cli, fnum);
302
303         /* the logic here is rather more complex than I would like */
304         switch (mode) {
305         case ACL_DELETE:
306                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
307                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
308                                 if (sec_ace_equal(&sd->dacl->ace[i],
309                                                   &old->dacl->ace[j])) {
310                                         if (j != old->dacl->num_aces-1) {
311                                                 old->dacl->ace[j] = old->dacl->ace[j+1];
312                                         }
313                                         old->dacl->num_aces--;
314                                         if (old->dacl->num_aces == 0) {
315                                                 free(old->dacl->ace);
316                                                 old->dacl->ace=NULL;
317                                                 free(old->dacl);
318                                                 old->dacl = NULL;
319                                                 old->off_dacl = 0;
320                                         }
321                                         break;
322                                 }
323                         }
324                 }
325                 for (i=0;sd->sacl && i<sd->sacl->num_aces;i++) {
326                         for (j=0;old->sacl && j<old->sacl->num_aces;j++) {
327                                 if (sec_ace_equal(&sd->sacl->ace[i],
328                                                   &old->sacl->ace[j])) {
329                                         if (j != old->sacl->num_aces-1) {
330                                                 old->sacl->ace[j] = old->sacl->ace[j+1];
331                                         }
332                                         old->sacl->num_aces--;
333                                         if (old->sacl->num_aces == 0) {
334                                                 free(old->sacl->ace);
335                                                 old->sacl->ace=NULL;
336                                                 free(old->sacl);
337                                                 old->sacl = NULL;
338                                                 old->off_sacl = 0;
339                                         }
340                                         break;
341                                 }
342                         }
343                 }
344                 break;
345
346         case ACL_MODIFY:
347                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
348                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
349                                 if (sid_equal(&sd->dacl->ace[i].sid,
350                                               &old->dacl->ace[j].sid)) {
351                                         old->dacl->ace[j] = sd->dacl->ace[i];
352                                 }
353                         }
354                 }
355                 for (i=0;sd->sacl && i<sd->sacl->num_aces;i++) {
356                         for (j=0;old->sacl && j<old->sacl->num_aces;j++) {
357                                 if (sid_equal(&sd->sacl->ace[i].sid,
358                                               &old->sacl->ace[j].sid)) {
359                                         old->sacl->ace[j] = sd->sacl->ace[i];
360                                 }
361                         }
362                 }
363                 break;
364
365         case ACL_ADD:
366                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
367                         add_ace(&old->dacl, &sd->dacl->ace[i]);
368                 }
369                 for (i=0;sd->sacl && i<sd->sacl->num_aces;i++) {
370                         add_ace(&old->sacl, &sd->sacl->ace[i]);
371                 }
372                 break;
373
374         case ACL_SET:
375                 free_sec_desc(&old);
376                 old = sd;
377                 break;
378                 
379         }
380
381         if (sd != old) {
382                 free_sec_desc(&sd);
383         }
384
385         sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid, 
386                            old->sacl, old->dacl, &sd_size);
387
388         if (!cli_set_secdesc(cli, fnum, sd)) {
389                 printf("ERROR: secdesc set failed\n");
390                 return;
391         }
392
393         free_sec_desc(&sd);
394         free_sec_desc(&old);
395
396         cli_close(cli, fnum);
397 }
398
399
400 /***************************************************** 
401 return a connection to a server
402 *******************************************************/
403 struct cli_state *connect_one(char *share)
404 {
405         struct cli_state *c;
406         struct nmb_name called, calling;
407         char *server_n;
408         fstring server;
409         struct in_addr ip;
410         extern struct in_addr ipzero;
411         extern pstring global_myname;
412
413         fstrcpy(server,share+2);
414         share = strchr(server,'\\');
415         if (!share) return NULL;
416         *share = 0;
417         share++;
418
419         server_n = server;
420         
421         ip = ipzero;
422
423         make_nmb_name(&calling, global_myname, 0x0);
424         make_nmb_name(&called , server, 0x20);
425
426  again:
427         ip = ipzero;
428
429         /* have to open a new connection */
430         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
431             !cli_connect(c, server_n, &ip)) {
432                 DEBUG(0,("Connection to %s failed\n", server_n));
433                 return NULL;
434         }
435
436         if (!cli_session_request(c, &calling, &called)) {
437                 DEBUG(0,("session request to %s failed\n", called.name));
438                 cli_shutdown(c);
439                 if (strcmp(called.name, "*SMBSERVER")) {
440                         make_nmb_name(&called , "*SMBSERVER", 0x20);
441                         goto again;
442                 }
443                 return NULL;
444         }
445
446         DEBUG(4,(" session request ok\n"));
447
448         if (!cli_negprot(c)) {
449                 DEBUG(0,("protocol negotiation failed\n"));
450                 cli_shutdown(c);
451                 return NULL;
452         }
453
454         if (!got_pass) {
455                 char *pass = getpass("Password: ");
456                 if (pass) {
457                         pstrcpy(password, pass);
458                 }
459         }
460
461         if (!cli_session_setup(c, username, 
462                                password, strlen(password),
463                                password, strlen(password),
464                                lp_workgroup())) {
465                 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
466                 return NULL;
467         }
468
469         DEBUG(4,(" session setup ok\n"));
470
471         if (!cli_send_tconX(c, share, "?????",
472                             password, strlen(password)+1)) {
473                 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
474                 cli_shutdown(c);
475                 return NULL;
476         }
477
478         DEBUG(4,(" tconx ok\n"));
479
480         return c;
481 }
482
483
484 static void usage(void)
485 {
486         printf(
487 "Usage:\n\
488   smbcacls //server1/share1 filename [options]\n\n\
489 \n\
490   -D <acls>               delete an acl\n\
491   -M <acls>               modify an acl\n\
492   -A <acls>               add an acl\n\
493   -S <acls>               set acls\n\
494 \n\
495   an acl is of the form SID:type/flags/mask\n\
496   you can string acls together with spaces, commas or newlines\n\
497 ");
498 }
499
500 /****************************************************************************
501   main program
502 ****************************************************************************/
503  int main(int argc,char *argv[])
504 {
505         char *share;
506         char *filename;
507         extern char *optarg;
508         extern int optind;
509         extern FILE *dbf;
510         int opt;
511         char *p;
512         int seed;
513         static pstring servicesf = CONFIGFILE;
514         struct cli_state *cli;
515         enum acl_mode mode;
516         char *acl = NULL;
517
518         setlinebuf(stdout);
519
520         dbf = stderr;
521
522         if (argc < 4 || argv[1][0] == '-') {
523                 usage();
524                 exit(1);
525         }
526
527         setup_logging(argv[0],True);
528
529         share = argv[1];
530         filename = argv[2];
531         all_string_sub(share,"/","\\",0);
532
533         argc -= 2;
534         argv += 2;
535
536         TimeInit();
537         charset_initialise();
538
539         lp_load(servicesf,True,False,False);
540         codepage_initialise(lp_client_code_page());
541         load_interfaces();
542
543         if (getenv("USER")) {
544                 pstrcpy(username,getenv("USER"));
545         }
546
547         seed = time(NULL);
548
549         while ((opt = getopt(argc, argv, "U:nhS:D:A:M:")) != EOF) {
550                 switch (opt) {
551                 case 'U':
552                         pstrcpy(username,optarg);
553                         p = strchr(username,'%');
554                         if (p) {
555                                 *p = 0;
556                                 pstrcpy(password, p+1);
557                                 got_pass = 1;
558                         }
559                         break;
560
561                 case 'S':
562                         acl = optarg;
563                         mode = ACL_SET;
564                         break;
565
566                 case 'D':
567                         acl = optarg;
568                         mode = ACL_DELETE;
569                         break;
570
571                 case 'M':
572                         acl = optarg;
573                         mode = ACL_MODIFY;
574                         break;
575
576                 case 'A':
577                         acl = optarg;
578                         mode = ACL_ADD;
579                         break;
580
581                 case 'n':
582                         numeric = 1;
583                         break;
584
585                 case 'h':
586                         usage();
587                         exit(1);
588                 default:
589                         printf("Unknown option %c (%d)\n", (char)opt, opt);
590                         exit(1);
591                 }
592         }
593
594         argc -= optind;
595         argv += optind;
596
597         cli = connect_one(share);
598         if (!cli) exit(1);
599
600         if (acl) {
601                 cacl_set(cli, filename, acl, mode);
602         } else {
603                 cacl_dump(cli, filename);
604         }
605
606         return(0);
607 }