40f71fbdbb6ce884e6e78ddef94d1904108feb3c
[samba.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    Copyright (C) Noel Power <noel.power@suse.com> 2013
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "lib/cmdline/cmdline.h"
27 #include "rpc_client/cli_pipe.h"
28 #include "../librpc/gen_ndr/ndr_lsa.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../libcli/security/security.h"
31 #include "libsmb/libsmb.h"
32 #include "libsmb/clirap.h"
33 #include "passdb/machine_sid.h"
34 #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 #include "util_sd.h"
36 #include "lib/param/param.h"
37
38 static char DIRSEP_CHAR = '\\';
39
40 static int inheritance = 0;
41 static const char *save_file = NULL;
42 static const char *restore_file = NULL;
43 static int recurse;
44 static int test_args;
45 static int sddl;
46 static int query_sec_info = -1;
47 static int set_sec_info = -1;
48 static bool want_mxac;
49
50 static const char *domain_sid = NULL;
51
52 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
53 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
54 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
55
56 struct cacl_callback_state {
57         struct cli_credentials *creds;
58         struct cli_state *cli;
59         struct security_descriptor *aclsd;
60         struct security_acl *acl_to_add;
61         enum acl_mode mode;
62         char *the_acl;
63         bool acl_no_propagate;
64         bool numeric;
65 };
66
67 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
68                                           struct dom_sid *sid)
69 {
70         union lsa_PolicyInformation *info = NULL;
71         struct smbXcli_tcon *orig_tcon = NULL;
72         char *orig_share = NULL;
73         struct rpc_pipe_client *rpc_pipe = NULL;
74         struct policy_handle handle;
75         NTSTATUS status, result;
76         TALLOC_CTX *frame = talloc_stackframe();
77
78         if (cli_state_has_tcon(cli)) {
79                 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
80         }
81
82         status = cli_tree_connect(cli, "IPC$", "?????", NULL);
83         if (!NT_STATUS_IS_OK(status)) {
84                 goto done;
85         }
86
87         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
88         if (!NT_STATUS_IS_OK(status)) {
89                 goto tdis;
90         }
91
92         status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
93                                         GENERIC_EXECUTE_ACCESS, &handle);
94         if (!NT_STATUS_IS_OK(status)) {
95                 goto tdis;
96         }
97
98         status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
99                                              frame, &handle,
100                                              LSA_POLICY_INFO_DOMAIN,
101                                              &info, &result);
102
103         if (any_nt_status_not_ok(status, result, &status)) {
104                 goto tdis;
105         }
106
107         *sid = *info->domain.sid;
108
109 tdis:
110         TALLOC_FREE(rpc_pipe);
111         cli_tdis(cli);
112 done:
113         cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
114         TALLOC_FREE(frame);
115         return status;
116 }
117
118 static struct dom_sid *get_domain_sid(struct cli_state *cli)
119 {
120         NTSTATUS status;
121         struct dom_sid_buf buf;
122
123         struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
124         if (sid == NULL) {
125                 DEBUG(0, ("Out of memory\n"));
126                 return NULL;
127         }
128
129         if (domain_sid) {
130                 if (!dom_sid_parse(domain_sid, sid)) {
131                         DEBUG(0,("failed to parse domain sid\n"));
132                         TALLOC_FREE(sid);
133                 }
134         } else {
135                 status = cli_lsa_lookup_domain_sid(cli, sid);
136
137                 if (!NT_STATUS_IS_OK(status)) {
138                         DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
139                         TALLOC_FREE(sid);
140                 }
141
142         }
143
144         DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
145         return sid;
146 }
147
148 /* add an ACE to a list of ACEs in a struct security_acl */
149 static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
150                              const struct security_ace *ace)
151
152 {
153         struct security_acl *acl = *the_acl;
154
155         if (acl == NULL) {
156                 acl = make_sec_acl(ctx, 3, 0, NULL);
157                 if (acl == NULL) {
158                         return false;
159                 }
160         }
161
162         if (acl->num_aces == UINT32_MAX) {
163                 return false;
164         }
165         ADD_TO_ARRAY(
166                 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
167         *the_acl = acl;
168         return True;
169 }
170
171 static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
172 {
173         return add_ace_with_ctx(talloc_tos(), the_acl, ace);
174 }
175
176 /* parse a ascii version of a security descriptor */
177 static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
178 {
179         const char *p = str;
180         char *tok;
181         struct security_descriptor *ret = NULL;
182         size_t sd_size;
183         struct dom_sid owner_sid = { .num_auths = 0 };
184         bool have_owner = false;
185         struct dom_sid group_sid = { .num_auths = 0 };
186         bool have_group = false;
187         struct security_acl *dacl=NULL;
188         int revision=1;
189
190         while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
191                 if (strncmp(tok,"REVISION:", 9) == 0) {
192                         revision = strtol(tok+9, NULL, 16);
193                         continue;
194                 }
195
196                 if (strncmp(tok,"OWNER:", 6) == 0) {
197                         if (have_owner) {
198                                 printf("Only specify owner once\n");
199                                 goto done;
200                         }
201                         if (!StringToSid(cli, &owner_sid, tok+6)) {
202                                 printf("Failed to parse owner sid\n");
203                                 goto done;
204                         }
205                         have_owner = true;
206                         continue;
207                 }
208
209                 if (strncmp(tok,"GROUP:", 6) == 0) {
210                         if (have_group) {
211                                 printf("Only specify group once\n");
212                                 goto done;
213                         }
214                         if (!StringToSid(cli, &group_sid, tok+6)) {
215                                 printf("Failed to parse group sid\n");
216                                 goto done;
217                         }
218                         have_group = true;
219                         continue;
220                 }
221
222                 if (strncmp(tok,"ACL:", 4) == 0) {
223                         struct security_ace ace;
224                         if (!parse_ace(cli, &ace, tok+4)) {
225                                 goto done;
226                         }
227                         if(!add_ace(&dacl, &ace)) {
228                                 printf("Failed to add ACL %s\n", tok);
229                                 goto done;
230                         }
231                         continue;
232                 }
233
234                 printf("Failed to parse token '%s' in security descriptor,\n", tok);
235                 goto done;
236         }
237
238         ret = make_sec_desc(
239                 ctx,
240                 revision,
241                 SEC_DESC_SELF_RELATIVE,
242                 have_owner ? &owner_sid : NULL,
243                 have_group ? &group_sid : NULL,
244                 NULL,
245                 dacl,
246                 &sd_size);
247
248 done:
249         return ret;
250 }
251
252 /*****************************************************
253 get fileinfo for filename
254 *******************************************************/
255 static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
256 {
257         uint16_t fnum = (uint16_t)-1;
258         NTSTATUS status;
259         struct smb_create_returns cr = {0};
260
261         /* The desired access below is the only one I could find that works
262            with NT4, W2KP and Samba */
263
264         status = cli_ntcreate(
265                 cli,                    /* cli */
266                 filename,               /* fname */
267                 0,                      /* CreatFlags */
268                 READ_CONTROL_ACCESS,    /* CreatFlags */
269                 0,                      /* FileAttributes */
270                 FILE_SHARE_READ|
271                 FILE_SHARE_WRITE,       /* ShareAccess */
272                 FILE_OPEN,              /* CreateDisposition */
273                 0x0,                    /* CreateOptions */
274                 0x0,                    /* SecurityFlags */
275                 &fnum,                  /* pfid */
276                 &cr);                   /* cr */
277         if (!NT_STATUS_IS_OK(status)) {
278                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
279                 return 0;
280         }
281
282         cli_close(cli, fnum);
283         return cr.file_attributes;
284 }
285
286 /*****************************************************
287 get sec desc for filename
288 *******************************************************/
289 static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
290                                                         struct cli_state *cli,
291                                                         const char *filename)
292 {
293         uint16_t fnum = (uint16_t)-1;
294         struct security_descriptor *sd;
295         NTSTATUS status;
296         uint32_t sec_info;
297         uint32_t desired_access = 0;
298
299         if (query_sec_info == -1) {
300                 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
301         } else {
302                 sec_info = query_sec_info;
303         }
304
305         if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
306                 desired_access |= SEC_STD_READ_CONTROL;
307         }
308         if (sec_info & SECINFO_SACL) {
309                 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
310         }
311
312         if (desired_access == 0) {
313                 desired_access |= SEC_STD_READ_CONTROL;
314         }
315
316         status = cli_ntcreate(cli, filename, 0, desired_access,
317                               0, FILE_SHARE_READ|FILE_SHARE_WRITE,
318                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
319         if (!NT_STATUS_IS_OK(status)) {
320                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
321                 return NULL;
322         }
323
324         status = cli_query_security_descriptor(cli, fnum, sec_info,
325                                                ctx, &sd);
326
327         cli_close(cli, fnum);
328
329         if (!NT_STATUS_IS_OK(status)) {
330                 printf("Failed to get security descriptor: %s\n",
331                        nt_errstr(status));
332                 return NULL;
333         }
334         return sd;
335 }
336
337 static struct security_descriptor *get_secdesc(struct cli_state *cli,
338                                                const char *filename)
339 {
340         return get_secdesc_with_ctx(talloc_tos(), cli, filename);
341 }
342 /*****************************************************
343 set sec desc for filename
344 *******************************************************/
345 static bool set_secdesc(struct cli_state *cli, const char *filename,
346                         struct security_descriptor *sd)
347 {
348         uint16_t fnum = (uint16_t)-1;
349         bool result=true;
350         NTSTATUS status;
351         uint32_t desired_access = 0;
352         uint32_t sec_info;
353
354         if (set_sec_info == -1) {
355                 sec_info = 0;
356
357                 if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
358                         sec_info |= SECINFO_DACL;
359                 }
360                 if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
361                         sec_info |= SECINFO_SACL;
362                 }
363                 if (sd->owner_sid) {
364                         sec_info |= SECINFO_OWNER;
365                 }
366                 if (sd->group_sid) {
367                         sec_info |= SECINFO_GROUP;
368                 }
369         } else {
370                 sec_info = set_sec_info;
371         }
372
373         /* Make the desired_access more specific. */
374         if (sec_info & SECINFO_DACL) {
375                 desired_access |= SEC_STD_WRITE_DAC;
376         }
377         if (sec_info & SECINFO_SACL) {
378                 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
379         }
380         if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
381                 desired_access |= SEC_STD_WRITE_OWNER;
382         }
383
384         status = cli_ntcreate(cli, filename, 0,
385                               desired_access,
386                               0, FILE_SHARE_READ|FILE_SHARE_WRITE,
387                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
388         if (!NT_STATUS_IS_OK(status)) {
389                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
390                 return false;
391         }
392
393         status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
394         if (!NT_STATUS_IS_OK(status)) {
395                 printf("ERROR: security descriptor set failed: %s\n",
396                        nt_errstr(status));
397                 result=false;
398         }
399
400         cli_close(cli, fnum);
401         return result;
402 }
403
404 /*****************************************************
405 get maximum access for a file
406 *******************************************************/
407 static int cacl_mxac(struct cli_state *cli, const char *filename)
408 {
409         NTSTATUS status;
410         uint32_t mxac;
411
412         status = cli_query_mxac(cli, filename, &mxac);
413         if (!NT_STATUS_IS_OK(status)) {
414                 printf("Failed to get mxac: %s\n", nt_errstr(status));
415                 return EXIT_FAILED;
416         }
417
418         printf("Maximum access: 0x%x\n", mxac);
419
420         return EXIT_OK;
421 }
422
423
424 /*****************************************************
425 dump the acls for a file
426 *******************************************************/
427 static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
428 {
429         struct security_descriptor *sd;
430         int ret;
431
432         if (test_args) {
433                 return EXIT_OK;
434         }
435
436         sd = get_secdesc(cli, filename);
437         if (sd == NULL) {
438                 return EXIT_FAILED;
439         }
440
441         if (sddl) {
442                 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
443                 if (str == NULL) {
444                         return EXIT_FAILED;
445                 }
446                 printf("%s\n", str);
447                 TALLOC_FREE(str);
448         } else {
449                 sec_desc_print(cli, stdout, sd, numeric);
450         }
451
452         if (want_mxac) {
453                 ret = cacl_mxac(cli, filename);
454                 if (ret != EXIT_OK) {
455                         return ret;
456                 }
457         }
458
459         return EXIT_OK;
460 }
461
462 /*****************************************************
463 Change the ownership or group ownership of a file. Just
464 because the NT docs say this can't be done :-). JRA.
465 *******************************************************/
466
467 static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
468                         const char *filename, const char *new_username)
469 {
470         struct dom_sid sid;
471         struct security_descriptor *sd;
472         size_t sd_size;
473
474         if (!StringToSid(cli, &sid, new_username))
475                 return EXIT_PARSE_ERROR;
476
477         sd = make_sec_desc(talloc_tos(),
478                            SECURITY_DESCRIPTOR_REVISION_1,
479                            SEC_DESC_SELF_RELATIVE,
480                            (change_mode == REQUEST_CHOWN) ? &sid : NULL,
481                            (change_mode == REQUEST_CHGRP) ? &sid : NULL,
482                            NULL, NULL, &sd_size);
483
484         if (!set_secdesc(cli, filename, sd)) {
485                 return EXIT_FAILED;
486         }
487
488         return EXIT_OK;
489 }
490
491
492 /* The MSDN is contradictory over the ordering of ACE entries in an
493    ACL.  However NT4 gives a "The information may have been modified
494    by a computer running Windows NT 5.0" if denied ACEs do not appear
495    before allowed ACEs. At
496    http://technet.microsoft.com/en-us/library/cc781716.aspx the
497    canonical order is specified as "Explicit Deny, Explicit Allow,
498    Inherited ACEs unchanged" */
499
500 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
501 {
502         if (security_ace_equal(ace1, ace2))
503                 return 0;
504
505         if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
506                         !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
507                 return 1;
508         if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
509                         (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
510                 return -1;
511         if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
512                         (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
513                 return ace1 - ace2;
514
515         if (ace1->type != ace2->type)
516                 return ace2->type - ace1->type;
517
518         if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
519                 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
520
521         if (ace1->flags != ace2->flags)
522                 return ace1->flags - ace2->flags;
523
524         if (ace1->access_mask != ace2->access_mask)
525                 return ace1->access_mask - ace2->access_mask;
526
527         if (ace1->size != ace2->size)
528                 return ace1->size - ace2->size;
529
530         return memcmp(ace1, ace2, sizeof(struct security_ace));
531 }
532
533 static void sort_acl(struct security_acl *the_acl)
534 {
535         uint32_t i;
536         if (!the_acl) return;
537
538         TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
539
540         for (i=1;i<the_acl->num_aces;) {
541                 if (security_ace_equal(&the_acl->aces[i-1],
542                                        &the_acl->aces[i])) {
543                         ARRAY_DEL_ELEMENT(
544                                 the_acl->aces, i, the_acl->num_aces);
545                         the_acl->num_aces--;
546                 } else {
547                         i++;
548                 }
549         }
550 }
551
552 /*****************************************************
553 set the ACLs on a file given a security descriptor
554 *******************************************************/
555
556 static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
557                             struct security_descriptor *sd, enum acl_mode mode,
558                             bool numeric)
559 {
560         struct security_descriptor *old = NULL;
561         uint32_t i, j;
562         size_t sd_size;
563         int result = EXIT_OK;
564
565         if (!sd) return EXIT_PARSE_ERROR;
566         if (test_args) return EXIT_OK;
567
568         if (mode != SMB_ACL_SET) {
569                 /*
570                  * Do not fetch old ACL when it will be overwritten
571                  * completely with a new one.
572                  */
573                 old = get_secdesc(cli, filename);
574
575                 if (!old) {
576                         return EXIT_FAILED;
577                 }
578         }
579
580         /* the logic here is rather more complex than I would like */
581         switch (mode) {
582         case SMB_ACL_DELETE:
583                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
584                         bool found = False;
585
586                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
587                                 if (security_ace_equal(&sd->dacl->aces[i],
588                                                        &old->dacl->aces[j])) {
589                                         uint32_t k;
590                                         for (k=j; k<old->dacl->num_aces-1;k++) {
591                                                 old->dacl->aces[k] = old->dacl->aces[k+1];
592                                         }
593                                         old->dacl->num_aces--;
594                                         found = True;
595                                         break;
596                                 }
597                         }
598
599                         if (!found) {
600                                 printf("ACL for ACE:");
601                                 print_ace(cli, stdout, &sd->dacl->aces[i],
602                                           numeric);
603                                 printf(" not found\n");
604                         }
605                 }
606                 break;
607
608         case SMB_ACL_MODIFY:
609                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
610                         bool found = False;
611
612                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
613                                 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
614                                               &old->dacl->aces[j].trustee)) {
615                                         old->dacl->aces[j] = sd->dacl->aces[i];
616                                         found = True;
617                                 }
618                         }
619
620                         if (!found) {
621                                 fstring str;
622
623                                 SidToString(cli, str,
624                                             &sd->dacl->aces[i].trustee,
625                                             numeric);
626                                 printf("ACL for SID %s not found\n", str);
627                         }
628                 }
629
630                 if (sd->owner_sid) {
631                         old->owner_sid = sd->owner_sid;
632                 }
633
634                 if (sd->group_sid) {
635                         old->group_sid = sd->group_sid;
636                 }
637
638                 break;
639
640         case SMB_ACL_ADD:
641                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
642                         add_ace(&old->dacl, &sd->dacl->aces[i]);
643                 }
644                 break;
645
646         case SMB_ACL_SET:
647                 old = sd;
648                 break;
649         }
650
651         /* Denied ACE entries must come before allowed ones */
652         sort_acl(old->dacl);
653
654         /* Create new security descriptor and set it */
655
656         /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
657            But if we're sending an owner, even if it's the same as the one
658            that already exists then W2K3 insists we open with WRITE_OWNER access.
659            I need to check that setting a SD with no owner set works against WNT
660            and W2K. JRA.
661         */
662
663         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
664                            old->owner_sid, old->group_sid,
665                            NULL, old->dacl, &sd_size);
666
667         if (!set_secdesc(cli, filename, sd)) {
668                 result = EXIT_FAILED;
669         }
670
671         return result;
672 }
673
674 /*****************************************************
675 set the ACLs on a file given an ascii description
676 *******************************************************/
677
678 static int cacl_set(struct cli_state *cli, const char *filename,
679                     char *the_acl, enum acl_mode mode, bool numeric)
680 {
681         struct security_descriptor *sd = NULL;
682
683         if (sddl) {
684                 sd = sddl_decode(talloc_tos(), the_acl, get_domain_sid(cli));
685         } else {
686                 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
687         }
688
689         if (sd == NULL) {
690                 return EXIT_PARSE_ERROR;
691         }
692         if (test_args) {
693                 return EXIT_OK;
694         }
695         return cacl_set_from_sd(cli, filename, sd, mode, numeric);
696 }
697
698 /*****************************************************
699 set the inherit on a file
700 *******************************************************/
701 static int inherit(struct cli_state *cli, const char *filename,
702                    const char *type)
703 {
704         struct security_descriptor *old,*sd;
705         uint32_t oldattr;
706         size_t sd_size;
707         int result = EXIT_OK;
708
709         old = get_secdesc(cli, filename);
710
711         if (!old) {
712                 return EXIT_FAILED;
713         }
714
715         oldattr = get_fileinfo(cli,filename);
716
717         if (strcmp(type,"allow")==0) {
718                 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
719                     SEC_DESC_DACL_PROTECTED) {
720                         uint32_t i;
721                         char *parentname,*temp;
722                         struct security_descriptor *parent;
723                         temp = talloc_strdup(talloc_tos(), filename);
724
725                         old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
726
727                         /* look at parent and copy in all its inheritable ACL's. */
728                         string_replace(temp, '\\', '/');
729                         if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
730                                 return EXIT_FAILED;
731                         }
732                         string_replace(parentname, '/', '\\');
733                         parent = get_secdesc(cli,parentname);
734                         if (parent == NULL) {
735                                 return EXIT_FAILED;
736                         }
737                         for (i=0;i<parent->dacl->num_aces;i++) {
738                                 struct security_ace *ace=&parent->dacl->aces[i];
739                                 /* Add inherited flag to all aces */
740                                 ace->flags=ace->flags|
741                                            SEC_ACE_FLAG_INHERITED_ACE;
742                                 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
743                                         if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
744                                             SEC_ACE_FLAG_CONTAINER_INHERIT) {
745                                                 add_ace(&old->dacl, ace);
746                                         }
747                                 } else {
748                                         if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
749                                             SEC_ACE_FLAG_OBJECT_INHERIT) {
750                                                 /* clear flags for files */
751                                                 ace->flags=0;
752                                                 add_ace(&old->dacl, ace);
753                                         }
754                                 }
755                         }
756                 } else {
757                         printf("Already set to inheritable permissions.\n");
758                         return EXIT_FAILED;
759                 }
760         } else if (strcmp(type,"remove")==0) {
761                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
762                     SEC_DESC_DACL_PROTECTED) {
763                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
764
765                         /* remove all inherited ACL's. */
766                         if (old->dacl) {
767                                 int i;
768                                 struct security_acl *temp=old->dacl;
769                                 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
770                                 for (i=temp->num_aces-1;i>=0;i--) {
771                                         struct security_ace *ace=&temp->aces[i];
772                                         /* Remove all ace with INHERITED flag set */
773                                         if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
774                                             SEC_ACE_FLAG_INHERITED_ACE) {
775                                                 add_ace(&old->dacl,ace);
776                                         }
777                                 }
778                         }
779                 } else {
780                         printf("Already set to no inheritable permissions.\n");
781                         return EXIT_FAILED;
782                 }
783         } else if (strcmp(type,"copy")==0) {
784                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
785                     SEC_DESC_DACL_PROTECTED) {
786                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
787
788                         /*
789                          * convert all inherited ACL's to non
790                          * inherited ACL's.
791                          */
792                         if (old->dacl) {
793                                 uint32_t i;
794                                 for (i=0;i<old->dacl->num_aces;i++) {
795                                         struct security_ace *ace=&old->dacl->aces[i];
796                                         /* Remove INHERITED FLAG from all aces */
797                                         ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
798                                 }
799                         }
800                 } else {
801                         printf("Already set to no inheritable permissions.\n");
802                         return EXIT_FAILED;
803                 }
804         }
805
806         /* Denied ACE entries must come before allowed ones */
807         sort_acl(old->dacl);
808
809         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
810                            old->owner_sid, old->group_sid,
811                            NULL, old->dacl, &sd_size);
812
813         if (!set_secdesc(cli, filename, sd)) {
814                 result = EXIT_FAILED;
815         }
816
817         return result;
818 }
819
820 /*****************************************************
821  Return a connection to a server.
822 *******************************************************/
823 static struct cli_state *connect_one(struct cli_credentials *creds,
824                                      const char *server, const char *share)
825 {
826         struct cli_state *c = NULL;
827         NTSTATUS nt_status;
828         uint32_t flags = 0;
829
830         nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
831                                 NULL, 0,
832                                 share, "?????",
833                                 creds,
834                                 flags);
835         if (!NT_STATUS_IS_OK(nt_status)) {
836                 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
837                 return NULL;
838         }
839
840         return c;
841 }
842
843 /*
844  * Process resulting combination of mask & fname ensuring
845  * terminated with wildcard
846  */
847 static char *build_dirname(TALLOC_CTX *ctx,
848         const char *mask, char *dir, char *fname)
849 {
850         char *mask2 = NULL;
851         char *p = NULL;
852
853         mask2 = talloc_strdup(ctx, mask);
854         if (!mask2) {
855                 return NULL;
856         }
857         p = strrchr_m(mask2, DIRSEP_CHAR);
858         if (p) {
859                 p[1] = 0;
860         } else {
861                 mask2[0] = '\0';
862         }
863         mask2 = talloc_asprintf_append(mask2,
864                                 "%s\\*",
865                                 fname);
866         return mask2;
867 }
868
869 /*
870  * Returns a copy of the ACL flags in ace modified according
871  * to some inheritance rules.
872  *   a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
873  *   b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
874  *   c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
875  *      stripped from flags to be propagated to non-container children
876  *   d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
877  *      stripped from flags to be propagated if the NP flag
878  *      SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
879  */
880
881 static uint8_t get_flags_to_propagate(bool is_container,
882                                 struct security_ace *ace)
883 {
884         uint8_t newflags = ace->flags;
885         /* OBJECT inheritance */
886         bool acl_objinherit = (ace->flags &
887                 SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
888         /* CONTAINER inheritance */
889         bool acl_cntrinherit = (ace->flags &
890                 SEC_ACE_FLAG_CONTAINER_INHERIT) ==
891                         SEC_ACE_FLAG_CONTAINER_INHERIT;
892         /* PROHIBIT inheritance */
893         bool prohibit_inheritance = ((ace->flags &
894                 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
895                         SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
896
897         /* Assume we are not propagating the ACE */
898
899         newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
900         /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
901         if (acl_cntrinherit || acl_objinherit) {
902                 /*
903                  * object inherit ( alone ) on a container needs
904                  * SEC_ACE_FLAG_INHERIT_ONLY
905                  */
906                 if (is_container) {
907                         if (acl_objinherit && !acl_cntrinherit) {
908                                 newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
909                         }
910                         /*
911                          * this is tricky, the only time we would not
912                          * propagate the ace for a container is if
913                          * prohibit_inheritance is set and object inheritance
914                          * alone is set
915                          */
916                         if ((prohibit_inheritance
917                             && acl_objinherit
918                             && !acl_cntrinherit) == false) {
919                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
920                         }
921                 } else {
922                         /*
923                          * don't apply object/container inheritance flags to
924                          * non dirs
925                          */
926                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
927                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
928                                         | SEC_ACE_FLAG_INHERIT_ONLY);
929                         /*
930                          * only apply ace to file if object inherit
931                          */
932                         if (acl_objinherit) {
933                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
934                         }
935                 }
936
937                 /* if NP is specified strip NP and all OI/CI INHERIT flags */
938                 if (prohibit_inheritance) {
939                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
940                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
941                                         | SEC_ACE_FLAG_INHERIT_ONLY
942                                         | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
943                 }
944         }
945         return newflags;
946 }
947
948 /*
949  * This function builds a new acl for 'caclfile', first it removes any
950  * existing inheritable ace(s) from the current acl of caclfile, secondly it
951  * applies any inheritable acls of the parent of caclfile ( inheritable acls of
952  * caclfile's parent are passed via acl_to_add member of cbstate )
953  *
954  */
955 static NTSTATUS propagate_inherited_aces(char *caclfile,
956                         struct cacl_callback_state *cbstate)
957 {
958         TALLOC_CTX *aclctx = NULL;
959         NTSTATUS status;
960         int result;
961         int fileattr;
962         struct security_descriptor *old = NULL;
963         bool is_container = false;
964         struct security_acl *acl_to_add = cbstate->acl_to_add;
965         struct security_acl *acl_to_remove = NULL;
966         uint32_t i, j;
967
968         aclctx = talloc_new(NULL);
969         if (aclctx == NULL) {
970                 return NT_STATUS_NO_MEMORY;
971         }
972         old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
973
974         if (!old) {
975                 status = NT_STATUS_UNSUCCESSFUL;
976                 goto out;
977         }
978
979         /* inhibit propagation? */
980         if ((old->type & SEC_DESC_DACL_PROTECTED) ==
981                 SEC_DESC_DACL_PROTECTED){
982                 status = NT_STATUS_OK;
983                 goto out;
984         }
985
986         fileattr = get_fileinfo(cbstate->cli, caclfile);
987         is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
988
989         /* find acl(s) that are inherited */
990         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
991
992                 if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
993                         if (!add_ace_with_ctx(aclctx, &acl_to_remove,
994                                               &old->dacl->aces[j])) {
995                                 status = NT_STATUS_NO_MEMORY;
996                                 goto out;
997                         }
998                 }
999         }
1000
1001         /* remove any acl(s) that are inherited */
1002         if (acl_to_remove) {
1003                 for (i = 0; i < acl_to_remove->num_aces; i++) {
1004                         struct security_ace ace = acl_to_remove->aces[i];
1005                         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1006
1007                                 if (security_ace_equal(&ace,
1008                                                   &old->dacl->aces[j])) {
1009                                         uint32_t k;
1010                                         for (k = j; k < old->dacl->num_aces-1;
1011                                                 k++) {
1012                                                 old->dacl->aces[k] =
1013                                                         old->dacl->aces[k+1];
1014                                         }
1015                                         old->dacl->num_aces--;
1016                                         break;
1017                                 }
1018                         }
1019                 }
1020         }
1021         /* propagate any inheritable ace to be added */
1022         if (acl_to_add) {
1023                 for (i = 0; i < acl_to_add->num_aces; i++) {
1024                         struct security_ace ace = acl_to_add->aces[i];
1025                         bool is_objectinherit = (ace.flags &
1026                                 SEC_ACE_FLAG_OBJECT_INHERIT) ==
1027                                         SEC_ACE_FLAG_OBJECT_INHERIT;
1028                         bool is_inherited;
1029                         /* don't propagate flags to a file unless OI */
1030                         if (!is_objectinherit && !is_container) {
1031                                 continue;
1032                         }
1033                         /*
1034                          * adjust flags according to inheritance
1035                          * rules
1036                          */
1037                         ace.flags = get_flags_to_propagate(is_container, &ace);
1038                         is_inherited = (ace.flags &
1039                                 SEC_ACE_FLAG_INHERITED_ACE) ==
1040                                         SEC_ACE_FLAG_INHERITED_ACE;
1041                         /* don't propagate non inherited flags */
1042                         if (!is_inherited) {
1043                                 continue;
1044                         }
1045                         if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1046                                 status = NT_STATUS_NO_MEMORY;
1047                                 goto out;
1048                         }
1049                 }
1050         }
1051
1052         result = cacl_set_from_sd(cbstate->cli, caclfile,
1053                                   old,
1054                                   SMB_ACL_SET, cbstate->numeric);
1055         if (result != EXIT_OK) {
1056                 status = NT_STATUS_UNSUCCESSFUL;
1057                 goto out;
1058         }
1059
1060         status = NT_STATUS_OK;
1061 out:
1062         TALLOC_FREE(aclctx);
1063         return status;
1064 }
1065
1066 /*
1067  * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1068  * SEC_ACE_FLAG_CONTAINER_INHERIT
1069  */
1070 static bool is_inheritable_ace(struct security_ace *ace)
1071 {
1072         uint8_t flags = ace->flags;
1073         if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1074                         | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1075                 return true;
1076         }
1077         return false;
1078 }
1079
1080 /* This method does some basic sanity checking with respect to automatic
1081  * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1082  * attempts to set inherited permissions directly. Additionally this method
1083  * does some basic initialisation for instance it parses the ACL passed on the
1084  * command line.
1085  */
1086 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1087                         struct cacl_callback_state *cbstate)
1088 {
1089         NTSTATUS result;
1090         char *the_acl = cbstate->the_acl;
1091         struct cli_state *cli = cbstate->cli;
1092         enum acl_mode mode = cbstate->mode;
1093         struct security_descriptor *sd = NULL;
1094         struct security_descriptor *old = NULL;
1095         uint32_t j;
1096         bool propagate = false;
1097
1098         old = get_secdesc_with_ctx(ctx, cli, filename);
1099         if (old == NULL) {
1100                 return NT_STATUS_NO_MEMORY;
1101         }
1102
1103         /* parse acl passed on the command line */
1104         if (sddl) {
1105                 cbstate->aclsd = sddl_decode(ctx, the_acl,
1106                                              get_domain_sid(cli));
1107         } else {
1108                 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1109         }
1110
1111         if (!cbstate->aclsd) {
1112                 result = NT_STATUS_UNSUCCESSFUL;
1113                 goto out;
1114         }
1115
1116         sd = cbstate->aclsd;
1117
1118         /* set operation if inheritance is enabled doesn't make sense */
1119         if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1120                 SEC_DESC_DACL_PROTECTED)){
1121                 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1122                 result = NT_STATUS_UNSUCCESSFUL;
1123                 goto out;
1124
1125         }
1126
1127         /*
1128          * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1129          * flags that are set
1130          */
1131         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1132                 struct security_ace *ace = &sd->dacl->aces[j];
1133                 if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1134                         d_printf("Illegal parameter %s\n", the_acl);
1135                         result = NT_STATUS_UNSUCCESSFUL;
1136                         goto out;
1137                 }
1138                 if (!propagate) {
1139                         if (is_inheritable_ace(ace)) {
1140                                 propagate = true;
1141                         }
1142                 }
1143         }
1144
1145         result = NT_STATUS_OK;
1146 out:
1147         cbstate->acl_no_propagate = !propagate;
1148         return result;
1149 }
1150
1151 /*
1152  * This method builds inheritable ace(s) from filename (which should be
1153  * a container) that need propagating to children in order to provide
1154  * automatic inheritance. Those inheritable ace(s) are stored in
1155  * acl_to_add member of cbstate for later processing
1156  * (see propagate_inherited_aces)
1157  */
1158 static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1159                         struct cacl_callback_state *cbstate)
1160 {
1161         NTSTATUS result;
1162         struct cli_state *cli = NULL;
1163         struct security_descriptor *sd = NULL;
1164         struct security_acl *acl_to_add = NULL;
1165         uint32_t j;
1166
1167         cli = cbstate->cli;
1168         sd = get_secdesc_with_ctx(ctx, cli, filename);
1169
1170         if (sd == NULL) {
1171                 return NT_STATUS_NO_MEMORY;
1172         }
1173
1174         /*
1175          * Check if any inheritance related flags are used, if not then
1176          * nothing to do. At the same time populate acls for inheritance
1177          * related ace(s) that need to be added to or deleted from children as
1178          * a result of inheritance propagation.
1179          */
1180
1181         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1182                 struct security_ace *ace = &sd->dacl->aces[j];
1183                 if (is_inheritable_ace(ace)) {
1184                         bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1185                         if (!added) {
1186                                 result = NT_STATUS_NO_MEMORY;
1187                                 goto out;
1188                         }
1189                 }
1190         }
1191         cbstate->acl_to_add = acl_to_add;
1192         result = NT_STATUS_OK;
1193 out:
1194         return result;
1195 }
1196
1197 /*
1198  * Callback handler to handle child elements processed by cli_list,  we attempt
1199  * to propagate inheritable ace(s) to each child via the function
1200  * propagate_inherited_aces. Children that are themselves directories are passed
1201  * to cli_list again ( to descend the directory structure )
1202  */
1203 static NTSTATUS cacl_set_cb(struct file_info *f,
1204                            const char *mask, void *state)
1205 {
1206         struct cacl_callback_state *cbstate =
1207                 (struct cacl_callback_state *)state;
1208         struct cli_state *cli = NULL;
1209         struct cli_credentials *creds = NULL;
1210
1211         TALLOC_CTX *dirctx = NULL;
1212         NTSTATUS status;
1213         struct cli_state *targetcli = NULL;
1214
1215         char *dir = NULL;
1216         char *dir_end = NULL;
1217         char *mask2 = NULL;
1218         char *targetpath = NULL;
1219         char *caclfile = NULL;
1220
1221         dirctx = talloc_new(NULL);
1222         if (!dirctx) {
1223                 status = NT_STATUS_NO_MEMORY;
1224                 goto out;
1225         }
1226
1227         cli = cbstate->cli;
1228         creds = cbstate->creds;
1229
1230         /* Work out the directory. */
1231         dir = talloc_strdup(dirctx, mask);
1232         if (!dir) {
1233                 status = NT_STATUS_NO_MEMORY;
1234                 goto out;
1235         }
1236
1237         dir_end = strrchr(dir, DIRSEP_CHAR);
1238         if (dir_end != NULL) {
1239                 *dir_end = '\0';
1240         }
1241
1242         if (!f->name || !f->name[0]) {
1243                 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1244                 status = NT_STATUS_UNSUCCESSFUL;
1245                 goto out;
1246         }
1247
1248         if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1249                 struct cacl_callback_state dir_cbstate;
1250                 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1251                         | FILE_ATTRIBUTE_SYSTEM
1252                         | FILE_ATTRIBUTE_HIDDEN;
1253                 dir_end = NULL;
1254
1255                 /* ignore special '.' & '..' */
1256                 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1257                         status = NT_STATUS_OK;
1258                         goto out;
1259                 }
1260
1261                 mask2 = build_dirname(dirctx, mask, dir, f->name);
1262                 if (mask2 == NULL) {
1263                         status = NT_STATUS_NO_MEMORY;
1264                         goto out;
1265                 }
1266
1267                 /* check for dfs */
1268                 status = cli_resolve_path(dirctx, "", creds, cli,
1269                         mask2, &targetcli, &targetpath);
1270                 if (!NT_STATUS_IS_OK(status)) {
1271                         goto out;
1272                 }
1273
1274                 /*
1275                  * prepare path to caclfile, remove any existing wildcard
1276                  * chars and convert path separators.
1277                  */
1278
1279                 caclfile = talloc_strdup(dirctx, targetpath);
1280                 if (!caclfile) {
1281                         status = NT_STATUS_NO_MEMORY;
1282                         goto out;
1283                 }
1284                 dir_end = strrchr(caclfile, '*');
1285                 if (dir_end != NULL) {
1286                         *dir_end = '\0';
1287                 }
1288
1289                 string_replace(caclfile, '/', '\\');
1290                 /*
1291                  * make directory specific copy of cbstate here
1292                  * (for this directory level) to be available as
1293                  * the parent cbstate for the children of this directory.
1294                  * Note: cbstate is overwritten for the current file being
1295                  *       processed.
1296                  */
1297                 dir_cbstate = *cbstate;
1298                 dir_cbstate.cli = targetcli;
1299
1300                 /*
1301                  * propagate any inherited ace from our parent
1302                  */
1303                 status = propagate_inherited_aces(caclfile, &dir_cbstate);
1304                 if (!NT_STATUS_IS_OK(status)) {
1305                         goto out;
1306                 }
1307
1308                 /*
1309                  * get inheritable ace(s) for this dir/container
1310                  * that will be propagated to its children
1311                  */
1312                 status = get_inheritable_aces(dirctx, caclfile,
1313                                                       &dir_cbstate);
1314                 if (!NT_STATUS_IS_OK(status)) {
1315                         goto out;
1316                 }
1317
1318                 /*
1319                  * ensure cacl_set_cb gets called for children
1320                  * of this directory (targetpath)
1321                  */
1322                 status = cli_list(targetcli, targetpath,
1323                         attribute, cacl_set_cb,
1324                         (void *)&dir_cbstate);
1325
1326                 if (!NT_STATUS_IS_OK(status)) {
1327                         goto out;
1328                 }
1329
1330         } else {
1331                 /*
1332                  * build full path to caclfile and replace '/' with '\' so
1333                  * other utility functions can deal with it
1334                  */
1335
1336                 targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1337                 if (!targetpath) {
1338                         status = NT_STATUS_NO_MEMORY;
1339                         goto out;
1340                 }
1341                 string_replace(targetpath, '/', '\\');
1342
1343                 /* attempt to propagate any inherited ace to file caclfile */
1344                 status = propagate_inherited_aces(targetpath, cbstate);
1345
1346                 if (!NT_STATUS_IS_OK(status)) {
1347                         goto out;
1348                 }
1349         }
1350         status = NT_STATUS_OK;
1351 out:
1352         if (!NT_STATUS_IS_OK(status)) {
1353                 d_printf("error %s: processing %s\n",
1354                         nt_errstr(status),
1355                         targetpath);
1356         }
1357         TALLOC_FREE(dirctx);
1358         return status;
1359 }
1360
1361
1362 /*
1363  * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1364  * helper callback function 'cacl_set_cb' handles the child elements processed
1365  * by cli_list.
1366  */
1367 static int inheritance_cacl_set(char *filename,
1368                         struct cacl_callback_state *cbstate)
1369 {
1370         int result;
1371         NTSTATUS ntstatus;
1372         int fileattr;
1373         char *mask = NULL;
1374         struct cli_state *cli = cbstate->cli;
1375         TALLOC_CTX *ctx = NULL;
1376         bool isdirectory = false;
1377         uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1378                                 | FILE_ATTRIBUTE_HIDDEN;
1379         ctx = talloc_init("inherit_set");
1380         if (ctx == NULL) {
1381                 d_printf("out of memory\n");
1382                 result = EXIT_FAILED;
1383                 goto out;
1384         }
1385
1386         /* ensure we have a filename that starts with '\' */
1387         if (!filename || *filename != DIRSEP_CHAR) {
1388                 /* illegal or no filename */
1389                 result = EXIT_FAILED;
1390                 d_printf("illegal or missing name '%s'\n", filename);
1391                 goto out;
1392         }
1393
1394
1395         fileattr = get_fileinfo(cli, filename);
1396         isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1397                 == FILE_ATTRIBUTE_DIRECTORY;
1398
1399         /*
1400          * if we've got as far as here then we have already evaluated
1401          * the args.
1402          */
1403         if (test_args) {
1404                 result = EXIT_OK;
1405                 goto out;
1406         }
1407
1408         mask = NULL;
1409         /* make sure we have a trailing '\*' for directory */
1410         if (!isdirectory) {
1411                 mask = talloc_strdup(ctx, filename);
1412         } else if (strlen(filename) > 1) {
1413                 /*
1414                  * if the passed file name doesn't have a trailing '\'
1415                  * append it.
1416                  */
1417                 char *name_end = strrchr(filename, DIRSEP_CHAR);
1418                 if (name_end != filename + strlen(filename) + 1) {
1419                         mask = talloc_asprintf(ctx, "%s\\*", filename);
1420                 } else {
1421                         mask = talloc_strdup(ctx, filename);
1422                 }
1423         } else {
1424                 /* filename is a single '\', just append '*' */
1425                 mask = talloc_asprintf_append(mask, "%s*", filename);
1426         }
1427
1428         if (!mask) {
1429                 result = EXIT_FAILED;
1430                 goto out;
1431         }
1432
1433         /*
1434          * prepare for automatic propagation of the acl passed on the
1435          * cmdline.
1436          */
1437
1438         ntstatus = prepare_inheritance_propagation(ctx, filename,
1439                                                            cbstate);
1440         if (!NT_STATUS_IS_OK(ntstatus)) {
1441                 d_printf("error: %s processing %s\n",
1442                          nt_errstr(ntstatus), filename);
1443                 result = EXIT_FAILED;
1444                 goto out;
1445         }
1446
1447         result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1448                                 cbstate->mode, cbstate->numeric);
1449
1450         /*
1451          * strictly speaking it could be considered an error if a file was
1452          * specified with '--propagate-inheritance'. However we really want
1453          * to eventually get rid of '--propagate-inheritance' so we will be
1454          * more forgiving here and instead just exit early.
1455          */
1456         if (!isdirectory || (result != EXIT_OK)) {
1457                 goto out;
1458         }
1459
1460         /* check if there is actually any need to propagate */
1461         if (cbstate->acl_no_propagate) {
1462                 goto out;
1463         }
1464         /* get inheritable attributes this parent container (e.g. filename) */
1465         ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1466         if (NT_STATUS_IS_OK(ntstatus)) {
1467                 /* process children */
1468                 ntstatus = cli_list(cli, mask, attribute,
1469                                 cacl_set_cb,
1470                                 (void *)cbstate);
1471         }
1472
1473         if (!NT_STATUS_IS_OK(ntstatus)) {
1474                 d_printf("error: %s processing %s\n",
1475                          nt_errstr(ntstatus), filename);
1476                 result = EXIT_FAILED;
1477                 goto out;
1478         }
1479
1480 out:
1481         TALLOC_FREE(ctx);
1482         return result;
1483 }
1484
1485 struct diritem {
1486        struct diritem *prev, *next;
1487        /*
1488         * dirname and targetpath below are sanitized,
1489         * e.g.
1490         *   + start and end with '\'
1491         *   + have no trailing '*'
1492         *   + all '/' have been converted to '\'
1493         */
1494        char *dirname;
1495        char  *targetpath;
1496        struct cli_state *targetcli;
1497 };
1498
1499 struct save_restore_stats
1500 {
1501         int success;
1502         int failure;
1503 };
1504
1505 struct dump_context {
1506         struct diritem *list;
1507         struct cli_credentials *creds;
1508         struct cli_state *cli;
1509         struct save_restore_stats *stats;
1510         int save_fd;
1511         struct diritem *dir;
1512         NTSTATUS status;
1513 };
1514
1515 static int write_dacl(struct dump_context *ctx,
1516                       struct cli_state *cli,
1517                       const char *filename,
1518                       const char *origfname)
1519 {
1520         struct security_descriptor *sd = NULL;
1521         char *str = NULL;
1522         const char *output_fmt = "%s\r\n%s\r\n";
1523         const char *tmp = NULL;
1524         char *out_str = NULL;
1525         uint8_t *dest = NULL;
1526         ssize_t s_len;
1527         size_t d_len;
1528         bool ok;
1529         int result;
1530         TALLOC_CTX *frame = talloc_stackframe();
1531
1532         if (test_args) {
1533                 return EXIT_OK;
1534         }
1535
1536         if (ctx->save_fd < 0) {
1537                 DBG_ERR("error processing %s no file descriptor\n", filename);
1538                 result = EXIT_FAILED;
1539                 goto out;
1540         }
1541
1542         sd = get_secdesc(cli, filename);
1543         if (sd == NULL) {
1544                 result = EXIT_FAILED;
1545                 goto out;
1546         }
1547
1548         sd->owner_sid = NULL;
1549         sd->group_sid = NULL;
1550
1551         str = sddl_encode(frame, sd, get_domain_sid(cli));
1552         if (str == NULL) {
1553                 DBG_ERR("error processing %s couldn't encode DACL\n", filename);
1554                 result = EXIT_FAILED;
1555                 goto out;
1556         }
1557         /*
1558          * format of icacls save file is
1559          * a line containing the path of the file/dir
1560          * followed by a line containing the sddl format
1561          * of the dacl.
1562          * The format of the strings are null terminated
1563          * 16-bit Unicode. Each line is terminated by "\r\n"
1564          */
1565
1566         tmp = origfname;
1567         /* skip leading '\' */
1568         if (tmp[0] == '\\') {
1569                 tmp++;
1570         }
1571         out_str = talloc_asprintf(frame, output_fmt, tmp, str);
1572
1573         if (out_str == NULL) {
1574                 result = EXIT_FAILED;
1575                 goto out;
1576         }
1577
1578         s_len = strlen(out_str);
1579
1580         ok = convert_string_talloc(out_str,
1581                                    CH_UNIX,
1582                                    CH_UTF16,
1583                                    out_str,
1584                                    s_len, (void **)(void *)&dest, &d_len);
1585         if (!ok) {
1586                 DBG_ERR("error processing %s out of memory\n", tmp);
1587                 result = EXIT_FAILED;
1588                 goto out;
1589         }
1590
1591         if (write(ctx->save_fd, dest, d_len) != d_len) {
1592                 DBG_ERR("error processing %s failed to write to file.\n", tmp);
1593                 result = EXIT_FAILED;
1594                 goto out;
1595         }
1596         fsync(ctx->save_fd);
1597
1598         result = EXIT_OK;
1599         ctx->stats->success += 1;
1600         fprintf(stdout, "Successfully processed file: %s\n", tmp);
1601 out:
1602         TALLOC_FREE(frame);
1603         if (result != EXIT_OK) {
1604                 ctx->stats->failure += 1;
1605         }
1606         return result;
1607 }
1608
1609 /*
1610  * Sanitize directory name.
1611  * Given a directory name 'dir' ensure it;
1612  *    o starts with '\'
1613  *    o ends with '\'
1614  *    o doesn't end with trailing '*'
1615  *    o ensure all '/' are converted to '\'
1616  */
1617
1618 static char *sanitize_dirname(TALLOC_CTX *ctx,
1619                          const char *dir)
1620 {
1621         char *mask = NULL;
1622         char *name_end = NULL;
1623
1624         mask = talloc_strdup(ctx, dir);
1625         name_end = strrchr(mask, '*');
1626         if (name_end) {
1627                 *name_end = '\0';
1628         }
1629
1630         name_end = strrchr(mask, DIRSEP_CHAR);
1631
1632         if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) {
1633                 mask = talloc_asprintf(ctx, "%s\\", mask);
1634         }
1635
1636         string_replace(mask, '/', '\\');
1637         return mask;
1638 }
1639
1640 /*
1641  * Process each entry (child) of a directory.
1642  * Each entry, regardless of whether it is itself a file or directory
1643  * has it's dacl written to the restore/save file.
1644  * Each directory is saved to context->list (for further processing)
1645  * write_dacl will update the stats (success/fail)
1646  */
1647 static NTSTATUS cacl_dump_dacl_cb(struct file_info *f,
1648                                   const char *mask, void *state)
1649 {
1650         struct dump_context *ctx = talloc_get_type_abort(state,
1651                                                          struct dump_context);
1652
1653         NTSTATUS status;
1654
1655         char *mask2 = NULL;
1656         char *targetpath = NULL;
1657         char *unresolved = NULL;
1658
1659         /*
1660          * if we have already encountered an error
1661          * bail out
1662          */
1663         if (!NT_STATUS_IS_OK(ctx->status)) {
1664                 return ctx->status;
1665         }
1666
1667         if (!f->name || !f->name[0]) {
1668                 DBG_ERR("Empty dir name returned. Possible server "
1669                         "misconfiguration.\n");
1670                 status = NT_STATUS_UNSUCCESSFUL;
1671                 goto out;
1672         }
1673
1674         mask2 = sanitize_dirname(ctx, mask);
1675         if (!mask2) {
1676                 status = NT_STATUS_NO_MEMORY;
1677                 goto out;
1678         }
1679         if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1680                 struct diritem *item = NULL;
1681
1682                 /* ignore special '.' & '..' */
1683                 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1684                         status = NT_STATUS_OK;
1685                         goto out;
1686                 }
1687
1688                 /* Work out the directory. */
1689                 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1690                 if (!unresolved) {
1691                         status = NT_STATUS_NO_MEMORY;
1692                         goto out;
1693                 }
1694
1695                 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1696
1697                 if (unresolved == NULL) {
1698                         status = NT_STATUS_NO_MEMORY;
1699                         goto out;
1700                 }
1701
1702                 item = talloc_zero(ctx, struct diritem);
1703                 if (item == NULL) {
1704                         status = NT_STATUS_NO_MEMORY;
1705                         goto out;
1706                 }
1707
1708                 item->dirname = unresolved;
1709
1710                 mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1711                 if (!mask2) {
1712                         status = NT_STATUS_NO_MEMORY;
1713                         goto out;
1714                 }
1715
1716                 status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli,
1717                                           mask2, &item->targetcli, &targetpath);
1718
1719                 if (!NT_STATUS_IS_OK(status)) {
1720                         DBG_ERR("error failed to resolve: %s\n",
1721                                 nt_errstr(status));
1722                         goto out;
1723                 }
1724
1725                 item->targetpath = sanitize_dirname(ctx, targetpath);
1726                 if (!item->targetpath) {
1727                         status = NT_STATUS_NO_MEMORY;
1728                         goto out;
1729                 }
1730
1731                 if (write_dacl(ctx,
1732                                item->targetcli,
1733                                item->targetpath, unresolved) != EXIT_OK) {
1734                         status = NT_STATUS_UNSUCCESSFUL;
1735                         /*
1736                          * cli_list happily ignores error encountered
1737                          * when processing the callback so we need
1738                          * to save any error status encountered while
1739                          * processing directories (so we can stop recursing
1740                          * those as soon as possible).
1741                          * Changing the current behaviour of the callback
1742                          * handling by cli_list would be I think be too
1743                          * risky.
1744                          */
1745                         ctx->status = status;
1746                         goto out;
1747                 }
1748
1749                 DLIST_ADD_END(ctx->list, item);
1750
1751         } else {
1752                 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1753                 if (!unresolved) {
1754                         status = NT_STATUS_NO_MEMORY;
1755                         goto out;
1756                 }
1757
1758                 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1759
1760                 if (!unresolved) {
1761                         status = NT_STATUS_NO_MEMORY;
1762                         goto out;
1763                 }
1764                 /*
1765                  * build full path to the file and replace '/' with '\' so
1766                  * other utility functions can deal with it
1767                  */
1768
1769                 targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1770
1771                 if (!targetpath) {
1772                         status = NT_STATUS_NO_MEMORY;
1773                         goto out;
1774                 }
1775
1776                 if (write_dacl(ctx,
1777                                ctx->dir->targetcli,
1778                                targetpath, unresolved) != EXIT_OK) {
1779                         status = NT_STATUS_UNSUCCESSFUL;
1780                         /*
1781                          * cli_list happily ignores error encountered
1782                          * when processing the callback so we need
1783                          * to save any error status encountered while
1784                          * processing directories (so we can stop recursing
1785                          * those as soon as possible).
1786                          * Changing the current behaviour of the callback
1787                          * handling by cli_list would be I think be too
1788                          * risky.
1789                          */
1790                         ctx->status = status;
1791                         goto out;
1792                 }
1793         }
1794         status = NT_STATUS_OK;
1795 out:
1796         if (!NT_STATUS_IS_OK(status)) {
1797                 DBG_ERR("error %s: processing %s\n",
1798                         nt_errstr(status), targetpath);
1799         }
1800         return status;
1801 }
1802
1803 /*
1804  * dump_ctx contains a list of directories to be processed
1805  *    + each directory 'dir' is scanned by cli_list, the cli_list
1806  *      callback 'cacl_dump_dacl_cb' writes out the dacl of each
1807  *      child of 'dir' (regardless of whether it is a dir or file)
1808  *      to the restore/save file. Additionally any directories encountered
1809  *      are returned in the passed in dump_ctx->list member
1810  *    + the directory list returned from cli_list is passed and processed
1811  *      by recursively calling dump_dacl_dirtree
1812  *
1813  */
1814 static int dump_dacl_dirtree(struct dump_context *dump_ctx)
1815 {
1816         struct diritem *item = NULL;
1817         struct dump_context *new_dump_ctx = NULL;
1818         int result;
1819         for (item = dump_ctx->list; item; item = item->next) {
1820                 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1821                     | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1822                 NTSTATUS status;
1823                 char *mask = NULL;
1824
1825                 new_dump_ctx = talloc_zero(dump_ctx, struct dump_context);
1826
1827                 if (new_dump_ctx == NULL) {
1828                         DBG_ERR("out of memory\n");
1829                         result = EXIT_FAILED;
1830                         goto out;
1831                 }
1832
1833                 if (item->targetcli == NULL) {
1834                         status = cli_resolve_path(new_dump_ctx,
1835                                                   "",
1836                                                   dump_ctx->creds,
1837                                                   dump_ctx->cli,
1838                                                   item->dirname,
1839                                                   &item->targetcli,
1840                                                   &item->targetpath);
1841                         if (!NT_STATUS_IS_OK(status)) {
1842                                 DBG_ERR("failed to resolve path %s "
1843                                         "error: %s\n",
1844                                         item->dirname, nt_errstr(status));
1845                                 result = EXIT_FAILED;
1846                                 goto out;
1847                         }
1848                 }
1849                 new_dump_ctx->creds = dump_ctx->creds;
1850                 new_dump_ctx->save_fd = dump_ctx->save_fd;
1851                 new_dump_ctx->stats = dump_ctx->stats;
1852                 new_dump_ctx->dir = item;
1853                 new_dump_ctx->cli = item->targetcli;
1854
1855                 mask = talloc_asprintf(new_dump_ctx, "%s*",
1856                                        new_dump_ctx->dir->targetpath);
1857                 status = cli_list(new_dump_ctx->dir->targetcli,
1858                                   mask,
1859                                   attribute, cacl_dump_dacl_cb, new_dump_ctx);
1860
1861                 if (!NT_STATUS_IS_OK(status) ||
1862                     !NT_STATUS_IS_OK(new_dump_ctx->status)) {
1863                         NTSTATUS tmpstatus;
1864                         if (!NT_STATUS_IS_OK(status)) {
1865                                 /*
1866                                  * cli_list failed for some reason
1867                                  * so we need to update the failure stat
1868                                  */
1869                                 new_dump_ctx->stats->failure += 1;
1870                                 tmpstatus = status;
1871                         } else {
1872                                 /* cacl_dump_dacl_cb should have updated stat */
1873                                 tmpstatus = new_dump_ctx->status;
1874                         }
1875                         DBG_ERR("error %s: processing %s\n",
1876                                 nt_errstr(tmpstatus), item->dirname);
1877                         result = EXIT_FAILED;
1878                         goto out;
1879                 }
1880                 result = dump_dacl_dirtree(new_dump_ctx);
1881                 if (result != EXIT_OK) {
1882                         goto out;
1883                 }
1884         }
1885
1886         result = EXIT_OK;
1887 out:
1888         TALLOC_FREE(new_dump_ctx);
1889         return result;
1890 }
1891
1892 static int cacl_dump_dacl(struct cli_state *cli,
1893                           struct cli_credentials *creds,
1894                           char *filename)
1895 {
1896         int fileattr;
1897         char *mask = NULL;
1898         TALLOC_CTX *ctx = NULL;
1899         bool isdirectory = false;
1900         int result;
1901         struct dump_context *dump_ctx = NULL;
1902         struct save_restore_stats stats = {0};
1903         struct diritem *item = NULL;
1904         struct cli_state *targetcli = NULL;
1905         char *targetpath = NULL;
1906         NTSTATUS status;
1907
1908         ctx = talloc_init("cacl_dump");
1909         if (ctx == NULL) {
1910                 DBG_ERR("out of memory\n");
1911                 result = EXIT_FAILED;
1912                 goto out;
1913         }
1914
1915         dump_ctx = talloc_zero(ctx, struct dump_context);
1916         if (dump_ctx == NULL) {
1917                 DBG_ERR("out of memory\n");
1918                 result = EXIT_FAILED;
1919                 goto out;
1920         }
1921
1922         dump_ctx->save_fd = open(save_file,
1923                                  O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
1924
1925         if (dump_ctx->save_fd < 0) {
1926                 result = EXIT_FAILED;
1927                 goto out;
1928         }
1929
1930         dump_ctx->creds = creds;
1931         dump_ctx->cli = cli;
1932         dump_ctx->stats = &stats;
1933
1934         /* ensure we have a filename that starts with '\' */
1935         if (!filename || *filename != DIRSEP_CHAR) {
1936                 /* illegal or no filename */
1937                 result = EXIT_FAILED;
1938                 DBG_ERR("illegal or missing name '%s'\n", filename);
1939                 goto out;
1940         }
1941
1942         status = cli_resolve_path(dump_ctx, "",
1943                                   dump_ctx->creds,
1944                                   dump_ctx->cli,
1945                                   filename, &targetcli, &targetpath);
1946         if (!NT_STATUS_IS_OK(status)) {
1947                 DBG_ERR("failed resolve %s\n", filename);
1948                 result = EXIT_FAILED;
1949                 goto out;
1950         }
1951
1952         fileattr = get_fileinfo(targetcli, targetpath);
1953         isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1954             == FILE_ATTRIBUTE_DIRECTORY;
1955
1956         /*
1957          * if we've got as far as here then we have already evaluated
1958          * the args.
1959          */
1960         if (test_args) {
1961                 result = EXIT_OK;
1962                 goto out;
1963         }
1964
1965         mask = NULL;
1966         /* make sure we have a trailing '\*' for directory */
1967         if (!isdirectory) {
1968                 mask = talloc_strdup(ctx, filename);
1969         } else if (strlen(filename) > 1) {
1970                 mask = sanitize_dirname(ctx, filename);
1971         } else {
1972                 /* filename is a single '\' */
1973                 mask = talloc_strdup(ctx, filename);
1974         }
1975         if (!mask) {
1976                 result = EXIT_FAILED;
1977                 goto out;
1978         }
1979
1980         write_dacl(dump_ctx, targetcli, targetpath, filename);
1981         if (isdirectory && recurse) {
1982                 item = talloc_zero(dump_ctx, struct diritem);
1983                 if (!item) {
1984                         result = EXIT_FAILED;
1985                         goto out;
1986                 }
1987                 item->dirname = mask;
1988                 DLIST_ADD_END(dump_ctx->list, item);
1989                 dump_dacl_dirtree(dump_ctx);
1990         }
1991
1992         fprintf(stdout, "Successfully processed %d files: "
1993                 "Failed processing %d files\n",
1994                 dump_ctx->stats->success, dump_ctx->stats->failure);
1995         result = EXIT_OK;
1996 out:
1997         if (dump_ctx && dump_ctx->save_fd) {
1998                 close(dump_ctx->save_fd);
1999         }
2000         TALLOC_FREE(ctx);
2001         return result;
2002 }
2003
2004 /****************************************************************************
2005   main program
2006 ****************************************************************************/
2007 int main(int argc, char *argv[])
2008 {
2009         const char **argv_const = discard_const_p(const char *, argv);
2010         char *share;
2011         int opt;
2012         enum acl_mode mode = SMB_ACL_SET;
2013         static char *the_acl = NULL;
2014         enum chown_mode change_mode = REQUEST_NONE;
2015         int result;
2016         char *path;
2017         char *filename = NULL;
2018         poptContext pc;
2019         /* numeric is set when the user wants numeric SIDs and ACEs rather
2020            than going via LSA calls to resolve them */
2021         int numeric = 0;
2022         struct cli_state *targetcli = NULL;
2023         struct cli_credentials *creds = NULL;
2024         char *targetfile = NULL;
2025         NTSTATUS status;
2026         bool ok;
2027         struct loadparm_context *lp_ctx = NULL;
2028
2029         struct poptOption long_options[] = {
2030                 POPT_AUTOHELP
2031                 {
2032                         .longName   = "delete",
2033                         .shortName  = 'D',
2034                         .argInfo    = POPT_ARG_STRING,
2035                         .arg        = NULL,
2036                         .val        = 'D',
2037                         .descrip    = "Delete an acl",
2038                         .argDescrip = "ACL",
2039                 },
2040                 {
2041                         .longName   = "modify",
2042                         .shortName  = 'M',
2043                         .argInfo    = POPT_ARG_STRING,
2044                         .arg        = NULL,
2045                         .val        = 'M',
2046                         .descrip    = "Modify an acl",
2047                         .argDescrip = "ACL",
2048                 },
2049                 {
2050                         .longName   = "add",
2051                         .shortName  = 'a',
2052                         .argInfo    = POPT_ARG_STRING,
2053                         .arg        = NULL,
2054                         .val        = 'a',
2055                         .descrip    = "Add an acl",
2056                         .argDescrip = "ACL",
2057                 },
2058                 {
2059                         .longName   = "set",
2060                         .shortName  = 'S',
2061                         .argInfo    = POPT_ARG_STRING,
2062                         .arg        = NULL,
2063                         .val        = 'S',
2064                         .descrip    = "Set acls",
2065                         .argDescrip = "ACLS",
2066                 },
2067                 {
2068                         .longName   = "chown",
2069                         .shortName  = 'C',
2070                         .argInfo    = POPT_ARG_STRING,
2071                         .arg        = NULL,
2072                         .val        = 'C',
2073                         .descrip    = "Change ownership of a file",
2074                         .argDescrip = "USERNAME",
2075                 },
2076                 {
2077                         .longName   = "chgrp",
2078                         .shortName  = 'G',
2079                         .argInfo    = POPT_ARG_STRING,
2080                         .arg        = NULL,
2081                         .val        = 'G',
2082                         .descrip    = "Change group ownership of a file",
2083                         .argDescrip = "GROUPNAME",
2084                 },
2085                 {
2086                         .longName   = "inherit",
2087                         .shortName  = 'I',
2088                         .argInfo    = POPT_ARG_STRING,
2089                         .arg        = NULL,
2090                         .val        = 'I',
2091                         .descrip    = "Inherit allow|remove|copy",
2092                 },
2093                 {
2094                         .longName   = "propagate-inheritance",
2095                         .shortName  = 0,
2096                         .argInfo    = POPT_ARG_NONE,
2097                         .arg        = &inheritance,
2098                         .val        = 1,
2099                         .descrip    = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
2100                 },
2101                 {
2102                         .longName   = "save",
2103                         .shortName  = 0,
2104                         .argInfo    = POPT_ARG_STRING,
2105                         .arg        = &save_file,
2106                         .val        = 1,
2107                         .descrip    = "stores the DACLs in sddl format of the "
2108                                       "specified file or folder for later use "
2109                                       "with restore. SACLS, owner or integrity"
2110                                       " labels are not stored",
2111                 },
2112                 {
2113                         .longName   = "restore",
2114                         .shortName  = 0,
2115                         .argInfo    = POPT_ARG_STRING,
2116                         .arg        = &restore_file,
2117                         .val        = 1,
2118                         .descrip    = "applies the stored DACLS to files in "
2119                                       "directory.",
2120                 },
2121                 {
2122                         .longName   = "recurse",
2123                         .shortName  = 0,
2124                         .argInfo    = POPT_ARG_NONE,
2125                         .arg        = &recurse,
2126                         .val        = 1,
2127                         .descrip    = "indicates the operation is performed "
2128                                       "on directory and all files/directories"
2129                                       " below. (only applies to save option)",
2130                 },
2131                 {
2132                         .longName   = "numeric",
2133                         .shortName  = 0,
2134                         .argInfo    = POPT_ARG_NONE,
2135                         .arg        = &numeric,
2136                         .val        = 1,
2137                         .descrip    = "Don't resolve sids or masks to names",
2138                 },
2139                 {
2140                         .longName   = "sddl",
2141                         .shortName  = 0,
2142                         .argInfo    = POPT_ARG_NONE,
2143                         .arg        = &sddl,
2144                         .val        = 1,
2145                         .descrip    = "Output and input acls in sddl format",
2146                 },
2147                 {
2148                         .longName   = "query-security-info",
2149                         .shortName  = 0,
2150                         .argInfo    = POPT_ARG_INT,
2151                         .arg        = &query_sec_info,
2152                         .val        = 1,
2153                         .descrip    = "The security-info flags for queries"
2154                 },
2155                 {
2156                         .longName   = "set-security-info",
2157                         .shortName  = 0,
2158                         .argInfo    = POPT_ARG_INT,
2159                         .arg        = &set_sec_info,
2160                         .val        = 1,
2161                         .descrip    = "The security-info flags for modifications"
2162                 },
2163                 {
2164                         .longName   = "test-args",
2165                         .shortName  = 't',
2166                         .argInfo    = POPT_ARG_NONE,
2167                         .arg        = &test_args,
2168                         .val        = 1,
2169                         .descrip    = "Test arguments"
2170                 },
2171                 {
2172                         .longName   = "domain-sid",
2173                         .shortName  = 0,
2174                         .argInfo    = POPT_ARG_STRING,
2175                         .arg        = &domain_sid,
2176                         .val        = 0,
2177                         .descrip    = "Domain SID for sddl",
2178                         .argDescrip = "SID"},
2179                 {
2180                         .longName   = "maximum-access",
2181                         .shortName  = 'x',
2182                         .argInfo    = POPT_ARG_NONE,
2183                         .arg        = NULL,
2184                         .val        = 'x',
2185                         .descrip    = "Query maximum permissions",
2186                 },
2187                 POPT_COMMON_SAMBA
2188                 POPT_COMMON_CONNECTION
2189                 POPT_COMMON_CREDENTIALS
2190                 POPT_LEGACY_S3
2191                 POPT_COMMON_VERSION
2192                 POPT_TABLEEND
2193         };
2194
2195         struct cli_state *cli;
2196         TALLOC_CTX *frame = talloc_stackframe();
2197         const char *owner_username = "";
2198         char *server;
2199
2200         smb_init_locale();
2201
2202         ok = samba_cmdline_init(frame,
2203                                 SAMBA_CMDLINE_CONFIG_CLIENT,
2204                                 false /* require_smbconf */);
2205         if (!ok) {
2206                 DBG_ERR("Failed to init cmdline parser!\n");
2207                 TALLOC_FREE(frame);
2208                 exit(1);
2209         }
2210         lp_ctx = samba_cmdline_get_lp_ctx();
2211         /* set default debug level to 1 regardless of what smb.conf sets */
2212         lpcfg_set_cmdline(lp_ctx, "log level", "1");
2213
2214         setlinebuf(stdout);
2215
2216         pc = samba_popt_get_context(getprogname(),
2217                                     argc,
2218                                     argv_const,
2219                                     long_options,
2220                                     0);
2221         if (pc == NULL) {
2222                 DBG_ERR("Failed to setup popt context!\n");
2223                 TALLOC_FREE(frame);
2224                 exit(1);
2225         }
2226
2227         poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
2228                 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
2229
2230         while ((opt = poptGetNextOpt(pc)) != -1) {
2231                 switch (opt) {
2232                 case 'S':
2233                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2234                         mode = SMB_ACL_SET;
2235                         break;
2236
2237                 case 'D':
2238                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2239                         mode = SMB_ACL_DELETE;
2240                         break;
2241
2242                 case 'M':
2243                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2244                         mode = SMB_ACL_MODIFY;
2245                         break;
2246
2247                 case 'a':
2248                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2249                         mode = SMB_ACL_ADD;
2250                         break;
2251
2252                 case 'C':
2253                         owner_username = poptGetOptArg(pc);
2254                         change_mode = REQUEST_CHOWN;
2255                         break;
2256
2257                 case 'G':
2258                         owner_username = poptGetOptArg(pc);
2259                         change_mode = REQUEST_CHGRP;
2260                         break;
2261
2262                 case 'I':
2263                         owner_username = poptGetOptArg(pc);
2264                         change_mode = REQUEST_INHERIT;
2265                         break;
2266                 case 'm':
2267                         lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
2268                         break;
2269                 case 'x':
2270                         want_mxac = true;
2271                         break;
2272                 case POPT_ERROR_BADOPT:
2273                         fprintf(stderr, "\nInvalid option %s: %s\n\n",
2274                                 poptBadOption(pc, 0), poptStrerror(opt));
2275                         poptPrintUsage(pc, stderr, 0);
2276                         exit(1);
2277                 }
2278         }
2279         if (inheritance && !the_acl) {
2280                 poptPrintUsage(pc, stderr, 0);
2281                 return -1;
2282         }
2283
2284         if(!poptPeekArg(pc)) {
2285                 poptPrintUsage(pc, stderr, 0);
2286                 return -1;
2287         }
2288
2289         path = talloc_strdup(frame, poptGetArg(pc));
2290         if (!path) {
2291                 return -1;
2292         }
2293
2294         if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
2295                 printf("Invalid argument: %s\n", path);
2296                 return -1;
2297         }
2298
2299         if(!poptPeekArg(pc)) {
2300                 poptPrintUsage(pc, stderr, 0);
2301                 return -1;
2302         }
2303
2304         filename = talloc_strdup(frame, poptGetArg(pc));
2305         if (!filename) {
2306                 return -1;
2307         }
2308
2309         poptFreeContext(pc);
2310         samba_cmdline_burn(argc, argv);
2311
2312         string_replace(path,'/','\\');
2313
2314         server = talloc_strdup(frame, path+2);
2315         if (!server) {
2316                 return -1;
2317         }
2318         share = strchr_m(server,'\\');
2319         if (share == NULL) {
2320                 printf("Invalid argument\n");
2321                 return -1;
2322         }
2323
2324         *share = 0;
2325         share++;
2326
2327         creds = samba_cmdline_get_creds();
2328
2329         /* Make connection to server */
2330         if (!test_args) {
2331                 cli = connect_one(creds, server, share);
2332                 if (!cli) {
2333                         exit(EXIT_FAILED);
2334                 }
2335         } else {
2336                 exit(0);
2337         }
2338
2339         string_replace(filename, '/', '\\');
2340         if (filename[0] != '\\') {
2341                 filename = talloc_asprintf(frame,
2342                                 "\\%s",
2343                                 filename);
2344                 if (!filename) {
2345                         return -1;
2346                 }
2347         }
2348
2349         status = cli_resolve_path(frame,
2350                                   "",
2351                                   creds,
2352                                   cli,
2353                                   filename,
2354                                   &targetcli,
2355                                   &targetfile);
2356         if (!NT_STATUS_IS_OK(status)) {
2357                 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
2358                 return -1;
2359         }
2360
2361         /* Perform requested action */
2362
2363         if (change_mode == REQUEST_INHERIT) {
2364                 result = inherit(targetcli, targetfile, owner_username);
2365         } else if (change_mode != REQUEST_NONE) {
2366                 result = owner_set(targetcli, change_mode, targetfile, owner_username);
2367         } else if (the_acl) {
2368                 if (inheritance) {
2369                         struct cacl_callback_state cbstate = {
2370                                 .creds = creds,
2371                                 .cli = targetcli,
2372                                 .mode = mode,
2373                                 .the_acl = the_acl,
2374                                 .numeric = numeric,
2375                         };
2376                         result = inheritance_cacl_set(targetfile, &cbstate);
2377                 } else {
2378                         result =  cacl_set(targetcli,
2379                                            targetfile,
2380                                            the_acl,
2381                                            mode,
2382                                            numeric);
2383                 }
2384         } else {
2385                 if (save_file) {
2386                         sddl = 1;
2387                         result = cacl_dump_dacl(cli, creds, filename);
2388                 } else {
2389                         result = cacl_dump(targetcli, targetfile, numeric);
2390                 }
2391         }
2392
2393         gfree_all();
2394         TALLOC_FREE(frame);
2395
2396         return result;
2397 }