0c1d2b38c9357f9bd6a3728e46f7ceb4aa9c79da
[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 NUMERIC_CMP(ace1, ace2);
514
515         if (ace1->type != ace2->type) {
516                 /* note the reverse order */
517                 return NUMERIC_CMP(ace2->type, ace1->type);
518         }
519         if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
520                 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
521
522         if (ace1->flags != ace2->flags)
523                 return NUMERIC_CMP(ace1->flags, ace2->flags);
524
525         if (ace1->access_mask != ace2->access_mask)
526                 return NUMERIC_CMP(ace1->access_mask, ace2->access_mask);
527
528         if (ace1->size != ace2->size)
529                 return NUMERIC_CMP(ace1->size, ace2->size);
530
531         return memcmp(ace1, ace2, sizeof(struct security_ace));
532 }
533
534 static void sort_acl(struct security_acl *the_acl)
535 {
536         uint32_t i;
537         if (!the_acl) return;
538
539         TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
540
541         for (i=1;i<the_acl->num_aces;) {
542                 if (security_ace_equal(&the_acl->aces[i-1],
543                                        &the_acl->aces[i])) {
544                         ARRAY_DEL_ELEMENT(
545                                 the_acl->aces, i, the_acl->num_aces);
546                         the_acl->num_aces--;
547                 } else {
548                         i++;
549                 }
550         }
551 }
552
553 /*****************************************************
554 set the ACLs on a file given a security descriptor
555 *******************************************************/
556
557 static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
558                             struct security_descriptor *sd, enum acl_mode mode,
559                             bool numeric)
560 {
561         struct security_descriptor *old = NULL;
562         uint32_t i, j;
563         size_t sd_size;
564         int result = EXIT_OK;
565
566         if (!sd) return EXIT_PARSE_ERROR;
567         if (test_args) return EXIT_OK;
568
569         if (mode != SMB_ACL_SET) {
570                 /*
571                  * Do not fetch old ACL when it will be overwritten
572                  * completely with a new one.
573                  */
574                 old = get_secdesc(cli, filename);
575
576                 if (!old) {
577                         return EXIT_FAILED;
578                 }
579         }
580
581         /* the logic here is rather more complex than I would like */
582         switch (mode) {
583         case SMB_ACL_DELETE:
584                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
585                         bool found = False;
586
587                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
588                                 if (security_ace_equal(&sd->dacl->aces[i],
589                                                        &old->dacl->aces[j])) {
590                                         uint32_t k;
591                                         for (k=j; k<old->dacl->num_aces-1;k++) {
592                                                 old->dacl->aces[k] = old->dacl->aces[k+1];
593                                         }
594                                         old->dacl->num_aces--;
595                                         found = True;
596                                         break;
597                                 }
598                         }
599
600                         if (!found) {
601                                 printf("ACL for ACE:");
602                                 print_ace(cli, stdout, &sd->dacl->aces[i],
603                                           numeric);
604                                 printf(" not found\n");
605                         }
606                 }
607                 break;
608
609         case SMB_ACL_MODIFY:
610                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
611                         bool found = False;
612
613                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
614                                 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
615                                               &old->dacl->aces[j].trustee)) {
616                                         old->dacl->aces[j] = sd->dacl->aces[i];
617                                         found = True;
618                                 }
619                         }
620
621                         if (!found) {
622                                 fstring str;
623
624                                 SidToString(cli, str,
625                                             &sd->dacl->aces[i].trustee,
626                                             numeric);
627                                 printf("ACL for SID %s not found\n", str);
628                         }
629                 }
630
631                 if (sd->owner_sid) {
632                         old->owner_sid = sd->owner_sid;
633                 }
634
635                 if (sd->group_sid) {
636                         old->group_sid = sd->group_sid;
637                 }
638
639                 break;
640
641         case SMB_ACL_ADD:
642                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
643                         add_ace(&old->dacl, &sd->dacl->aces[i]);
644                 }
645                 break;
646
647         case SMB_ACL_SET:
648                 old = sd;
649                 break;
650         }
651
652         /* Denied ACE entries must come before allowed ones */
653         sort_acl(old->dacl);
654
655         /* Create new security descriptor and set it */
656
657         /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
658            But if we're sending an owner, even if it's the same as the one
659            that already exists then W2K3 insists we open with WRITE_OWNER access.
660            I need to check that setting a SD with no owner set works against WNT
661            and W2K. JRA.
662         */
663
664         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
665                            old->owner_sid, old->group_sid,
666                            NULL, old->dacl, &sd_size);
667
668         if (!set_secdesc(cli, filename, sd)) {
669                 result = EXIT_FAILED;
670         }
671
672         return result;
673 }
674
675 /*****************************************************
676 set the ACLs on a file given an ascii description
677 *******************************************************/
678
679 static int cacl_set(struct cli_state *cli, const char *filename,
680                     char *the_acl, enum acl_mode mode, bool numeric)
681 {
682         struct security_descriptor *sd = NULL;
683
684         if (sddl) {
685                 const char *msg = NULL;
686                 size_t msg_offset = 0;
687                 enum ace_condition_flags flags =
688                         ACE_CONDITION_FLAG_ALLOW_DEVICE;
689                 sd = sddl_decode_err_msg(talloc_tos(),
690                                         the_acl,
691                                         get_domain_sid(cli),
692                                         flags,
693                                         &msg,
694                                         &msg_offset);
695                 if (sd == NULL) {
696                         DBG_ERR("could not decode '%s'\n", the_acl);
697                         if (msg != NULL) {
698                                 DBG_ERR("                  %*c\n",
699                                         (int)msg_offset, '^');
700                                 DBG_ERR("error '%s'\n", msg);
701                         }
702                 }
703         } else {
704                 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
705         }
706
707         if (sd == NULL) {
708                 return EXIT_PARSE_ERROR;
709         }
710         if (test_args) {
711                 return EXIT_OK;
712         }
713         return cacl_set_from_sd(cli, filename, sd, mode, numeric);
714 }
715
716 /*****************************************************
717 set the inherit on a file
718 *******************************************************/
719 static int inherit(struct cli_state *cli, const char *filename,
720                    const char *type)
721 {
722         struct security_descriptor *old,*sd;
723         uint32_t oldattr;
724         size_t sd_size;
725         int result = EXIT_OK;
726
727         old = get_secdesc(cli, filename);
728
729         if (!old) {
730                 return EXIT_FAILED;
731         }
732
733         oldattr = get_fileinfo(cli,filename);
734
735         if (strcmp(type,"allow")==0) {
736                 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
737                     SEC_DESC_DACL_PROTECTED) {
738                         uint32_t i;
739                         char *parentname,*temp;
740                         struct security_descriptor *parent;
741                         temp = talloc_strdup(talloc_tos(), filename);
742
743                         old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
744
745                         /* look at parent and copy in all its inheritable ACL's. */
746                         string_replace(temp, '\\', '/');
747                         if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
748                                 return EXIT_FAILED;
749                         }
750                         string_replace(parentname, '/', '\\');
751                         parent = get_secdesc(cli,parentname);
752                         if (parent == NULL) {
753                                 return EXIT_FAILED;
754                         }
755                         for (i=0;i<parent->dacl->num_aces;i++) {
756                                 struct security_ace *ace=&parent->dacl->aces[i];
757                                 /* Add inherited flag to all aces */
758                                 ace->flags=ace->flags|
759                                            SEC_ACE_FLAG_INHERITED_ACE;
760                                 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
761                                         if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
762                                             SEC_ACE_FLAG_CONTAINER_INHERIT) {
763                                                 add_ace(&old->dacl, ace);
764                                         }
765                                 } else {
766                                         if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
767                                             SEC_ACE_FLAG_OBJECT_INHERIT) {
768                                                 /* clear flags for files */
769                                                 ace->flags=0;
770                                                 add_ace(&old->dacl, ace);
771                                         }
772                                 }
773                         }
774                 } else {
775                         printf("Already set to inheritable permissions.\n");
776                         return EXIT_FAILED;
777                 }
778         } else if (strcmp(type,"remove")==0) {
779                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
780                     SEC_DESC_DACL_PROTECTED) {
781                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
782
783                         /* remove all inherited ACL's. */
784                         if (old->dacl) {
785                                 int i;
786                                 struct security_acl *temp=old->dacl;
787                                 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
788                                 for (i=temp->num_aces-1;i>=0;i--) {
789                                         struct security_ace *ace=&temp->aces[i];
790                                         /* Remove all ace with INHERITED flag set */
791                                         if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
792                                             SEC_ACE_FLAG_INHERITED_ACE) {
793                                                 add_ace(&old->dacl,ace);
794                                         }
795                                 }
796                         }
797                 } else {
798                         printf("Already set to no inheritable permissions.\n");
799                         return EXIT_FAILED;
800                 }
801         } else if (strcmp(type,"copy")==0) {
802                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
803                     SEC_DESC_DACL_PROTECTED) {
804                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
805
806                         /*
807                          * convert all inherited ACL's to non
808                          * inherited ACL's.
809                          */
810                         if (old->dacl) {
811                                 uint32_t i;
812                                 for (i=0;i<old->dacl->num_aces;i++) {
813                                         struct security_ace *ace=&old->dacl->aces[i];
814                                         /* Remove INHERITED FLAG from all aces */
815                                         ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
816                                 }
817                         }
818                 } else {
819                         printf("Already set to no inheritable permissions.\n");
820                         return EXIT_FAILED;
821                 }
822         }
823
824         /* Denied ACE entries must come before allowed ones */
825         sort_acl(old->dacl);
826
827         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
828                            old->owner_sid, old->group_sid,
829                            NULL, old->dacl, &sd_size);
830
831         if (!set_secdesc(cli, filename, sd)) {
832                 result = EXIT_FAILED;
833         }
834
835         return result;
836 }
837
838 /*****************************************************
839  Return a connection to a server.
840 *******************************************************/
841 static struct cli_state *connect_one(struct cli_credentials *creds,
842                                      const char *server, const char *share)
843 {
844         struct cli_state *c = NULL;
845         NTSTATUS nt_status;
846         uint32_t flags = 0;
847
848         nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
849                                 NULL, 0,
850                                 share, "?????",
851                                 creds,
852                                 flags);
853         if (!NT_STATUS_IS_OK(nt_status)) {
854                 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
855                 return NULL;
856         }
857
858         return c;
859 }
860
861 /*
862  * Process resulting combination of mask & fname ensuring
863  * terminated with wildcard
864  */
865 static char *build_dirname(TALLOC_CTX *ctx,
866         const char *mask, char *dir, char *fname)
867 {
868         char *mask2 = NULL;
869         char *p = NULL;
870
871         mask2 = talloc_strdup(ctx, mask);
872         if (!mask2) {
873                 return NULL;
874         }
875         p = strrchr_m(mask2, DIRSEP_CHAR);
876         if (p) {
877                 p[1] = 0;
878         } else {
879                 mask2[0] = '\0';
880         }
881         mask2 = talloc_asprintf_append(mask2,
882                                 "%s\\*",
883                                 fname);
884         return mask2;
885 }
886
887 /*
888  * Returns a copy of the ACL flags in ace modified according
889  * to some inheritance rules.
890  *   a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
891  *   b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
892  *   c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
893  *      stripped from flags to be propagated to non-container children
894  *   d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
895  *      stripped from flags to be propagated if the NP flag
896  *      SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
897  */
898
899 static uint8_t get_flags_to_propagate(bool is_container,
900                                 struct security_ace *ace)
901 {
902         uint8_t newflags = ace->flags;
903         /* OBJECT inheritance */
904         bool acl_objinherit = (ace->flags &
905                 SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
906         /* CONTAINER inheritance */
907         bool acl_cntrinherit = (ace->flags &
908                 SEC_ACE_FLAG_CONTAINER_INHERIT) ==
909                         SEC_ACE_FLAG_CONTAINER_INHERIT;
910         /* PROHIBIT inheritance */
911         bool prohibit_inheritance = ((ace->flags &
912                 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
913                         SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
914
915         /* Assume we are not propagating the ACE */
916
917         newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
918         /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
919         if (acl_cntrinherit || acl_objinherit) {
920                 /*
921                  * object inherit ( alone ) on a container needs
922                  * SEC_ACE_FLAG_INHERIT_ONLY
923                  */
924                 if (is_container) {
925                         if (acl_objinherit && !acl_cntrinherit) {
926                                 newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
927                         }
928                         /*
929                          * this is tricky, the only time we would not
930                          * propagate the ace for a container is if
931                          * prohibit_inheritance is set and object inheritance
932                          * alone is set
933                          */
934                         if ((prohibit_inheritance
935                             && acl_objinherit
936                             && !acl_cntrinherit) == false) {
937                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
938                         }
939                 } else {
940                         /*
941                          * don't apply object/container inheritance flags to
942                          * non dirs
943                          */
944                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
945                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
946                                         | SEC_ACE_FLAG_INHERIT_ONLY);
947                         /*
948                          * only apply ace to file if object inherit
949                          */
950                         if (acl_objinherit) {
951                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
952                         }
953                 }
954
955                 /* if NP is specified strip NP and all OI/CI INHERIT flags */
956                 if (prohibit_inheritance) {
957                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
958                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
959                                         | SEC_ACE_FLAG_INHERIT_ONLY
960                                         | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
961                 }
962         }
963         return newflags;
964 }
965
966 /*
967  * This function builds a new acl for 'caclfile', first it removes any
968  * existing inheritable ace(s) from the current acl of caclfile, secondly it
969  * applies any inheritable acls of the parent of caclfile ( inheritable acls of
970  * caclfile's parent are passed via acl_to_add member of cbstate )
971  *
972  */
973 static NTSTATUS propagate_inherited_aces(char *caclfile,
974                         struct cacl_callback_state *cbstate)
975 {
976         TALLOC_CTX *aclctx = NULL;
977         NTSTATUS status;
978         int result;
979         int fileattr;
980         struct security_descriptor *old = NULL;
981         bool is_container = false;
982         struct security_acl *acl_to_add = cbstate->acl_to_add;
983         struct security_acl *acl_to_remove = NULL;
984         uint32_t i, j;
985
986         aclctx = talloc_new(NULL);
987         if (aclctx == NULL) {
988                 return NT_STATUS_NO_MEMORY;
989         }
990         old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
991
992         if (!old) {
993                 status = NT_STATUS_UNSUCCESSFUL;
994                 goto out;
995         }
996
997         /* inhibit propagation? */
998         if ((old->type & SEC_DESC_DACL_PROTECTED) ==
999                 SEC_DESC_DACL_PROTECTED){
1000                 status = NT_STATUS_OK;
1001                 goto out;
1002         }
1003
1004         fileattr = get_fileinfo(cbstate->cli, caclfile);
1005         is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
1006
1007         /* find acl(s) that are inherited */
1008         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1009
1010                 if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
1011                         if (!add_ace_with_ctx(aclctx, &acl_to_remove,
1012                                               &old->dacl->aces[j])) {
1013                                 status = NT_STATUS_NO_MEMORY;
1014                                 goto out;
1015                         }
1016                 }
1017         }
1018
1019         /* remove any acl(s) that are inherited */
1020         if (acl_to_remove) {
1021                 for (i = 0; i < acl_to_remove->num_aces; i++) {
1022                         struct security_ace ace = acl_to_remove->aces[i];
1023                         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1024
1025                                 if (security_ace_equal(&ace,
1026                                                   &old->dacl->aces[j])) {
1027                                         uint32_t k;
1028                                         for (k = j; k < old->dacl->num_aces-1;
1029                                                 k++) {
1030                                                 old->dacl->aces[k] =
1031                                                         old->dacl->aces[k+1];
1032                                         }
1033                                         old->dacl->num_aces--;
1034                                         break;
1035                                 }
1036                         }
1037                 }
1038         }
1039         /* propagate any inheritable ace to be added */
1040         if (acl_to_add) {
1041                 for (i = 0; i < acl_to_add->num_aces; i++) {
1042                         struct security_ace ace = acl_to_add->aces[i];
1043                         bool is_objectinherit = (ace.flags &
1044                                 SEC_ACE_FLAG_OBJECT_INHERIT) ==
1045                                         SEC_ACE_FLAG_OBJECT_INHERIT;
1046                         bool is_inherited;
1047                         /* don't propagate flags to a file unless OI */
1048                         if (!is_objectinherit && !is_container) {
1049                                 continue;
1050                         }
1051                         /*
1052                          * adjust flags according to inheritance
1053                          * rules
1054                          */
1055                         ace.flags = get_flags_to_propagate(is_container, &ace);
1056                         is_inherited = (ace.flags &
1057                                 SEC_ACE_FLAG_INHERITED_ACE) ==
1058                                         SEC_ACE_FLAG_INHERITED_ACE;
1059                         /* don't propagate non inherited flags */
1060                         if (!is_inherited) {
1061                                 continue;
1062                         }
1063                         if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1064                                 status = NT_STATUS_NO_MEMORY;
1065                                 goto out;
1066                         }
1067                 }
1068         }
1069
1070         result = cacl_set_from_sd(cbstate->cli, caclfile,
1071                                   old,
1072                                   SMB_ACL_SET, cbstate->numeric);
1073         if (result != EXIT_OK) {
1074                 status = NT_STATUS_UNSUCCESSFUL;
1075                 goto out;
1076         }
1077
1078         status = NT_STATUS_OK;
1079 out:
1080         TALLOC_FREE(aclctx);
1081         return status;
1082 }
1083
1084 /*
1085  * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1086  * SEC_ACE_FLAG_CONTAINER_INHERIT
1087  */
1088 static bool is_inheritable_ace(struct security_ace *ace)
1089 {
1090         uint8_t flags = ace->flags;
1091         if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1092                         | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1093                 return true;
1094         }
1095         return false;
1096 }
1097
1098 /* This method does some basic sanity checking with respect to automatic
1099  * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1100  * attempts to set inherited permissions directly. Additionally this method
1101  * does some basic initialisation for instance it parses the ACL passed on the
1102  * command line.
1103  */
1104 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1105                         struct cacl_callback_state *cbstate)
1106 {
1107         NTSTATUS result;
1108         char *the_acl = cbstate->the_acl;
1109         struct cli_state *cli = cbstate->cli;
1110         enum acl_mode mode = cbstate->mode;
1111         struct security_descriptor *sd = NULL;
1112         struct security_descriptor *old = NULL;
1113         uint32_t j;
1114         bool propagate = false;
1115
1116         old = get_secdesc_with_ctx(ctx, cli, filename);
1117         if (old == NULL) {
1118                 return NT_STATUS_NO_MEMORY;
1119         }
1120
1121         /* parse acl passed on the command line */
1122         if (sddl) {
1123                 const char *msg = NULL;
1124                 size_t msg_offset = 0;
1125                 enum ace_condition_flags flags =
1126                         ACE_CONDITION_FLAG_ALLOW_DEVICE;
1127
1128                 cbstate->aclsd = sddl_decode_err_msg(ctx,
1129                                                      the_acl,
1130                                                      get_domain_sid(cli),
1131                                                      flags,
1132                                                      &msg,
1133                                                      &msg_offset);
1134                 if (cbstate->aclsd == NULL) {
1135                         DBG_ERR("could not decode '%s'\n", the_acl);
1136                         if (msg != NULL) {
1137                                 DBG_ERR("                  %*c\n",
1138                                         (int)msg_offset, '^');
1139                                 DBG_ERR("error '%s'\n", msg);
1140                         }
1141                 }
1142         } else {
1143                 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1144         }
1145
1146         if (!cbstate->aclsd) {
1147                 result = NT_STATUS_UNSUCCESSFUL;
1148                 goto out;
1149         }
1150
1151         sd = cbstate->aclsd;
1152
1153         /* set operation if inheritance is enabled doesn't make sense */
1154         if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1155                 SEC_DESC_DACL_PROTECTED)){
1156                 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1157                 result = NT_STATUS_UNSUCCESSFUL;
1158                 goto out;
1159
1160         }
1161
1162         /*
1163          * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1164          * flags that are set
1165          */
1166         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1167                 struct security_ace *ace = &sd->dacl->aces[j];
1168                 if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1169                         d_printf("Illegal parameter %s\n", the_acl);
1170                         result = NT_STATUS_UNSUCCESSFUL;
1171                         goto out;
1172                 }
1173                 if (!propagate) {
1174                         if (is_inheritable_ace(ace)) {
1175                                 propagate = true;
1176                         }
1177                 }
1178         }
1179
1180         result = NT_STATUS_OK;
1181 out:
1182         cbstate->acl_no_propagate = !propagate;
1183         return result;
1184 }
1185
1186 /*
1187  * This method builds inheritable ace(s) from filename (which should be
1188  * a container) that need propagating to children in order to provide
1189  * automatic inheritance. Those inheritable ace(s) are stored in
1190  * acl_to_add member of cbstate for later processing
1191  * (see propagate_inherited_aces)
1192  */
1193 static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1194                         struct cacl_callback_state *cbstate)
1195 {
1196         NTSTATUS result;
1197         struct cli_state *cli = NULL;
1198         struct security_descriptor *sd = NULL;
1199         struct security_acl *acl_to_add = NULL;
1200         uint32_t j;
1201
1202         cli = cbstate->cli;
1203         sd = get_secdesc_with_ctx(ctx, cli, filename);
1204
1205         if (sd == NULL) {
1206                 return NT_STATUS_NO_MEMORY;
1207         }
1208
1209         /*
1210          * Check if any inheritance related flags are used, if not then
1211          * nothing to do. At the same time populate acls for inheritance
1212          * related ace(s) that need to be added to or deleted from children as
1213          * a result of inheritance propagation.
1214          */
1215
1216         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1217                 struct security_ace *ace = &sd->dacl->aces[j];
1218                 if (is_inheritable_ace(ace)) {
1219                         bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1220                         if (!added) {
1221                                 result = NT_STATUS_NO_MEMORY;
1222                                 goto out;
1223                         }
1224                 }
1225         }
1226         cbstate->acl_to_add = acl_to_add;
1227         result = NT_STATUS_OK;
1228 out:
1229         return result;
1230 }
1231
1232 /*
1233  * Callback handler to handle child elements processed by cli_list,  we attempt
1234  * to propagate inheritable ace(s) to each child via the function
1235  * propagate_inherited_aces. Children that are themselves directories are passed
1236  * to cli_list again ( to descend the directory structure )
1237  */
1238 static NTSTATUS cacl_set_cb(struct file_info *f,
1239                            const char *mask, void *state)
1240 {
1241         struct cacl_callback_state *cbstate =
1242                 (struct cacl_callback_state *)state;
1243         struct cli_state *cli = NULL;
1244         struct cli_credentials *creds = NULL;
1245
1246         TALLOC_CTX *dirctx = NULL;
1247         NTSTATUS status;
1248         struct cli_state *targetcli = NULL;
1249
1250         char *dir = NULL;
1251         char *dir_end = NULL;
1252         char *mask2 = NULL;
1253         char *targetpath = NULL;
1254         char *caclfile = NULL;
1255
1256         dirctx = talloc_new(NULL);
1257         if (!dirctx) {
1258                 status = NT_STATUS_NO_MEMORY;
1259                 goto out;
1260         }
1261
1262         cli = cbstate->cli;
1263         creds = cbstate->creds;
1264
1265         /* Work out the directory. */
1266         dir = talloc_strdup(dirctx, mask);
1267         if (!dir) {
1268                 status = NT_STATUS_NO_MEMORY;
1269                 goto out;
1270         }
1271
1272         dir_end = strrchr(dir, DIRSEP_CHAR);
1273         if (dir_end != NULL) {
1274                 *dir_end = '\0';
1275         }
1276
1277         if (!f->name || !f->name[0]) {
1278                 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1279                 status = NT_STATUS_UNSUCCESSFUL;
1280                 goto out;
1281         }
1282
1283         if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1284                 struct cacl_callback_state dir_cbstate;
1285                 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1286                         | FILE_ATTRIBUTE_SYSTEM
1287                         | FILE_ATTRIBUTE_HIDDEN;
1288                 dir_end = NULL;
1289
1290                 /* ignore special '.' & '..' */
1291                 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1292                         status = NT_STATUS_OK;
1293                         goto out;
1294                 }
1295
1296                 mask2 = build_dirname(dirctx, mask, dir, f->name);
1297                 if (mask2 == NULL) {
1298                         status = NT_STATUS_NO_MEMORY;
1299                         goto out;
1300                 }
1301
1302                 /* check for dfs */
1303                 status = cli_resolve_path(dirctx, "", creds, cli,
1304                         mask2, &targetcli, &targetpath);
1305                 if (!NT_STATUS_IS_OK(status)) {
1306                         goto out;
1307                 }
1308
1309                 /*
1310                  * prepare path to caclfile, remove any existing wildcard
1311                  * chars and convert path separators.
1312                  */
1313
1314                 caclfile = talloc_strdup(dirctx, targetpath);
1315                 if (!caclfile) {
1316                         status = NT_STATUS_NO_MEMORY;
1317                         goto out;
1318                 }
1319                 dir_end = strrchr(caclfile, '*');
1320                 if (dir_end != NULL) {
1321                         *dir_end = '\0';
1322                 }
1323
1324                 string_replace(caclfile, '/', '\\');
1325                 /*
1326                  * make directory specific copy of cbstate here
1327                  * (for this directory level) to be available as
1328                  * the parent cbstate for the children of this directory.
1329                  * Note: cbstate is overwritten for the current file being
1330                  *       processed.
1331                  */
1332                 dir_cbstate = *cbstate;
1333                 dir_cbstate.cli = targetcli;
1334
1335                 /*
1336                  * propagate any inherited ace from our parent
1337                  */
1338                 status = propagate_inherited_aces(caclfile, &dir_cbstate);
1339                 if (!NT_STATUS_IS_OK(status)) {
1340                         goto out;
1341                 }
1342
1343                 /*
1344                  * get inheritable ace(s) for this dir/container
1345                  * that will be propagated to its children
1346                  */
1347                 status = get_inheritable_aces(dirctx, caclfile,
1348                                                       &dir_cbstate);
1349                 if (!NT_STATUS_IS_OK(status)) {
1350                         goto out;
1351                 }
1352
1353                 /*
1354                  * ensure cacl_set_cb gets called for children
1355                  * of this directory (targetpath)
1356                  */
1357                 status = cli_list(targetcli, targetpath,
1358                         attribute, cacl_set_cb,
1359                         (void *)&dir_cbstate);
1360
1361                 if (!NT_STATUS_IS_OK(status)) {
1362                         goto out;
1363                 }
1364
1365         } else {
1366                 /*
1367                  * build full path to caclfile and replace '/' with '\' so
1368                  * other utility functions can deal with it
1369                  */
1370
1371                 targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1372                 if (!targetpath) {
1373                         status = NT_STATUS_NO_MEMORY;
1374                         goto out;
1375                 }
1376                 string_replace(targetpath, '/', '\\');
1377
1378                 /* attempt to propagate any inherited ace to file caclfile */
1379                 status = propagate_inherited_aces(targetpath, cbstate);
1380
1381                 if (!NT_STATUS_IS_OK(status)) {
1382                         goto out;
1383                 }
1384         }
1385         status = NT_STATUS_OK;
1386 out:
1387         if (!NT_STATUS_IS_OK(status)) {
1388                 d_printf("error %s: processing %s\n",
1389                         nt_errstr(status),
1390                         targetpath);
1391         }
1392         TALLOC_FREE(dirctx);
1393         return status;
1394 }
1395
1396
1397 /*
1398  * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1399  * helper callback function 'cacl_set_cb' handles the child elements processed
1400  * by cli_list.
1401  */
1402 static int inheritance_cacl_set(char *filename,
1403                         struct cacl_callback_state *cbstate)
1404 {
1405         int result;
1406         NTSTATUS ntstatus;
1407         int fileattr;
1408         char *mask = NULL;
1409         struct cli_state *cli = cbstate->cli;
1410         TALLOC_CTX *ctx = NULL;
1411         bool isdirectory = false;
1412         uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1413                                 | FILE_ATTRIBUTE_HIDDEN;
1414         ctx = talloc_init("inherit_set");
1415         if (ctx == NULL) {
1416                 d_printf("out of memory\n");
1417                 result = EXIT_FAILED;
1418                 goto out;
1419         }
1420
1421         /* ensure we have a filename that starts with '\' */
1422         if (!filename || *filename != DIRSEP_CHAR) {
1423                 /* illegal or no filename */
1424                 result = EXIT_FAILED;
1425                 d_printf("illegal or missing name '%s'\n", filename);
1426                 goto out;
1427         }
1428
1429
1430         fileattr = get_fileinfo(cli, filename);
1431         isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1432                 == FILE_ATTRIBUTE_DIRECTORY;
1433
1434         /*
1435          * if we've got as far as here then we have already evaluated
1436          * the args.
1437          */
1438         if (test_args) {
1439                 result = EXIT_OK;
1440                 goto out;
1441         }
1442
1443         mask = NULL;
1444         /* make sure we have a trailing '\*' for directory */
1445         if (!isdirectory) {
1446                 mask = talloc_strdup(ctx, filename);
1447         } else if (strlen(filename) > 1) {
1448                 /*
1449                  * if the passed file name doesn't have a trailing '\'
1450                  * append it.
1451                  */
1452                 char *name_end = strrchr(filename, DIRSEP_CHAR);
1453                 if (name_end != filename + strlen(filename) + 1) {
1454                         mask = talloc_asprintf(ctx, "%s\\*", filename);
1455                 } else {
1456                         mask = talloc_strdup(ctx, filename);
1457                 }
1458         } else {
1459                 /* filename is a single '\', just append '*' */
1460                 mask = talloc_asprintf_append(mask, "%s*", filename);
1461         }
1462
1463         if (!mask) {
1464                 result = EXIT_FAILED;
1465                 goto out;
1466         }
1467
1468         /*
1469          * prepare for automatic propagation of the acl passed on the
1470          * cmdline.
1471          */
1472
1473         ntstatus = prepare_inheritance_propagation(ctx, filename,
1474                                                            cbstate);
1475         if (!NT_STATUS_IS_OK(ntstatus)) {
1476                 d_printf("error: %s processing %s\n",
1477                          nt_errstr(ntstatus), filename);
1478                 result = EXIT_FAILED;
1479                 goto out;
1480         }
1481
1482         result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1483                                 cbstate->mode, cbstate->numeric);
1484
1485         /*
1486          * strictly speaking it could be considered an error if a file was
1487          * specified with '--propagate-inheritance'. However we really want
1488          * to eventually get rid of '--propagate-inheritance' so we will be
1489          * more forgiving here and instead just exit early.
1490          */
1491         if (!isdirectory || (result != EXIT_OK)) {
1492                 goto out;
1493         }
1494
1495         /* check if there is actually any need to propagate */
1496         if (cbstate->acl_no_propagate) {
1497                 goto out;
1498         }
1499         /* get inheritable attributes this parent container (e.g. filename) */
1500         ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1501         if (NT_STATUS_IS_OK(ntstatus)) {
1502                 /* process children */
1503                 ntstatus = cli_list(cli, mask, attribute,
1504                                 cacl_set_cb,
1505                                 (void *)cbstate);
1506         }
1507
1508         if (!NT_STATUS_IS_OK(ntstatus)) {
1509                 d_printf("error: %s processing %s\n",
1510                          nt_errstr(ntstatus), filename);
1511                 result = EXIT_FAILED;
1512                 goto out;
1513         }
1514
1515 out:
1516         TALLOC_FREE(ctx);
1517         return result;
1518 }
1519
1520 struct diritem {
1521        struct diritem *prev, *next;
1522        /*
1523         * dirname and targetpath below are sanitized,
1524         * e.g.
1525         *   + start and end with '\'
1526         *   + have no trailing '*'
1527         *   + all '/' have been converted to '\'
1528         */
1529        char *dirname;
1530        char  *targetpath;
1531        struct cli_state *targetcli;
1532 };
1533
1534 struct save_restore_stats
1535 {
1536         int success;
1537         int failure;
1538 };
1539
1540 struct dump_context {
1541         struct diritem *list;
1542         struct cli_credentials *creds;
1543         struct cli_state *cli;
1544         struct save_restore_stats *stats;
1545         int save_fd;
1546         struct diritem *dir;
1547         NTSTATUS status;
1548 };
1549
1550 static int write_dacl(struct dump_context *ctx,
1551                       struct cli_state *cli,
1552                       const char *filename,
1553                       const char *origfname)
1554 {
1555         struct security_descriptor *sd = NULL;
1556         char *str = NULL;
1557         const char *output_fmt = "%s\r\n%s\r\n";
1558         const char *tmp = NULL;
1559         char *out_str = NULL;
1560         uint8_t *dest = NULL;
1561         ssize_t s_len;
1562         size_t d_len;
1563         bool ok;
1564         int result;
1565         TALLOC_CTX *frame = talloc_stackframe();
1566
1567         if (test_args) {
1568                 return EXIT_OK;
1569         }
1570
1571         if (ctx->save_fd < 0) {
1572                 DBG_ERR("error processing %s no file descriptor\n", filename);
1573                 result = EXIT_FAILED;
1574                 goto out;
1575         }
1576
1577         sd = get_secdesc(cli, filename);
1578         if (sd == NULL) {
1579                 result = EXIT_FAILED;
1580                 goto out;
1581         }
1582
1583         sd->owner_sid = NULL;
1584         sd->group_sid = NULL;
1585
1586         str = sddl_encode(frame, sd, get_domain_sid(cli));
1587         if (str == NULL) {
1588                 DBG_ERR("error processing %s couldn't encode DACL\n", filename);
1589                 result = EXIT_FAILED;
1590                 goto out;
1591         }
1592         /*
1593          * format of icacls save file is
1594          * a line containing the path of the file/dir
1595          * followed by a line containing the sddl format
1596          * of the dacl.
1597          * The format of the strings are null terminated
1598          * 16-bit Unicode. Each line is terminated by "\r\n"
1599          */
1600
1601         tmp = origfname;
1602         /* skip leading '\' */
1603         if (tmp[0] == '\\') {
1604                 tmp++;
1605         }
1606         out_str = talloc_asprintf(frame, output_fmt, tmp, str);
1607
1608         if (out_str == NULL) {
1609                 result = EXIT_FAILED;
1610                 goto out;
1611         }
1612
1613         s_len = strlen(out_str);
1614
1615         ok = convert_string_talloc(out_str,
1616                                    CH_UNIX,
1617                                    CH_UTF16,
1618                                    out_str,
1619                                    s_len, (void **)(void *)&dest, &d_len);
1620         if (!ok) {
1621                 DBG_ERR("error processing %s out of memory\n", tmp);
1622                 result = EXIT_FAILED;
1623                 goto out;
1624         }
1625
1626         if (write(ctx->save_fd, dest, d_len) != d_len) {
1627                 DBG_ERR("error processing %s failed to write to file.\n", tmp);
1628                 result = EXIT_FAILED;
1629                 goto out;
1630         }
1631         fsync(ctx->save_fd);
1632
1633         result = EXIT_OK;
1634         ctx->stats->success += 1;
1635         fprintf(stdout, "Successfully processed file: %s\n", tmp);
1636 out:
1637         TALLOC_FREE(frame);
1638         if (result != EXIT_OK) {
1639                 ctx->stats->failure += 1;
1640         }
1641         return result;
1642 }
1643
1644 /*
1645  * Sanitize directory name.
1646  * Given a directory name 'dir' ensure it;
1647  *    o starts with '\'
1648  *    o ends with '\'
1649  *    o doesn't end with trailing '*'
1650  *    o ensure all '/' are converted to '\'
1651  */
1652
1653 static char *sanitize_dirname(TALLOC_CTX *ctx,
1654                          const char *dir)
1655 {
1656         char *mask = NULL;
1657         char *name_end = NULL;
1658
1659         mask = talloc_strdup(ctx, dir);
1660         name_end = strrchr(mask, '*');
1661         if (name_end) {
1662                 *name_end = '\0';
1663         }
1664
1665         name_end = strrchr(mask, DIRSEP_CHAR);
1666
1667         if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) {
1668                 mask = talloc_asprintf(ctx, "%s\\", mask);
1669         }
1670
1671         string_replace(mask, '/', '\\');
1672         return mask;
1673 }
1674
1675 /*
1676  * Process each entry (child) of a directory.
1677  * Each entry, regardless of whether it is itself a file or directory
1678  * has it's dacl written to the restore/save file.
1679  * Each directory is saved to context->list (for further processing)
1680  * write_dacl will update the stats (success/fail)
1681  */
1682 static NTSTATUS cacl_dump_dacl_cb(struct file_info *f,
1683                                   const char *mask, void *state)
1684 {
1685         struct dump_context *ctx = talloc_get_type_abort(state,
1686                                                          struct dump_context);
1687
1688         NTSTATUS status;
1689
1690         char *mask2 = NULL;
1691         char *targetpath = NULL;
1692         char *unresolved = NULL;
1693
1694         /*
1695          * if we have already encountered an error
1696          * bail out
1697          */
1698         if (!NT_STATUS_IS_OK(ctx->status)) {
1699                 return ctx->status;
1700         }
1701
1702         if (!f->name || !f->name[0]) {
1703                 DBG_ERR("Empty dir name returned. Possible server "
1704                         "misconfiguration.\n");
1705                 status = NT_STATUS_UNSUCCESSFUL;
1706                 goto out;
1707         }
1708
1709         mask2 = sanitize_dirname(ctx, mask);
1710         if (!mask2) {
1711                 status = NT_STATUS_NO_MEMORY;
1712                 goto out;
1713         }
1714         if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1715                 struct diritem *item = NULL;
1716
1717                 /* ignore special '.' & '..' */
1718                 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1719                         status = NT_STATUS_OK;
1720                         goto out;
1721                 }
1722
1723                 /* Work out the directory. */
1724                 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1725                 if (!unresolved) {
1726                         status = NT_STATUS_NO_MEMORY;
1727                         goto out;
1728                 }
1729
1730                 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1731
1732                 if (unresolved == NULL) {
1733                         status = NT_STATUS_NO_MEMORY;
1734                         goto out;
1735                 }
1736
1737                 item = talloc_zero(ctx, struct diritem);
1738                 if (item == NULL) {
1739                         status = NT_STATUS_NO_MEMORY;
1740                         goto out;
1741                 }
1742
1743                 item->dirname = unresolved;
1744
1745                 mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1746                 if (!mask2) {
1747                         status = NT_STATUS_NO_MEMORY;
1748                         goto out;
1749                 }
1750
1751                 status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli,
1752                                           mask2, &item->targetcli, &targetpath);
1753
1754                 if (!NT_STATUS_IS_OK(status)) {
1755                         DBG_ERR("error failed to resolve: %s\n",
1756                                 nt_errstr(status));
1757                         goto out;
1758                 }
1759
1760                 item->targetpath = sanitize_dirname(ctx, targetpath);
1761                 if (!item->targetpath) {
1762                         status = NT_STATUS_NO_MEMORY;
1763                         goto out;
1764                 }
1765
1766                 if (write_dacl(ctx,
1767                                item->targetcli,
1768                                item->targetpath, unresolved) != EXIT_OK) {
1769                         status = NT_STATUS_UNSUCCESSFUL;
1770                         /*
1771                          * cli_list happily ignores error encountered
1772                          * when processing the callback so we need
1773                          * to save any error status encountered while
1774                          * processing directories (so we can stop recursing
1775                          * those as soon as possible).
1776                          * Changing the current behaviour of the callback
1777                          * handling by cli_list would be I think be too
1778                          * risky.
1779                          */
1780                         ctx->status = status;
1781                         goto out;
1782                 }
1783
1784                 DLIST_ADD_END(ctx->list, item);
1785
1786         } else {
1787                 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1788                 if (!unresolved) {
1789                         status = NT_STATUS_NO_MEMORY;
1790                         goto out;
1791                 }
1792
1793                 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1794
1795                 if (!unresolved) {
1796                         status = NT_STATUS_NO_MEMORY;
1797                         goto out;
1798                 }
1799                 /*
1800                  * build full path to the file and replace '/' with '\' so
1801                  * other utility functions can deal with it
1802                  */
1803
1804                 targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1805
1806                 if (!targetpath) {
1807                         status = NT_STATUS_NO_MEMORY;
1808                         goto out;
1809                 }
1810
1811                 if (write_dacl(ctx,
1812                                ctx->dir->targetcli,
1813                                targetpath, unresolved) != EXIT_OK) {
1814                         status = NT_STATUS_UNSUCCESSFUL;
1815                         /*
1816                          * cli_list happily ignores error encountered
1817                          * when processing the callback so we need
1818                          * to save any error status encountered while
1819                          * processing directories (so we can stop recursing
1820                          * those as soon as possible).
1821                          * Changing the current behaviour of the callback
1822                          * handling by cli_list would be I think be too
1823                          * risky.
1824                          */
1825                         ctx->status = status;
1826                         goto out;
1827                 }
1828         }
1829         status = NT_STATUS_OK;
1830 out:
1831         if (!NT_STATUS_IS_OK(status)) {
1832                 DBG_ERR("error %s: processing %s\n",
1833                         nt_errstr(status), targetpath);
1834         }
1835         return status;
1836 }
1837
1838 /*
1839  * dump_ctx contains a list of directories to be processed
1840  *    + each directory 'dir' is scanned by cli_list, the cli_list
1841  *      callback 'cacl_dump_dacl_cb' writes out the dacl of each
1842  *      child of 'dir' (regardless of whether it is a dir or file)
1843  *      to the restore/save file. Additionally any directories encountered
1844  *      are returned in the passed in dump_ctx->list member
1845  *    + the directory list returned from cli_list is passed and processed
1846  *      by recursively calling dump_dacl_dirtree
1847  *
1848  */
1849 static int dump_dacl_dirtree(struct dump_context *dump_ctx)
1850 {
1851         struct diritem *item = NULL;
1852         struct dump_context *new_dump_ctx = NULL;
1853         int result;
1854         for (item = dump_ctx->list; item; item = item->next) {
1855                 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1856                     | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1857                 NTSTATUS status;
1858                 char *mask = NULL;
1859
1860                 new_dump_ctx = talloc_zero(dump_ctx, struct dump_context);
1861
1862                 if (new_dump_ctx == NULL) {
1863                         DBG_ERR("out of memory\n");
1864                         result = EXIT_FAILED;
1865                         goto out;
1866                 }
1867
1868                 if (item->targetcli == NULL) {
1869                         status = cli_resolve_path(new_dump_ctx,
1870                                                   "",
1871                                                   dump_ctx->creds,
1872                                                   dump_ctx->cli,
1873                                                   item->dirname,
1874                                                   &item->targetcli,
1875                                                   &item->targetpath);
1876                         if (!NT_STATUS_IS_OK(status)) {
1877                                 DBG_ERR("failed to resolve path %s "
1878                                         "error: %s\n",
1879                                         item->dirname, nt_errstr(status));
1880                                 result = EXIT_FAILED;
1881                                 goto out;
1882                         }
1883                 }
1884                 new_dump_ctx->creds = dump_ctx->creds;
1885                 new_dump_ctx->save_fd = dump_ctx->save_fd;
1886                 new_dump_ctx->stats = dump_ctx->stats;
1887                 new_dump_ctx->dir = item;
1888                 new_dump_ctx->cli = item->targetcli;
1889
1890                 mask = talloc_asprintf(new_dump_ctx, "%s*",
1891                                        new_dump_ctx->dir->targetpath);
1892                 status = cli_list(new_dump_ctx->dir->targetcli,
1893                                   mask,
1894                                   attribute, cacl_dump_dacl_cb, new_dump_ctx);
1895
1896                 if (!NT_STATUS_IS_OK(status) ||
1897                     !NT_STATUS_IS_OK(new_dump_ctx->status)) {
1898                         NTSTATUS tmpstatus;
1899                         if (!NT_STATUS_IS_OK(status)) {
1900                                 /*
1901                                  * cli_list failed for some reason
1902                                  * so we need to update the failure stat
1903                                  */
1904                                 new_dump_ctx->stats->failure += 1;
1905                                 tmpstatus = status;
1906                         } else {
1907                                 /* cacl_dump_dacl_cb should have updated stat */
1908                                 tmpstatus = new_dump_ctx->status;
1909                         }
1910                         DBG_ERR("error %s: processing %s\n",
1911                                 nt_errstr(tmpstatus), item->dirname);
1912                         result = EXIT_FAILED;
1913                         goto out;
1914                 }
1915                 result = dump_dacl_dirtree(new_dump_ctx);
1916                 if (result != EXIT_OK) {
1917                         goto out;
1918                 }
1919         }
1920
1921         result = EXIT_OK;
1922 out:
1923         TALLOC_FREE(new_dump_ctx);
1924         return result;
1925 }
1926
1927 static int cacl_dump_dacl(struct cli_state *cli,
1928                           struct cli_credentials *creds,
1929                           char *filename)
1930 {
1931         int fileattr;
1932         char *mask = NULL;
1933         TALLOC_CTX *ctx = NULL;
1934         bool isdirectory = false;
1935         int result;
1936         struct dump_context *dump_ctx = NULL;
1937         struct save_restore_stats stats = {0};
1938         struct diritem *item = NULL;
1939         struct cli_state *targetcli = NULL;
1940         char *targetpath = NULL;
1941         NTSTATUS status;
1942
1943         ctx = talloc_init("cacl_dump");
1944         if (ctx == NULL) {
1945                 DBG_ERR("out of memory\n");
1946                 result = EXIT_FAILED;
1947                 goto out;
1948         }
1949
1950         dump_ctx = talloc_zero(ctx, struct dump_context);
1951         if (dump_ctx == NULL) {
1952                 DBG_ERR("out of memory\n");
1953                 result = EXIT_FAILED;
1954                 goto out;
1955         }
1956
1957         dump_ctx->save_fd = open(save_file,
1958                                  O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
1959
1960         if (dump_ctx->save_fd < 0) {
1961                 result = EXIT_FAILED;
1962                 goto out;
1963         }
1964
1965         dump_ctx->creds = creds;
1966         dump_ctx->cli = cli;
1967         dump_ctx->stats = &stats;
1968
1969         /* ensure we have a filename that starts with '\' */
1970         if (!filename || *filename != DIRSEP_CHAR) {
1971                 /* illegal or no filename */
1972                 result = EXIT_FAILED;
1973                 DBG_ERR("illegal or missing name '%s'\n", filename);
1974                 goto out;
1975         }
1976
1977         status = cli_resolve_path(dump_ctx, "",
1978                                   dump_ctx->creds,
1979                                   dump_ctx->cli,
1980                                   filename, &targetcli, &targetpath);
1981         if (!NT_STATUS_IS_OK(status)) {
1982                 DBG_ERR("failed resolve %s\n", filename);
1983                 result = EXIT_FAILED;
1984                 goto out;
1985         }
1986
1987         fileattr = get_fileinfo(targetcli, targetpath);
1988         isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1989             == FILE_ATTRIBUTE_DIRECTORY;
1990
1991         /*
1992          * if we've got as far as here then we have already evaluated
1993          * the args.
1994          */
1995         if (test_args) {
1996                 result = EXIT_OK;
1997                 goto out;
1998         }
1999
2000         mask = NULL;
2001         /* make sure we have a trailing '\*' for directory */
2002         if (!isdirectory) {
2003                 mask = talloc_strdup(ctx, filename);
2004         } else if (strlen(filename) > 1) {
2005                 mask = sanitize_dirname(ctx, filename);
2006         } else {
2007                 /* filename is a single '\' */
2008                 mask = talloc_strdup(ctx, filename);
2009         }
2010         if (!mask) {
2011                 result = EXIT_FAILED;
2012                 goto out;
2013         }
2014
2015         write_dacl(dump_ctx, targetcli, targetpath, filename);
2016         if (isdirectory && recurse) {
2017                 item = talloc_zero(dump_ctx, struct diritem);
2018                 if (!item) {
2019                         result = EXIT_FAILED;
2020                         goto out;
2021                 }
2022                 item->dirname = mask;
2023                 DLIST_ADD_END(dump_ctx->list, item);
2024                 dump_dacl_dirtree(dump_ctx);
2025         }
2026
2027         fprintf(stdout, "Successfully processed %d files: "
2028                 "Failed processing %d files\n",
2029                 dump_ctx->stats->success, dump_ctx->stats->failure);
2030         result = EXIT_OK;
2031 out:
2032         if (dump_ctx && dump_ctx->save_fd > 0) {
2033                 close(dump_ctx->save_fd);
2034         }
2035         TALLOC_FREE(ctx);
2036         return result;
2037 }
2038
2039 struct restore_dacl {
2040         const char *path;
2041         struct security_descriptor *sd;
2042 };
2043
2044 /*
2045  * Restore dacls from 'savefile' produced by
2046  * 'icacls name /save' or 'smbcacls --save'
2047  */
2048 static int cacl_restore(struct cli_state *cli,
2049                         struct cli_credentials *creds,
2050                         bool numeric, const char *restorefile)
2051 {
2052         int restore_fd;
2053         int result;
2054         struct save_restore_stats stats = { 0 };
2055
2056         char **lines = NULL;
2057         char *content = NULL;
2058         char *convert_content = NULL;
2059         size_t content_size;
2060         struct restore_dacl *entries = NULL;
2061         int numlines, i = 0;
2062         bool ok;
2063         struct dom_sid *sid = NULL;
2064
2065         if (restorefile == NULL) {
2066                 DBG_ERR("No restore file specified\n");
2067                 result = EXIT_FAILED;
2068                 goto out;
2069         }
2070
2071         if (test_args) {
2072                 result = EXIT_OK;
2073                 goto out;
2074         }
2075
2076         restore_fd = open(restorefile, O_RDONLY, S_IRUSR | S_IWUSR);
2077         if (restore_fd < 0) {
2078                 DBG_ERR("Failed to open %s.\n", restorefile);
2079                 result = EXIT_FAILED;
2080                 goto out;
2081         }
2082
2083         content = fd_load(restore_fd, &content_size, 0, talloc_tos());
2084
2085         close(restore_fd);
2086
2087         if (content == NULL) {
2088                 DBG_ERR("Failed to load content from %s.\n", restorefile);
2089                 result = EXIT_FAILED;
2090                 goto out;
2091         }
2092
2093         ok = convert_string_talloc(talloc_tos(),
2094                                    CH_UTF16,
2095                                    CH_UNIX,
2096                                    content,
2097                                    utf16_len_n(content, content_size),
2098                                    (void **)(void *)&convert_content,
2099                                    &content_size);
2100
2101         TALLOC_FREE(content);
2102
2103         if (!ok) {
2104                 DBG_ERR("Failed to convert content from %s "
2105                         "to CH_UNIX.\n", restorefile);
2106                 result = EXIT_FAILED;
2107                 goto out;
2108         }
2109
2110         lines = file_lines_parse(convert_content,
2111                                  content_size, &numlines, talloc_tos());
2112
2113         if (lines == NULL) {
2114                 DBG_ERR("Failed to parse lines from content of %s.",
2115                         restorefile);
2116                 result = EXIT_FAILED;
2117                 goto out;
2118         }
2119
2120         entries = talloc_zero_array(lines, struct restore_dacl, numlines / 2);
2121
2122         if (entries == NULL) {
2123                 DBG_ERR("error processing %s, out of memory\n", restorefile);
2124                 result = EXIT_FAILED;
2125                 goto out;
2126         }
2127
2128         sid = get_domain_sid(cli);
2129
2130         while (i < numlines) {
2131                 int index = i / 2;
2132                 int first_line = (i % 2) == 0;
2133
2134                 if (first_line) {
2135                         char *tmp = NULL;
2136                         tmp = lines[i];
2137                         /* line can be blank if root of share */
2138                         if (strlen(tmp) == 0) {
2139                                 entries[index].path = talloc_strdup(lines,
2140                                                                     "\\");
2141                         } else {
2142                                 entries[index].path = lines[i];
2143                         }
2144                 } else {
2145                         const char *msg = NULL;
2146                         size_t msg_offset = 0;
2147                         enum ace_condition_flags flags =
2148                                 ACE_CONDITION_FLAG_ALLOW_DEVICE;
2149                         entries[index].sd = sddl_decode_err_msg(lines,
2150                                                                 lines[i],
2151                                                                 sid,
2152                                                                 flags,
2153                                                                 &msg,
2154                                                                 &msg_offset);
2155                         if(entries[index].sd == NULL) {
2156                                 DBG_ERR("could not decode '%s'\n", lines[i]);
2157                                 if (msg != NULL) {
2158                                         DBG_ERR("                  %*c\n",
2159                                                 (int)msg_offset, '^');
2160                                         DBG_ERR("error '%s'\n", msg);
2161                                 }
2162                                 result = EXIT_FAILED;
2163                                 goto out;
2164                         }
2165                         entries[index].sd->type |=
2166                             SEC_DESC_DACL_AUTO_INHERIT_REQ;
2167                         entries[index].sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
2168                 }
2169                 i++;
2170         }
2171         for (i = 0; i < (numlines / 2); i++) {
2172                 int mode = SMB_ACL_SET;
2173                 int set_result;
2174                 struct cli_state *targetcli = NULL;
2175                 char *targetpath = NULL;
2176                 NTSTATUS status;
2177
2178                 /* check for dfs */
2179                 status = cli_resolve_path(talloc_tos(),
2180                                           "",
2181                                           creds,
2182                                           cli,
2183                                           entries[i].path,
2184                                           &targetcli, &targetpath);
2185
2186                 if (!NT_STATUS_IS_OK(status)) {
2187                         printf("Error failed to process file: %s\n",
2188                                entries[i].path);
2189                         stats.failure += 1;
2190                         continue;
2191                 }
2192
2193                 set_result = cacl_set_from_sd(targetcli,
2194                                               targetpath,
2195                                               entries[i].sd, mode, numeric);
2196
2197                 if (set_result == EXIT_OK) {
2198                         printf("Successfully processed file: %s\n",
2199                                entries[i].path);
2200                         stats.success += 1;
2201                 } else {
2202                         printf("Error failed to process file: %s\n",
2203                                entries[i].path);
2204                         stats.failure += 1;
2205                 }
2206         }
2207
2208         result = EXIT_OK;
2209 out:
2210         TALLOC_FREE(lines);
2211         fprintf(stdout, "Successfully processed %d files: "
2212                 "Failed processing %d files\n", stats.success, stats.failure);
2213         return result;
2214 }
2215
2216 /****************************************************************************
2217   main program
2218 ****************************************************************************/
2219 int main(int argc, char *argv[])
2220 {
2221         const char **argv_const = discard_const_p(const char *, argv);
2222         char *share;
2223         int opt;
2224         enum acl_mode mode = SMB_ACL_SET;
2225         static char *the_acl = NULL;
2226         enum chown_mode change_mode = REQUEST_NONE;
2227         int result;
2228         char *path;
2229         char *filename = NULL;
2230         poptContext pc;
2231         /* numeric is set when the user wants numeric SIDs and ACEs rather
2232            than going via LSA calls to resolve them */
2233         int numeric = 0;
2234         struct cli_state *targetcli = NULL;
2235         struct cli_credentials *creds = NULL;
2236         char *targetfile = NULL;
2237         NTSTATUS status;
2238         bool ok;
2239         struct loadparm_context *lp_ctx = NULL;
2240
2241         struct poptOption long_options[] = {
2242                 POPT_AUTOHELP
2243                 {
2244                         .longName   = "delete",
2245                         .shortName  = 'D',
2246                         .argInfo    = POPT_ARG_STRING,
2247                         .arg        = NULL,
2248                         .val        = 'D',
2249                         .descrip    = "Delete an acl",
2250                         .argDescrip = "ACL",
2251                 },
2252                 {
2253                         .longName   = "modify",
2254                         .shortName  = 'M',
2255                         .argInfo    = POPT_ARG_STRING,
2256                         .arg        = NULL,
2257                         .val        = 'M',
2258                         .descrip    = "Modify an acl",
2259                         .argDescrip = "ACL",
2260                 },
2261                 {
2262                         .longName   = "add",
2263                         .shortName  = 'a',
2264                         .argInfo    = POPT_ARG_STRING,
2265                         .arg        = NULL,
2266                         .val        = 'a',
2267                         .descrip    = "Add an acl",
2268                         .argDescrip = "ACL",
2269                 },
2270                 {
2271                         .longName   = "set",
2272                         .shortName  = 'S',
2273                         .argInfo    = POPT_ARG_STRING,
2274                         .arg        = NULL,
2275                         .val        = 'S',
2276                         .descrip    = "Set acls",
2277                         .argDescrip = "ACLS",
2278                 },
2279                 {
2280                         .longName   = "chown",
2281                         .shortName  = 'C',
2282                         .argInfo    = POPT_ARG_STRING,
2283                         .arg        = NULL,
2284                         .val        = 'C',
2285                         .descrip    = "Change ownership of a file",
2286                         .argDescrip = "USERNAME",
2287                 },
2288                 {
2289                         .longName   = "chgrp",
2290                         .shortName  = 'G',
2291                         .argInfo    = POPT_ARG_STRING,
2292                         .arg        = NULL,
2293                         .val        = 'G',
2294                         .descrip    = "Change group ownership of a file",
2295                         .argDescrip = "GROUPNAME",
2296                 },
2297                 {
2298                         .longName   = "inherit",
2299                         .shortName  = 'I',
2300                         .argInfo    = POPT_ARG_STRING,
2301                         .arg        = NULL,
2302                         .val        = 'I',
2303                         .descrip    = "Inherit allow|remove|copy",
2304                 },
2305                 {
2306                         .longName   = "propagate-inheritance",
2307                         .shortName  = 0,
2308                         .argInfo    = POPT_ARG_NONE,
2309                         .arg        = &inheritance,
2310                         .val        = 1,
2311                         .descrip    = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
2312                 },
2313                 {
2314                         .longName   = "save",
2315                         .shortName  = 0,
2316                         .argInfo    = POPT_ARG_STRING,
2317                         .arg        = &save_file,
2318                         .val        = 1,
2319                         .descrip    = "stores the DACLs in sddl format of the "
2320                                       "specified file or folder for later use "
2321                                       "with restore. SACLS, owner or integrity"
2322                                       " labels are not stored",
2323                 },
2324                 {
2325                         .longName   = "restore",
2326                         .shortName  = 0,
2327                         .argInfo    = POPT_ARG_STRING,
2328                         .arg        = &restore_file,
2329                         .val        = 1,
2330                         .descrip    = "applies the stored DACLS to files in "
2331                                       "directory.",
2332                 },
2333                 {
2334                         .longName   = "recurse",
2335                         .shortName  = 0,
2336                         .argInfo    = POPT_ARG_NONE,
2337                         .arg        = &recurse,
2338                         .val        = 1,
2339                         .descrip    = "indicates the operation is performed "
2340                                       "on directory and all files/directories"
2341                                       " below. (only applies to save option)",
2342                 },
2343                 {
2344                         .longName   = "numeric",
2345                         .shortName  = 0,
2346                         .argInfo    = POPT_ARG_NONE,
2347                         .arg        = &numeric,
2348                         .val        = 1,
2349                         .descrip    = "Don't resolve sids or masks to names",
2350                 },
2351                 {
2352                         .longName   = "sddl",
2353                         .shortName  = 0,
2354                         .argInfo    = POPT_ARG_NONE,
2355                         .arg        = &sddl,
2356                         .val        = 1,
2357                         .descrip    = "Output and input acls in sddl format",
2358                 },
2359                 {
2360                         .longName   = "query-security-info",
2361                         .shortName  = 0,
2362                         .argInfo    = POPT_ARG_INT,
2363                         .arg        = &query_sec_info,
2364                         .val        = 1,
2365                         .descrip    = "The security-info flags for queries"
2366                 },
2367                 {
2368                         .longName   = "set-security-info",
2369                         .shortName  = 0,
2370                         .argInfo    = POPT_ARG_INT,
2371                         .arg        = &set_sec_info,
2372                         .val        = 1,
2373                         .descrip    = "The security-info flags for modifications"
2374                 },
2375                 {
2376                         .longName   = "test-args",
2377                         .shortName  = 't',
2378                         .argInfo    = POPT_ARG_NONE,
2379                         .arg        = &test_args,
2380                         .val        = 1,
2381                         .descrip    = "Test arguments"
2382                 },
2383                 {
2384                         .longName   = "domain-sid",
2385                         .shortName  = 0,
2386                         .argInfo    = POPT_ARG_STRING,
2387                         .arg        = &domain_sid,
2388                         .val        = 0,
2389                         .descrip    = "Domain SID for sddl",
2390                         .argDescrip = "SID"},
2391                 {
2392                         .longName   = "maximum-access",
2393                         .shortName  = 'x',
2394                         .argInfo    = POPT_ARG_NONE,
2395                         .arg        = NULL,
2396                         .val        = 'x',
2397                         .descrip    = "Query maximum permissions",
2398                 },
2399                 POPT_COMMON_SAMBA
2400                 POPT_COMMON_CONNECTION
2401                 POPT_COMMON_CREDENTIALS
2402                 POPT_LEGACY_S3
2403                 POPT_COMMON_VERSION
2404                 POPT_TABLEEND
2405         };
2406
2407         struct cli_state *cli;
2408         TALLOC_CTX *frame = talloc_stackframe();
2409         const char *owner_username = "";
2410         char *server;
2411
2412         smb_init_locale();
2413
2414         ok = samba_cmdline_init(frame,
2415                                 SAMBA_CMDLINE_CONFIG_CLIENT,
2416                                 false /* require_smbconf */);
2417         if (!ok) {
2418                 DBG_ERR("Failed to init cmdline parser!\n");
2419                 TALLOC_FREE(frame);
2420                 exit(1);
2421         }
2422         lp_ctx = samba_cmdline_get_lp_ctx();
2423         /* set default debug level to 1 regardless of what smb.conf sets */
2424         lpcfg_set_cmdline(lp_ctx, "log level", "1");
2425
2426         setlinebuf(stdout);
2427
2428         pc = samba_popt_get_context(getprogname(),
2429                                     argc,
2430                                     argv_const,
2431                                     long_options,
2432                                     0);
2433         if (pc == NULL) {
2434                 DBG_ERR("Failed to setup popt context!\n");
2435                 TALLOC_FREE(frame);
2436                 exit(1);
2437         }
2438
2439         poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
2440                 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
2441
2442         while ((opt = poptGetNextOpt(pc)) != -1) {
2443                 switch (opt) {
2444                 case 'S':
2445                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2446                         mode = SMB_ACL_SET;
2447                         break;
2448
2449                 case 'D':
2450                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2451                         mode = SMB_ACL_DELETE;
2452                         break;
2453
2454                 case 'M':
2455                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2456                         mode = SMB_ACL_MODIFY;
2457                         break;
2458
2459                 case 'a':
2460                         the_acl = smb_xstrdup(poptGetOptArg(pc));
2461                         mode = SMB_ACL_ADD;
2462                         break;
2463
2464                 case 'C':
2465                         owner_username = poptGetOptArg(pc);
2466                         change_mode = REQUEST_CHOWN;
2467                         break;
2468
2469                 case 'G':
2470                         owner_username = poptGetOptArg(pc);
2471                         change_mode = REQUEST_CHGRP;
2472                         break;
2473
2474                 case 'I':
2475                         owner_username = poptGetOptArg(pc);
2476                         change_mode = REQUEST_INHERIT;
2477                         break;
2478                 case 'm':
2479                         lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
2480                         break;
2481                 case 'x':
2482                         want_mxac = true;
2483                         break;
2484                 case POPT_ERROR_BADOPT:
2485                         fprintf(stderr, "\nInvalid option %s: %s\n\n",
2486                                 poptBadOption(pc, 0), poptStrerror(opt));
2487                         poptPrintUsage(pc, stderr, 0);
2488                         exit(1);
2489                 }
2490         }
2491         if (inheritance && !the_acl) {
2492                 poptPrintUsage(pc, stderr, 0);
2493                 return -1;
2494         }
2495
2496         if(!poptPeekArg(pc)) {
2497                 poptPrintUsage(pc, stderr, 0);
2498                 return -1;
2499         }
2500
2501         path = talloc_strdup(frame, poptGetArg(pc));
2502         if (!path) {
2503                 return -1;
2504         }
2505
2506         if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
2507                 printf("Invalid argument: %s\n", path);
2508                 return -1;
2509         }
2510
2511         if(!poptPeekArg(pc)) {
2512                 poptPrintUsage(pc, stderr, 0);
2513                 return -1;
2514         }
2515
2516         filename = talloc_strdup(frame, poptGetArg(pc));
2517         if (!filename) {
2518                 return -1;
2519         }
2520
2521         poptFreeContext(pc);
2522         samba_cmdline_burn(argc, argv);
2523
2524         string_replace(path,'/','\\');
2525
2526         server = talloc_strdup(frame, path+2);
2527         if (!server) {
2528                 return -1;
2529         }
2530         share = strchr_m(server,'\\');
2531         if (share == NULL) {
2532                 printf("Invalid argument\n");
2533                 return -1;
2534         }
2535
2536         *share = 0;
2537         share++;
2538
2539         creds = samba_cmdline_get_creds();
2540
2541         /* Make connection to server */
2542         if (!test_args) {
2543                 cli = connect_one(creds, server, share);
2544                 if (!cli) {
2545                         exit(EXIT_FAILED);
2546                 }
2547         } else {
2548                 exit(0);
2549         }
2550
2551         string_replace(filename, '/', '\\');
2552         if (filename[0] != '\\') {
2553                 filename = talloc_asprintf(frame,
2554                                 "\\%s",
2555                                 filename);
2556                 if (!filename) {
2557                         return -1;
2558                 }
2559         }
2560
2561         status = cli_resolve_path(frame,
2562                                   "",
2563                                   creds,
2564                                   cli,
2565                                   filename,
2566                                   &targetcli,
2567                                   &targetfile);
2568         if (!NT_STATUS_IS_OK(status)) {
2569                 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
2570                 return -1;
2571         }
2572
2573         /* Perform requested action */
2574
2575         if (change_mode == REQUEST_INHERIT) {
2576                 result = inherit(targetcli, targetfile, owner_username);
2577         } else if (change_mode != REQUEST_NONE) {
2578                 result = owner_set(targetcli, change_mode, targetfile, owner_username);
2579         } else if (the_acl) {
2580                 if (inheritance) {
2581                         struct cacl_callback_state cbstate = {
2582                                 .creds = creds,
2583                                 .cli = targetcli,
2584                                 .mode = mode,
2585                                 .the_acl = the_acl,
2586                                 .numeric = numeric,
2587                         };
2588                         result = inheritance_cacl_set(targetfile, &cbstate);
2589                 } else {
2590                         result =  cacl_set(targetcli,
2591                                            targetfile,
2592                                            the_acl,
2593                                            mode,
2594                                            numeric);
2595                 }
2596         } else {
2597                 if (save_file || restore_file) {
2598                         sddl = 1;
2599                         if (save_file) {
2600                                 result = cacl_dump_dacl(cli, creds, filename);
2601                         } else {
2602                                 result = cacl_restore(targetcli,
2603                                                       creds,
2604                                                       numeric, restore_file);
2605                         }
2606                 } else {
2607                         result = cacl_dump(targetcli, targetfile, numeric);
2608                 }
2609         }
2610
2611         gfree_all();
2612         TALLOC_FREE(frame);
2613
2614         return result;
2615 }