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