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