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