af14c622dc9616e685945eded431f88b97c84a58
[kai/samba.git] / source / utils / smbcacls.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ACL get/set utility
4    
5    Copyright (C) Andrew Tridgell 2000
6    Copyright (C) Tim Potter      2000
7    Copyright (C) Jeremy Allison  2000
8    Copyright (C) Jelmer Vernooij 2003
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 #include "includes.h"
25
26 static int test_args = False;
27
28 #define CREATE_ACCESS_READ READ_CONTROL_ACCESS
29
30 /* numeric is set when the user wants numeric SIDs and ACEs rather
31    than going via LSA calls to resolve them */
32 static int numeric = False;
33
34 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
35 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
36 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
37
38 struct perm_value {
39         const char *perm;
40         uint32 mask;
41 };
42
43 /* These values discovered by inspection */
44
45 static const 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 const struct perm_value standard_values[] = {
56         { "READ",   0x001200a9 },
57         { "CHANGE", 0x001301bf },
58         { "FULL",   0x001f01ff },
59         { NULL, 0 },
60 };
61
62 /* Open cli connection and policy handle */
63
64 static NTSTATUS cli_lsa_lookup_sid(struct cli_state *cli,
65                                    const DOM_SID *sid,
66                                    TALLOC_CTX *mem_ctx,
67                                    enum lsa_SidType *type,
68                                    char **domain, char **name)
69 {
70         uint16 orig_cnum = cli->cnum;
71         struct rpc_pipe_client *p;
72         struct policy_handle handle;
73         NTSTATUS status;
74         TALLOC_CTX *frame = talloc_stackframe();
75         enum lsa_SidType *types;
76         char **domains;
77         char **names;
78
79         if (!cli_send_tconX(cli, "IPC$", "?????", "", 0)) {
80                 return cli_nt_error(cli);
81         }
82
83         p = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
84         if (p == NULL) {
85                 goto fail;
86         }
87
88         status = rpccli_lsa_open_policy(p, talloc_tos(), True,
89                                         GENERIC_EXECUTE_ACCESS, &handle);
90         if (!NT_STATUS_IS_OK(status)) {
91                 goto fail;
92         }
93
94         status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid,
95                                         &domains, &names, &types);
96         if (!NT_STATUS_IS_OK(status)) {
97                 goto fail;
98         }
99
100         *type = types[0];
101         *domain = talloc_move(mem_ctx, &domains[0]);
102         *name = talloc_move(mem_ctx, &names[0]);
103
104         status = NT_STATUS_OK;
105  fail:
106         TALLOC_FREE(p);
107         cli_tdis(cli);
108         cli->cnum = orig_cnum;
109         TALLOC_FREE(frame);
110         return status;
111 }
112
113 static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli,
114                                     const char *name,
115                                     enum lsa_SidType *type,
116                                     DOM_SID *sid)
117 {
118         uint16 orig_cnum = cli->cnum;
119         struct rpc_pipe_client *p;
120         struct policy_handle handle;
121         NTSTATUS status;
122         TALLOC_CTX *frame = talloc_stackframe();
123         DOM_SID *sids;
124         enum lsa_SidType *types;
125
126         if (!cli_send_tconX(cli, "IPC$", "?????", "", 0)) {
127                 return cli_nt_error(cli);
128         }
129
130         p = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
131         if (p == NULL) {
132                 goto fail;
133         }
134
135         status = rpccli_lsa_open_policy(p, talloc_tos(), True,
136                                         GENERIC_EXECUTE_ACCESS, &handle);
137         if (!NT_STATUS_IS_OK(status)) {
138                 goto fail;
139         }
140
141         status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name,
142                                          NULL, 1, &sids, &types);
143         if (!NT_STATUS_IS_OK(status)) {
144                 goto fail;
145         }
146
147         *type = types[0];
148         *sid = sids[0];
149
150         status = NT_STATUS_OK;
151  fail:
152         TALLOC_FREE(p);
153         cli_tdis(cli);
154         cli->cnum = orig_cnum;
155         TALLOC_FREE(frame);
156         return status;
157 }
158
159 /* convert a SID to a string, either numeric or username/group */
160 static void SidToString(struct cli_state *cli, fstring str, const DOM_SID *sid)
161 {
162         char *domain = NULL;
163         char *name = NULL;
164         enum lsa_SidType type;
165         NTSTATUS status;
166
167         sid_to_fstring(str, sid);
168
169         if (numeric) {
170                 return;
171         }
172
173         status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type,
174                                     &domain, &name);
175
176         if (!NT_STATUS_IS_OK(status)) {
177                 return;
178         }
179
180         slprintf(str, sizeof(fstring) - 1, "%s%s%s",
181                  domain, lp_winbind_separator(), name);
182         
183 }
184
185 /* convert a string to a SID, either numeric or username/group */
186 static bool StringToSid(struct cli_state *cli, DOM_SID *sid, const char *str)
187 {
188         enum lsa_SidType type;
189
190         if (strncmp(str, "S-", 2) == 0) {
191                 return string_to_sid(sid, str);
192         }
193
194         return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid));
195 }
196
197
198 /* print an ACE on a FILE, using either numeric or ascii representation */
199 static void print_ace(struct cli_state *cli, FILE *f, SEC_ACE *ace)
200 {
201         const struct perm_value *v;
202         fstring sidstr;
203         int do_print = 0;
204         uint32 got_mask;
205
206         SidToString(cli, sidstr, &ace->trustee);
207
208         fprintf(f, "%s:", sidstr);
209
210         if (numeric) {
211                 fprintf(f, "%d/%d/0x%08x", 
212                         ace->type, ace->flags, ace->access_mask);
213                 return;
214         }
215
216         /* Ace type */
217
218         if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
219                 fprintf(f, "ALLOWED");
220         } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
221                 fprintf(f, "DENIED");
222         } else {
223                 fprintf(f, "%d", ace->type);
224         }
225
226         /* Not sure what flags can be set in a file ACL */
227
228         fprintf(f, "/%d/", ace->flags);
229
230         /* Standard permissions */
231
232         for (v = standard_values; v->perm; v++) {
233                 if (ace->access_mask == v->mask) {
234                         fprintf(f, "%s", v->perm);
235                         return;
236                 }
237         }
238
239         /* Special permissions.  Print out a hex value if we have
240            leftover bits in the mask. */
241
242         got_mask = ace->access_mask;
243
244  again:
245         for (v = special_values; v->perm; v++) {
246                 if ((ace->access_mask & v->mask) == v->mask) {
247                         if (do_print) {
248                                 fprintf(f, "%s", v->perm);
249                         }
250                         got_mask &= ~v->mask;
251                 }
252         }
253
254         if (!do_print) {
255                 if (got_mask != 0) {
256                         fprintf(f, "0x%08x", ace->access_mask);
257                 } else {
258                         do_print = 1;
259                         goto again;
260                 }
261         }
262 }
263
264
265 /* parse an ACE in the same format as print_ace() */
266 static bool parse_ace(struct cli_state *cli, SEC_ACE *ace,
267                       const char *orig_str)
268 {
269         char *p;
270         const char *cp;
271         char *tok;
272         unsigned int atype = 0;
273         unsigned int aflags = 0;
274         unsigned int amask = 0;
275         DOM_SID sid;
276         SEC_ACCESS mask;
277         const struct perm_value *v;
278         char *str = SMB_STRDUP(orig_str);
279         TALLOC_CTX *frame = talloc_stackframe();
280
281         if (!str) {
282                 TALLOC_FREE(frame);
283                 return False;
284         }
285
286         ZERO_STRUCTP(ace);
287         p = strchr_m(str,':');
288         if (!p) {
289                 printf("ACE '%s': missing ':'.\n", orig_str);
290                 SAFE_FREE(str);
291                 TALLOC_FREE(frame);
292                 return False;
293         }
294         *p = '\0';
295         p++;
296         /* Try to parse numeric form */
297
298         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
299             StringToSid(cli, &sid, str)) {
300                 goto done;
301         }
302
303         /* Try to parse text form */
304
305         if (!StringToSid(cli, &sid, str)) {
306                 printf("ACE '%s': failed to convert '%s' to SID\n",
307                         orig_str, str);
308                 SAFE_FREE(str);
309                 TALLOC_FREE(frame);
310                 return False;
311         }
312
313         cp = p;
314         if (!next_token_talloc(frame, &cp, &tok, "/")) {
315                 printf("ACE '%s': failed to find '/' character.\n",
316                         orig_str);
317                 SAFE_FREE(str);
318                 TALLOC_FREE(frame);
319                 return False;
320         }
321
322         if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
323                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
324         } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
325                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
326         } else {
327                 printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n",
328                         orig_str, tok);
329                 SAFE_FREE(str);
330                 TALLOC_FREE(frame);
331                 return False;
332         }
333
334         /* Only numeric form accepted for flags at present */
335
336         if (!(next_token_talloc(frame, &cp, &tok, "/") &&
337               sscanf(tok, "%i", &aflags))) {
338                 printf("ACE '%s': bad integer flags entry at '%s'\n",
339                         orig_str, tok);
340                 SAFE_FREE(str);
341                 TALLOC_FREE(frame);
342                 return False;
343         }
344
345         if (!next_token_talloc(frame, &cp, &tok, "/")) {
346                 printf("ACE '%s': missing / at '%s'\n",
347                         orig_str, tok);
348                 SAFE_FREE(str);
349                 TALLOC_FREE(frame);
350                 return False;
351         }
352
353         if (strncmp(tok, "0x", 2) == 0) {
354                 if (sscanf(tok, "%i", &amask) != 1) {
355                         printf("ACE '%s': bad hex number at '%s'\n",
356                                 orig_str, tok);
357                         SAFE_FREE(str);
358                         TALLOC_FREE(frame);
359                         return False;
360                 }
361                 goto done;
362         }
363
364         for (v = standard_values; v->perm; v++) {
365                 if (strcmp(tok, v->perm) == 0) {
366                         amask = v->mask;
367                         goto done;
368                 }
369         }
370
371         p = tok;
372
373         while(*p) {
374                 bool found = False;
375
376                 for (v = special_values; v->perm; v++) {
377                         if (v->perm[0] == *p) {
378                                 amask |= v->mask;
379                                 found = True;
380                         }
381                 }
382
383                 if (!found) {
384                         printf("ACE '%s': bad permission value at '%s'\n",
385                                 orig_str, p);
386                         SAFE_FREE(str);
387                         TALLOC_FREE(frame);
388                         return False;
389                 }
390                 p++;
391         }
392
393         if (*p) {
394                 TALLOC_FREE(frame);
395                 SAFE_FREE(str);
396                 return False;
397         }
398
399  done:
400         mask = amask;
401         init_sec_ace(ace, &sid, atype, mask, aflags);
402         TALLOC_FREE(frame);
403         SAFE_FREE(str);
404         return True;
405 }
406
407 /* add an ACE to a list of ACEs in a SEC_ACL */
408 static bool add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
409 {
410         SEC_ACL *new_ace;
411         SEC_ACE *aces;
412         if (! *the_acl) {
413                 return (((*the_acl) = make_sec_acl(talloc_tos(), 3, 1, ace))
414                         != NULL);
415         }
416
417         if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) {
418                 return False;
419         }
420         memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
421         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
422         new_ace = make_sec_acl(talloc_tos(),(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
423         SAFE_FREE(aces);
424         (*the_acl) = new_ace;
425         return True;
426 }
427
428 /* parse a ascii version of a security descriptor */
429 static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
430 {
431         const char *p = str;
432         char *tok;
433         SEC_DESC *ret = NULL;
434         size_t sd_size;
435         DOM_SID *grp_sid=NULL, *owner_sid=NULL;
436         SEC_ACL *dacl=NULL;
437         int revision=1;
438
439         while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
440                 if (strncmp(tok,"REVISION:", 9) == 0) {
441                         revision = strtol(tok+9, NULL, 16);
442                         continue;
443                 }
444
445                 if (strncmp(tok,"OWNER:", 6) == 0) {
446                         if (owner_sid) {
447                                 printf("Only specify owner once\n");
448                                 goto done;
449                         }
450                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
451                         if (!owner_sid ||
452                             !StringToSid(cli, owner_sid, tok+6)) {
453                                 printf("Failed to parse owner sid\n");
454                                 goto done;
455                         }
456                         continue;
457                 }
458
459                 if (strncmp(tok,"GROUP:", 6) == 0) {
460                         if (grp_sid) {
461                                 printf("Only specify group once\n");
462                                 goto done;
463                         }
464                         grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
465                         if (!grp_sid ||
466                             !StringToSid(cli, grp_sid, tok+6)) {
467                                 printf("Failed to parse group sid\n");
468                                 goto done;
469                         }
470                         continue;
471                 }
472
473                 if (strncmp(tok,"ACL:", 4) == 0) {
474                         SEC_ACE ace;
475                         if (!parse_ace(cli, &ace, tok+4)) {
476                                 goto done;
477                         }
478                         if(!add_ace(&dacl, &ace)) {
479                                 printf("Failed to add ACL %s\n", tok);
480                                 goto done;
481                         }
482                         continue;
483                 }
484
485                 printf("Failed to parse token '%s' in security descriptor,\n", tok);
486                 goto done;
487         }
488
489         ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid,
490                             NULL, dacl, &sd_size);
491
492   done:
493         SAFE_FREE(grp_sid);
494         SAFE_FREE(owner_sid);
495
496         return ret;
497 }
498
499
500 /* print a ascii version of a security descriptor on a FILE handle */
501 static void sec_desc_print(struct cli_state *cli, FILE *f, SEC_DESC *sd)
502 {
503         fstring sidstr;
504         uint32 i;
505
506         fprintf(f, "REVISION:%d\n", sd->revision);
507
508         /* Print owner and group sid */
509
510         if (sd->owner_sid) {
511                 SidToString(cli, sidstr, sd->owner_sid);
512         } else {
513                 fstrcpy(sidstr, "");
514         }
515
516         fprintf(f, "OWNER:%s\n", sidstr);
517
518         if (sd->group_sid) {
519                 SidToString(cli, sidstr, sd->group_sid);
520         } else {
521                 fstrcpy(sidstr, "");
522         }
523
524         fprintf(f, "GROUP:%s\n", sidstr);
525
526         /* Print aces */
527         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
528                 SEC_ACE *ace = &sd->dacl->aces[i];
529                 fprintf(f, "ACL:");
530                 print_ace(cli, f, ace);
531                 fprintf(f, "\n");
532         }
533
534 }
535
536 /***************************************************** 
537 dump the acls for a file
538 *******************************************************/
539 static int cacl_dump(struct cli_state *cli, char *filename)
540 {
541         int result = EXIT_FAILED;
542         int fnum = -1;
543         SEC_DESC *sd;
544
545         if (test_args) 
546                 return EXIT_OK;
547
548         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
549
550         if (fnum == -1) {
551                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
552                 goto done;
553         }
554
555         sd = cli_query_secdesc(cli, fnum, talloc_tos());
556
557         if (!sd) {
558                 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
559                 goto done;
560         }
561
562         sec_desc_print(cli, stdout, sd);
563
564         result = EXIT_OK;
565
566 done:
567         if (fnum != -1)
568                 cli_close(cli, fnum);
569
570         return result;
571 }
572
573 /***************************************************** 
574 Change the ownership or group ownership of a file. Just
575 because the NT docs say this can't be done :-). JRA.
576 *******************************************************/
577
578 static int owner_set(struct cli_state *cli, enum chown_mode change_mode, 
579                         const char *filename, const char *new_username)
580 {
581         int fnum;
582         DOM_SID sid;
583         SEC_DESC *sd, *old;
584         size_t sd_size;
585
586         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
587
588         if (fnum == -1) {
589                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
590                 return EXIT_FAILED;
591         }
592
593         if (!StringToSid(cli, &sid, new_username))
594                 return EXIT_PARSE_ERROR;
595
596         old = cli_query_secdesc(cli, fnum, talloc_tos());
597
598         cli_close(cli, fnum);
599
600         if (!old) {
601                 printf("owner_set: Failed to query old descriptor\n");
602                 return EXIT_FAILED;
603         }
604
605         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
606                                 (change_mode == REQUEST_CHOWN) ? &sid : NULL,
607                                 (change_mode == REQUEST_CHGRP) ? &sid : NULL,
608                            NULL, NULL, &sd_size);
609
610         fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS);
611
612         if (fnum == -1) {
613                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
614                 return EXIT_FAILED;
615         }
616
617         if (!cli_set_secdesc(cli, fnum, sd)) {
618                 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
619         }
620
621         cli_close(cli, fnum);
622
623         return EXIT_OK;
624 }
625
626
627 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
628    However NT4 gives a "The information may have been modified by a
629    computer running Windows NT 5.0" if denied ACEs do not appear before
630    allowed ACEs. */
631
632 static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
633 {
634         if (sec_ace_equal(ace1, ace2)) 
635                 return 0;
636
637         if (ace1->type != ace2->type) 
638                 return ace2->type - ace1->type;
639
640         if (sid_compare(&ace1->trustee, &ace2->trustee)) 
641                 return sid_compare(&ace1->trustee, &ace2->trustee);
642
643         if (ace1->flags != ace2->flags) 
644                 return ace1->flags - ace2->flags;
645
646         if (ace1->access_mask != ace2->access_mask) 
647                 return ace1->access_mask - ace2->access_mask;
648
649         if (ace1->size != ace2->size) 
650                 return ace1->size - ace2->size;
651
652         return memcmp(ace1, ace2, sizeof(SEC_ACE));
653 }
654
655 static void sort_acl(SEC_ACL *the_acl)
656 {
657         uint32 i;
658         if (!the_acl) return;
659
660         qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare);
661
662         for (i=1;i<the_acl->num_aces;) {
663                 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
664                         int j;
665                         for (j=i; j<the_acl->num_aces-1; j++) {
666                                 the_acl->aces[j] = the_acl->aces[j+1];
667                         }
668                         the_acl->num_aces--;
669                 } else {
670                         i++;
671                 }
672         }
673 }
674
675 /***************************************************** 
676 set the ACLs on a file given an ascii description
677 *******************************************************/
678 static int cacl_set(struct cli_state *cli, char *filename, 
679                     char *the_acl, enum acl_mode mode)
680 {
681         int fnum;
682         SEC_DESC *sd, *old;
683         uint32 i, j;
684         size_t sd_size;
685         int result = EXIT_OK;
686
687         sd = sec_desc_parse(talloc_tos(), cli, the_acl);
688
689         if (!sd) return EXIT_PARSE_ERROR;
690         if (test_args) return EXIT_OK;
691
692         /* The desired access below is the only one I could find that works
693            with NT4, W2KP and Samba */
694
695         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
696
697         if (fnum == -1) {
698                 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
699                 return EXIT_FAILED;
700         }
701
702         old = cli_query_secdesc(cli, fnum, talloc_tos());
703
704         if (!old) {
705                 printf("calc_set: Failed to query old descriptor\n");
706                 return EXIT_FAILED;
707         }
708
709         cli_close(cli, fnum);
710
711         /* the logic here is rather more complex than I would like */
712         switch (mode) {
713         case SMB_ACL_DELETE:
714                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
715                         bool found = False;
716
717                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
718                                 if (sec_ace_equal(&sd->dacl->aces[i],
719                                                   &old->dacl->aces[j])) {
720                                         uint32 k;
721                                         for (k=j; k<old->dacl->num_aces-1;k++) {
722                                                 old->dacl->aces[k] = old->dacl->aces[k+1];
723                                         }
724                                         old->dacl->num_aces--;
725                                         found = True;
726                                         break;
727                                 }
728                         }
729
730                         if (!found) {
731                                 printf("ACL for ACE:"); 
732                                 print_ace(cli, stdout, &sd->dacl->aces[i]);
733                                 printf(" not found\n");
734                         }
735                 }
736                 break;
737
738         case SMB_ACL_MODIFY:
739                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
740                         bool found = False;
741
742                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
743                                 if (sid_equal(&sd->dacl->aces[i].trustee,
744                                               &old->dacl->aces[j].trustee)) {
745                                         old->dacl->aces[j] = sd->dacl->aces[i];
746                                         found = True;
747                                 }
748                         }
749
750                         if (!found) {
751                                 fstring str;
752
753                                 SidToString(cli, str,
754                                             &sd->dacl->aces[i].trustee);
755                                 printf("ACL for SID %s not found\n", str);
756                         }
757                 }
758
759                 if (sd->owner_sid) {
760                         old->owner_sid = sd->owner_sid;
761                 }
762
763                 if (sd->group_sid) { 
764                         old->group_sid = sd->group_sid;
765                 }
766
767                 break;
768
769         case SMB_ACL_ADD:
770                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
771                         add_ace(&old->dacl, &sd->dacl->aces[i]);
772                 }
773                 break;
774
775         case SMB_ACL_SET:
776                 old = sd;
777                 break;
778         }
779
780         /* Denied ACE entries must come before allowed ones */
781         sort_acl(old->dacl);
782
783         /* Create new security descriptor and set it */
784
785         /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
786            But if we're sending an owner, even if it's the same as the one
787            that already exists then W2K3 insists we open with WRITE_OWNER access.
788            I need to check that setting a SD with no owner set works against WNT
789            and W2K. JRA.
790         */
791
792         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
793                            old->owner_sid, old->group_sid,
794                            NULL, old->dacl, &sd_size);
795
796         fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS);
797
798         if (fnum == -1) {
799                 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
800                 return EXIT_FAILED;
801         }
802
803         if (!cli_set_secdesc(cli, fnum, sd)) {
804                 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
805                 result = EXIT_FAILED;
806         }
807
808         /* Clean up */
809
810         cli_close(cli, fnum);
811
812         return result;
813 }
814
815
816 /*****************************************************
817  Return a connection to a server.
818 *******************************************************/
819 static struct cli_state *connect_one(const char *server, const char *share)
820 {
821         struct cli_state *c = NULL;
822         struct sockaddr_storage ss;
823         NTSTATUS nt_status;
824         zero_addr(&ss);
825
826         if (!get_cmdline_auth_info_got_pass()) {
827                 char *pass = getpass("Password: ");
828                 if (pass) {
829                         set_cmdline_auth_info_password(pass);
830                 }
831         }
832
833         nt_status = cli_full_connection(&c, global_myname(), server, 
834                                 &ss, 0,
835                                 share, "?????",
836                                 get_cmdline_auth_info_username(),
837                                 lp_workgroup(),
838                                 get_cmdline_auth_info_password(),
839                                 get_cmdline_auth_info_use_kerberos() ? CLI_FULL_CONNECTION_USE_KERBEROS : 0,
840                                 get_cmdline_auth_info_signing_state(),
841                                 NULL);
842         if (!NT_STATUS_IS_OK(nt_status)) {
843                 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
844                 return NULL;
845         }
846
847         if (get_cmdline_auth_info_smb_encrypt()) {
848                 nt_status = cli_cm_force_encryption(c,
849                                         get_cmdline_auth_info_username(),
850                                         get_cmdline_auth_info_password(),
851                                         lp_workgroup(),
852                                         share);
853                 if (!NT_STATUS_IS_OK(nt_status)) {
854                         cli_shutdown(c);
855                         c = NULL;
856                 }
857         }
858
859         return c;
860 }
861
862 /****************************************************************************
863   main program
864 ****************************************************************************/
865  int main(int argc, const char *argv[])
866 {
867         char *share;
868         int opt;
869         enum acl_mode mode = SMB_ACL_SET;
870         static char *the_acl = NULL;
871         enum chown_mode change_mode = REQUEST_NONE;
872         int result;
873         char *path;
874         char *filename = NULL;
875         poptContext pc;
876         struct poptOption long_options[] = {
877                 POPT_AUTOHELP
878                 { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" },
879                 { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" },
880                 { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" },
881                 { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" },
882                 { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" },
883                 { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" },
884                 { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" },
885                 { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
886                 POPT_COMMON_SAMBA
887                 POPT_COMMON_CREDENTIALS
888                 { NULL }
889         };
890
891         struct cli_state *cli;
892         TALLOC_CTX *frame = talloc_stackframe();
893         const char *owner_username = "";
894         char *server;
895
896         load_case_tables();
897
898
899         /* set default debug level to 1 regardless of what smb.conf sets */
900         setup_logging( "smbcacls", True );
901         DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
902         dbf = x_stderr;
903         x_setbuf( x_stderr, NULL );
904
905         setlinebuf(stdout);
906
907         lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
908         load_interfaces();
909
910         pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
911
912         poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
913                 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
914
915         while ((opt = poptGetNextOpt(pc)) != -1) {
916                 switch (opt) {
917                 case 'S':
918                         the_acl = smb_xstrdup(poptGetOptArg(pc));
919                         mode = SMB_ACL_SET;
920                         break;
921
922                 case 'D':
923                         the_acl = smb_xstrdup(poptGetOptArg(pc));
924                         mode = SMB_ACL_DELETE;
925                         break;
926
927                 case 'M':
928                         the_acl = smb_xstrdup(poptGetOptArg(pc));
929                         mode = SMB_ACL_MODIFY;
930                         break;
931
932                 case 'a':
933                         the_acl = smb_xstrdup(poptGetOptArg(pc));
934                         mode = SMB_ACL_ADD;
935                         break;
936
937                 case 'C':
938                         owner_username = poptGetOptArg(pc);
939                         change_mode = REQUEST_CHOWN;
940                         break;
941
942                 case 'G':
943                         owner_username = poptGetOptArg(pc);
944                         change_mode = REQUEST_CHGRP;
945                         break;
946                 }
947         }
948
949         /* Make connection to server */
950         if(!poptPeekArg(pc)) {
951                 poptPrintUsage(pc, stderr, 0);
952                 return -1;
953         }
954
955         path = talloc_strdup(frame, poptGetArg(pc));
956         if (!path) {
957                 return -1;
958         }
959
960         if(!poptPeekArg(pc)) {
961                 poptPrintUsage(pc, stderr, 0);
962                 return -1;
963         }
964
965         filename = talloc_strdup(frame, poptGetArg(pc));
966         if (!filename) {
967                 return -1;
968         }
969
970         string_replace(path,'/','\\');
971
972         server = talloc_strdup(frame, path+2);
973         if (!server) {
974                 return -1;
975         }
976         share = strchr_m(server,'\\');
977         if (!share) {
978                 printf("Invalid argument: %s\n", share);
979                 return -1;
980         }
981
982         *share = 0;
983         share++;
984
985         if (!test_args) {
986                 cli = connect_one(server, share);
987                 if (!cli) {
988                         exit(EXIT_FAILED);
989                 }
990         } else {
991                 exit(0);
992         }
993
994         string_replace(filename, '/', '\\');
995         if (filename[0] != '\\') {
996                 filename = talloc_asprintf(frame,
997                                 "\\%s",
998                                 filename);
999                 if (!filename) {
1000                         return -1;
1001                 }
1002         }
1003
1004         /* Perform requested action */
1005
1006         if (change_mode != REQUEST_NONE) {
1007                 result = owner_set(cli, change_mode, filename, owner_username);
1008         } else if (the_acl) {
1009                 result = cacl_set(cli, filename, the_acl, mode);
1010         } else {
1011                 result = cacl_dump(cli, filename);
1012         }
1013
1014         TALLOC_FREE(frame);
1015
1016         return result;
1017 }