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