s3:utils: Remove trailing spaces in smbcacls; no changes
[samba.git] / source3 / utils / smbcacls.c
1 /*
2    Unix SMB/CIFS implementation.
3    ACL get/set utility
4
5    Copyright (C) Andrew Tridgell 2000
6    Copyright (C) Tim Potter      2000
7    Copyright (C) Jeremy Allison  2000
8    Copyright (C) Jelmer Vernooij 2003
9    Copyright (C) Noel Power <noel.power@suse.com> 2013
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "lib/cmdline/cmdline.h"
27 #include "rpc_client/cli_pipe.h"
28 #include "../librpc/gen_ndr/ndr_lsa.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../libcli/security/security.h"
31 #include "libsmb/libsmb.h"
32 #include "libsmb/clirap.h"
33 #include "passdb/machine_sid.h"
34 #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 #include "util_sd.h"
36 #include "lib/param/param.h"
37
38 static char DIRSEP_CHAR = '\\';
39
40 static int inheritance = 0;
41 static int test_args;
42 static int sddl;
43 static int query_sec_info = -1;
44 static int set_sec_info = -1;
45 static bool want_mxac;
46
47 static const char *domain_sid = NULL;
48
49 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
50 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
51 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
52
53 struct cacl_callback_state {
54         struct cli_credentials *creds;
55         struct cli_state *cli;
56         struct security_descriptor *aclsd;
57         struct security_acl *acl_to_add;
58         enum acl_mode mode;
59         char *the_acl;
60         bool acl_no_propagate;
61         bool numeric;
62 };
63
64 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
65                                           struct dom_sid *sid)
66 {
67         union lsa_PolicyInformation *info = NULL;
68         struct smbXcli_tcon *orig_tcon = NULL;
69         char *orig_share = NULL;
70         struct rpc_pipe_client *rpc_pipe = NULL;
71         struct policy_handle handle;
72         NTSTATUS status, result;
73         TALLOC_CTX *frame = talloc_stackframe();
74
75         if (cli_state_has_tcon(cli)) {
76                 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
77         }
78
79         status = cli_tree_connect(cli, "IPC$", "?????", NULL);
80         if (!NT_STATUS_IS_OK(status)) {
81                 goto done;
82         }
83
84         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
85         if (!NT_STATUS_IS_OK(status)) {
86                 goto tdis;
87         }
88
89         status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
90                                         GENERIC_EXECUTE_ACCESS, &handle);
91         if (!NT_STATUS_IS_OK(status)) {
92                 goto tdis;
93         }
94
95         status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
96                                              frame, &handle,
97                                              LSA_POLICY_INFO_DOMAIN,
98                                              &info, &result);
99
100         if (any_nt_status_not_ok(status, result, &status)) {
101                 goto tdis;
102         }
103
104         *sid = *info->domain.sid;
105
106 tdis:
107         TALLOC_FREE(rpc_pipe);
108         cli_tdis(cli);
109 done:
110         cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
111         TALLOC_FREE(frame);
112         return status;
113 }
114
115 static struct dom_sid *get_domain_sid(struct cli_state *cli)
116 {
117         NTSTATUS status;
118         struct dom_sid_buf buf;
119
120         struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
121         if (sid == NULL) {
122                 DEBUG(0, ("Out of memory\n"));
123                 return NULL;
124         }
125
126         if (domain_sid) {
127                 if (!dom_sid_parse(domain_sid, sid)) {
128                         DEBUG(0,("failed to parse domain sid\n"));
129                         TALLOC_FREE(sid);
130                 }
131         } else {
132                 status = cli_lsa_lookup_domain_sid(cli, sid);
133
134                 if (!NT_STATUS_IS_OK(status)) {
135                         DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
136                         TALLOC_FREE(sid);
137                 }
138
139         }
140
141         DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
142         return sid;
143 }
144
145 /* add an ACE to a list of ACEs in a struct security_acl */
146 static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
147                              const struct security_ace *ace)
148
149 {
150         struct security_acl *acl = *the_acl;
151
152         if (acl == NULL) {
153                 acl = make_sec_acl(ctx, 3, 0, NULL);
154                 if (acl == NULL) {
155                         return false;
156                 }
157         }
158
159         if (acl->num_aces == UINT32_MAX) {
160                 return false;
161         }
162         ADD_TO_ARRAY(
163                 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
164         *the_acl = acl;
165         return True;
166 }
167
168 static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
169 {
170         return add_ace_with_ctx(talloc_tos(), the_acl, ace);
171 }
172
173 /* parse a ascii version of a security descriptor */
174 static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
175 {
176         const char *p = str;
177         char *tok;
178         struct security_descriptor *ret = NULL;
179         size_t sd_size;
180         struct dom_sid owner_sid = { .num_auths = 0 };
181         bool have_owner = false;
182         struct dom_sid group_sid = { .num_auths = 0 };
183         bool have_group = false;
184         struct security_acl *dacl=NULL;
185         int revision=1;
186
187         while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
188                 if (strncmp(tok,"REVISION:", 9) == 0) {
189                         revision = strtol(tok+9, NULL, 16);
190                         continue;
191                 }
192
193                 if (strncmp(tok,"OWNER:", 6) == 0) {
194                         if (have_owner) {
195                                 printf("Only specify owner once\n");
196                                 goto done;
197                         }
198                         if (!StringToSid(cli, &owner_sid, tok+6)) {
199                                 printf("Failed to parse owner sid\n");
200                                 goto done;
201                         }
202                         have_owner = true;
203                         continue;
204                 }
205
206                 if (strncmp(tok,"GROUP:", 6) == 0) {
207                         if (have_group) {
208                                 printf("Only specify group once\n");
209                                 goto done;
210                         }
211                         if (!StringToSid(cli, &group_sid, tok+6)) {
212                                 printf("Failed to parse group sid\n");
213                                 goto done;
214                         }
215                         have_group = true;
216                         continue;
217                 }
218
219                 if (strncmp(tok,"ACL:", 4) == 0) {
220                         struct security_ace ace;
221                         if (!parse_ace(cli, &ace, tok+4)) {
222                                 goto done;
223                         }
224                         if(!add_ace(&dacl, &ace)) {
225                                 printf("Failed to add ACL %s\n", tok);
226                                 goto done;
227                         }
228                         continue;
229                 }
230
231                 printf("Failed to parse token '%s' in security descriptor,\n", tok);
232                 goto done;
233         }
234
235         ret = make_sec_desc(
236                 ctx,
237                 revision,
238                 SEC_DESC_SELF_RELATIVE,
239                 have_owner ? &owner_sid : NULL,
240                 have_group ? &group_sid : NULL,
241                 NULL,
242                 dacl,
243                 &sd_size);
244
245 done:
246         return ret;
247 }
248
249 /*****************************************************
250 get fileinfo for filename
251 *******************************************************/
252 static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
253 {
254         uint16_t fnum = (uint16_t)-1;
255         NTSTATUS status;
256         struct smb_create_returns cr = {0};
257
258         /* The desired access below is the only one I could find that works
259            with NT4, W2KP and Samba */
260
261         status = cli_ntcreate(
262                 cli,                    /* cli */
263                 filename,               /* fname */
264                 0,                      /* CreatFlags */
265                 READ_CONTROL_ACCESS,    /* CreatFlags */
266                 0,                      /* FileAttributes */
267                 FILE_SHARE_READ|
268                 FILE_SHARE_WRITE,       /* ShareAccess */
269                 FILE_OPEN,              /* CreateDisposition */
270                 0x0,                    /* CreateOptions */
271                 0x0,                    /* SecurityFlags */
272                 &fnum,                  /* pfid */
273                 &cr);                   /* cr */
274         if (!NT_STATUS_IS_OK(status)) {
275                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
276                 return 0;
277         }
278
279         cli_close(cli, fnum);
280         return cr.file_attributes;
281 }
282
283 /*****************************************************
284 get sec desc for filename
285 *******************************************************/
286 static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
287                                                         struct cli_state *cli,
288                                                         const char *filename)
289 {
290         uint16_t fnum = (uint16_t)-1;
291         struct security_descriptor *sd;
292         NTSTATUS status;
293         uint32_t sec_info;
294         uint32_t desired_access = 0;
295
296         if (query_sec_info == -1) {
297                 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
298         } else {
299                 sec_info = query_sec_info;
300         }
301
302         if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
303                 desired_access |= SEC_STD_READ_CONTROL;
304         }
305         if (sec_info & SECINFO_SACL) {
306                 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
307         }
308
309         if (desired_access == 0) {
310                 desired_access |= SEC_STD_READ_CONTROL;
311         }
312
313         status = cli_ntcreate(cli, filename, 0, desired_access,
314                               0, FILE_SHARE_READ|FILE_SHARE_WRITE,
315                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
316         if (!NT_STATUS_IS_OK(status)) {
317                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
318                 return NULL;
319         }
320
321         status = cli_query_security_descriptor(cli, fnum, sec_info,
322                                                ctx, &sd);
323
324         cli_close(cli, fnum);
325
326         if (!NT_STATUS_IS_OK(status)) {
327                 printf("Failed to get security descriptor: %s\n",
328                        nt_errstr(status));
329                 return NULL;
330         }
331         return sd;
332 }
333
334 static struct security_descriptor *get_secdesc(struct cli_state *cli,
335                                                const char *filename)
336 {
337         return get_secdesc_with_ctx(talloc_tos(), cli, filename);
338 }
339 /*****************************************************
340 set sec desc for filename
341 *******************************************************/
342 static bool set_secdesc(struct cli_state *cli, const char *filename,
343                         struct security_descriptor *sd)
344 {
345         uint16_t fnum = (uint16_t)-1;
346         bool result=true;
347         NTSTATUS status;
348         uint32_t desired_access = 0;
349         uint32_t sec_info;
350
351         if (set_sec_info == -1) {
352                 sec_info = 0;
353
354                 if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
355                         sec_info |= SECINFO_DACL;
356                 }
357                 if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
358                         sec_info |= SECINFO_SACL;
359                 }
360                 if (sd->owner_sid) {
361                         sec_info |= SECINFO_OWNER;
362                 }
363                 if (sd->group_sid) {
364                         sec_info |= SECINFO_GROUP;
365                 }
366         } else {
367                 sec_info = set_sec_info;
368         }
369
370         /* Make the desired_access more specific. */
371         if (sec_info & SECINFO_DACL) {
372                 desired_access |= SEC_STD_WRITE_DAC;
373         }
374         if (sec_info & SECINFO_SACL) {
375                 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
376         }
377         if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
378                 desired_access |= SEC_STD_WRITE_OWNER;
379         }
380
381         status = cli_ntcreate(cli, filename, 0,
382                               desired_access,
383                               0, FILE_SHARE_READ|FILE_SHARE_WRITE,
384                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
385         if (!NT_STATUS_IS_OK(status)) {
386                 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
387                 return false;
388         }
389
390         status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
391         if (!NT_STATUS_IS_OK(status)) {
392                 printf("ERROR: security descriptor set failed: %s\n",
393                        nt_errstr(status));
394                 result=false;
395         }
396
397         cli_close(cli, fnum);
398         return result;
399 }
400
401 /*****************************************************
402 get maximum access for a file
403 *******************************************************/
404 static int cacl_mxac(struct cli_state *cli, const char *filename)
405 {
406         NTSTATUS status;
407         uint32_t mxac;
408
409         status = cli_query_mxac(cli, filename, &mxac);
410         if (!NT_STATUS_IS_OK(status)) {
411                 printf("Failed to get mxac: %s\n", nt_errstr(status));
412                 return EXIT_FAILED;
413         }
414
415         printf("Maximum access: 0x%x\n", mxac);
416
417         return EXIT_OK;
418 }
419
420
421 /*****************************************************
422 dump the acls for a file
423 *******************************************************/
424 static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
425 {
426         struct security_descriptor *sd;
427         int ret;
428
429         if (test_args) {
430                 return EXIT_OK;
431         }
432
433         sd = get_secdesc(cli, filename);
434         if (sd == NULL) {
435                 return EXIT_FAILED;
436         }
437
438         if (sddl) {
439                 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
440                 if (str == NULL) {
441                         return EXIT_FAILED;
442                 }
443                 printf("%s\n", str);
444                 TALLOC_FREE(str);
445         } else {
446                 sec_desc_print(cli, stdout, sd, numeric);
447         }
448
449         if (want_mxac) {
450                 ret = cacl_mxac(cli, filename);
451                 if (ret != EXIT_OK) {
452                         return ret;
453                 }
454         }
455
456         return EXIT_OK;
457 }
458
459 /*****************************************************
460 Change the ownership or group ownership of a file. Just
461 because the NT docs say this can't be done :-). JRA.
462 *******************************************************/
463
464 static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
465                         const char *filename, const char *new_username)
466 {
467         struct dom_sid sid;
468         struct security_descriptor *sd;
469         size_t sd_size;
470
471         if (!StringToSid(cli, &sid, new_username))
472                 return EXIT_PARSE_ERROR;
473
474         sd = make_sec_desc(talloc_tos(),
475                            SECURITY_DESCRIPTOR_REVISION_1,
476                            SEC_DESC_SELF_RELATIVE,
477                            (change_mode == REQUEST_CHOWN) ? &sid : NULL,
478                            (change_mode == REQUEST_CHGRP) ? &sid : NULL,
479                            NULL, NULL, &sd_size);
480
481         if (!set_secdesc(cli, filename, sd)) {
482                 return EXIT_FAILED;
483         }
484
485         return EXIT_OK;
486 }
487
488
489 /* The MSDN is contradictory over the ordering of ACE entries in an
490    ACL.  However NT4 gives a "The information may have been modified
491    by a computer running Windows NT 5.0" if denied ACEs do not appear
492    before allowed ACEs. At
493    http://technet.microsoft.com/en-us/library/cc781716.aspx the
494    canonical order is specified as "Explicit Deny, Explicit Allow,
495    Inherited ACEs unchanged" */
496
497 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
498 {
499         if (security_ace_equal(ace1, ace2))
500                 return 0;
501
502         if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
503                         !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
504                 return 1;
505         if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
506                         (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
507                 return -1;
508         if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
509                         (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
510                 return ace1 - ace2;
511
512         if (ace1->type != ace2->type)
513                 return ace2->type - ace1->type;
514
515         if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
516                 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
517
518         if (ace1->flags != ace2->flags)
519                 return ace1->flags - ace2->flags;
520
521         if (ace1->access_mask != ace2->access_mask)
522                 return ace1->access_mask - ace2->access_mask;
523
524         if (ace1->size != ace2->size)
525                 return ace1->size - ace2->size;
526
527         return memcmp(ace1, ace2, sizeof(struct security_ace));
528 }
529
530 static void sort_acl(struct security_acl *the_acl)
531 {
532         uint32_t i;
533         if (!the_acl) return;
534
535         TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
536
537         for (i=1;i<the_acl->num_aces;) {
538                 if (security_ace_equal(&the_acl->aces[i-1],
539                                        &the_acl->aces[i])) {
540                         ARRAY_DEL_ELEMENT(
541                                 the_acl->aces, i, the_acl->num_aces);
542                         the_acl->num_aces--;
543                 } else {
544                         i++;
545                 }
546         }
547 }
548
549 /*****************************************************
550 set the ACLs on a file given a security descriptor
551 *******************************************************/
552
553 static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
554                             struct security_descriptor *sd, enum acl_mode mode,
555                             bool numeric)
556 {
557         struct security_descriptor *old = NULL;
558         uint32_t i, j;
559         size_t sd_size;
560         int result = EXIT_OK;
561
562         if (!sd) return EXIT_PARSE_ERROR;
563         if (test_args) return EXIT_OK;
564
565         if (mode != SMB_ACL_SET) {
566                 /*
567                  * Do not fetch old ACL when it will be overwritten
568                  * completely with a new one.
569                  */
570                 old = get_secdesc(cli, filename);
571
572                 if (!old) {
573                         return EXIT_FAILED;
574                 }
575         }
576
577         /* the logic here is rather more complex than I would like */
578         switch (mode) {
579         case SMB_ACL_DELETE:
580                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
581                         bool found = False;
582
583                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
584                                 if (security_ace_equal(&sd->dacl->aces[i],
585                                                        &old->dacl->aces[j])) {
586                                         uint32_t k;
587                                         for (k=j; k<old->dacl->num_aces-1;k++) {
588                                                 old->dacl->aces[k] = old->dacl->aces[k+1];
589                                         }
590                                         old->dacl->num_aces--;
591                                         found = True;
592                                         break;
593                                 }
594                         }
595
596                         if (!found) {
597                                 printf("ACL for ACE:");
598                                 print_ace(cli, stdout, &sd->dacl->aces[i],
599                                           numeric);
600                                 printf(" not found\n");
601                         }
602                 }
603                 break;
604
605         case SMB_ACL_MODIFY:
606                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
607                         bool found = False;
608
609                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
610                                 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
611                                               &old->dacl->aces[j].trustee)) {
612                                         old->dacl->aces[j] = sd->dacl->aces[i];
613                                         found = True;
614                                 }
615                         }
616
617                         if (!found) {
618                                 fstring str;
619
620                                 SidToString(cli, str,
621                                             &sd->dacl->aces[i].trustee,
622                                             numeric);
623                                 printf("ACL for SID %s not found\n", str);
624                         }
625                 }
626
627                 if (sd->owner_sid) {
628                         old->owner_sid = sd->owner_sid;
629                 }
630
631                 if (sd->group_sid) {
632                         old->group_sid = sd->group_sid;
633                 }
634
635                 break;
636
637         case SMB_ACL_ADD:
638                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
639                         add_ace(&old->dacl, &sd->dacl->aces[i]);
640                 }
641                 break;
642
643         case SMB_ACL_SET:
644                 old = sd;
645                 break;
646         }
647
648         /* Denied ACE entries must come before allowed ones */
649         sort_acl(old->dacl);
650
651         /* Create new security descriptor and set it */
652
653         /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
654            But if we're sending an owner, even if it's the same as the one
655            that already exists then W2K3 insists we open with WRITE_OWNER access.
656            I need to check that setting a SD with no owner set works against WNT
657            and W2K. JRA.
658         */
659
660         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
661                            old->owner_sid, old->group_sid,
662                            NULL, old->dacl, &sd_size);
663
664         if (!set_secdesc(cli, filename, sd)) {
665                 result = EXIT_FAILED;
666         }
667
668         return result;
669 }
670
671 /*****************************************************
672 set the ACLs on a file given an ascii description
673 *******************************************************/
674
675 static int cacl_set(struct cli_state *cli, const char *filename,
676                     char *the_acl, enum acl_mode mode, bool numeric)
677 {
678         struct security_descriptor *sd = NULL;
679
680         if (sddl) {
681                 sd = sddl_decode(talloc_tos(), the_acl, get_global_sam_sid());
682         } else {
683                 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
684         }
685
686         if (sd == NULL) {
687                 return EXIT_PARSE_ERROR;
688         }
689         if (test_args) {
690                 return EXIT_OK;
691         }
692         return cacl_set_from_sd(cli, filename, sd, mode, numeric);
693 }
694
695 /*****************************************************
696 set the inherit on a file
697 *******************************************************/
698 static int inherit(struct cli_state *cli, const char *filename,
699                    const char *type)
700 {
701         struct security_descriptor *old,*sd;
702         uint32_t oldattr;
703         size_t sd_size;
704         int result = EXIT_OK;
705
706         old = get_secdesc(cli, filename);
707
708         if (!old) {
709                 return EXIT_FAILED;
710         }
711
712         oldattr = get_fileinfo(cli,filename);
713
714         if (strcmp(type,"allow")==0) {
715                 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
716                     SEC_DESC_DACL_PROTECTED) {
717                         uint32_t i;
718                         char *parentname,*temp;
719                         struct security_descriptor *parent;
720                         temp = talloc_strdup(talloc_tos(), filename);
721
722                         old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
723
724                         /* look at parent and copy in all its inheritable ACL's. */
725                         string_replace(temp, '\\', '/');
726                         if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
727                                 return EXIT_FAILED;
728                         }
729                         string_replace(parentname, '/', '\\');
730                         parent = get_secdesc(cli,parentname);
731                         if (parent == NULL) {
732                                 return EXIT_FAILED;
733                         }
734                         for (i=0;i<parent->dacl->num_aces;i++) {
735                                 struct security_ace *ace=&parent->dacl->aces[i];
736                                 /* Add inherited flag to all aces */
737                                 ace->flags=ace->flags|
738                                            SEC_ACE_FLAG_INHERITED_ACE;
739                                 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
740                                         if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
741                                             SEC_ACE_FLAG_CONTAINER_INHERIT) {
742                                                 add_ace(&old->dacl, ace);
743                                         }
744                                 } else {
745                                         if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
746                                             SEC_ACE_FLAG_OBJECT_INHERIT) {
747                                                 /* clear flags for files */
748                                                 ace->flags=0;
749                                                 add_ace(&old->dacl, ace);
750                                         }
751                                 }
752                         }
753                 } else {
754                         printf("Already set to inheritable permissions.\n");
755                         return EXIT_FAILED;
756                 }
757         } else if (strcmp(type,"remove")==0) {
758                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
759                     SEC_DESC_DACL_PROTECTED) {
760                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
761
762                         /* remove all inherited ACL's. */
763                         if (old->dacl) {
764                                 int i;
765                                 struct security_acl *temp=old->dacl;
766                                 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
767                                 for (i=temp->num_aces-1;i>=0;i--) {
768                                         struct security_ace *ace=&temp->aces[i];
769                                         /* Remove all ace with INHERITED flag set */
770                                         if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
771                                             SEC_ACE_FLAG_INHERITED_ACE) {
772                                                 add_ace(&old->dacl,ace);
773                                         }
774                                 }
775                         }
776                 } else {
777                         printf("Already set to no inheritable permissions.\n");
778                         return EXIT_FAILED;
779                 }
780         } else if (strcmp(type,"copy")==0) {
781                 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
782                     SEC_DESC_DACL_PROTECTED) {
783                         old->type=old->type | SEC_DESC_DACL_PROTECTED;
784
785                         /*
786                          * convert all inherited ACL's to non
787                          * inherited ACL's.
788                          */
789                         if (old->dacl) {
790                                 uint32_t i;
791                                 for (i=0;i<old->dacl->num_aces;i++) {
792                                         struct security_ace *ace=&old->dacl->aces[i];
793                                         /* Remove INHERITED FLAG from all aces */
794                                         ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
795                                 }
796                         }
797                 } else {
798                         printf("Already set to no inheritable permissions.\n");
799                         return EXIT_FAILED;
800                 }
801         }
802
803         /* Denied ACE entries must come before allowed ones */
804         sort_acl(old->dacl);
805
806         sd = make_sec_desc(talloc_tos(),old->revision, old->type,
807                            old->owner_sid, old->group_sid,
808                            NULL, old->dacl, &sd_size);
809
810         if (!set_secdesc(cli, filename, sd)) {
811                 result = EXIT_FAILED;
812         }
813
814         return result;
815 }
816
817 /*****************************************************
818  Return a connection to a server.
819 *******************************************************/
820 static struct cli_state *connect_one(struct cli_credentials *creds,
821                                      const char *server, const char *share)
822 {
823         struct cli_state *c = NULL;
824         NTSTATUS nt_status;
825         uint32_t flags = 0;
826
827         nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
828                                 NULL, 0,
829                                 share, "?????",
830                                 creds,
831                                 flags);
832         if (!NT_STATUS_IS_OK(nt_status)) {
833                 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
834                 return NULL;
835         }
836
837         return c;
838 }
839
840 /*
841  * Process resulting combination of mask & fname ensuring
842  * terminated with wildcard
843  */
844 static char *build_dirname(TALLOC_CTX *ctx,
845         const char *mask, char *dir, char *fname)
846 {
847         char *mask2 = NULL;
848         char *p = NULL;
849
850         mask2 = talloc_strdup(ctx, mask);
851         if (!mask2) {
852                 return NULL;
853         }
854         p = strrchr_m(mask2, DIRSEP_CHAR);
855         if (p) {
856                 p[1] = 0;
857         } else {
858                 mask2[0] = '\0';
859         }
860         mask2 = talloc_asprintf_append(mask2,
861                                 "%s\\*",
862                                 fname);
863         return mask2;
864 }
865
866 /*
867  * Returns the a copy of the ACL flags in ace modified according
868  * to some inheritance rules.
869  *   a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
870  *   b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
871  *   c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
872  *      stripped from flags to be propagated to non-container children
873  *   d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
874  *      stripped from flags to be propagated if the NP flag
875  *      SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
876  */
877
878 static uint8_t get_flags_to_propagate(bool is_container,
879                                 struct security_ace *ace)
880 {
881         uint8_t newflags = ace->flags;
882         /* OBJECT inheritance */
883         bool acl_objinherit = (ace->flags &
884                 SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
885         /* CONTAINER inheritance */
886         bool acl_cntrinherit = (ace->flags &
887                 SEC_ACE_FLAG_CONTAINER_INHERIT) ==
888                         SEC_ACE_FLAG_CONTAINER_INHERIT;
889         /* PROHIBIT inheritance */
890         bool prohibit_inheritance = ((ace->flags &
891                 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
892                         SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
893
894         /* Assume we are not propagating the ACE */
895
896         newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
897         /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
898         if (acl_cntrinherit || acl_objinherit) {
899                 /*
900                  * object inherit ( alone ) on a container needs
901                  * SEC_ACE_FLAG_INHERIT_ONLY
902                  */
903                 if (is_container) {
904                         if (acl_objinherit && !acl_cntrinherit) {
905                                 newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
906                         }
907                         /*
908                          * this is tricky, the only time we would not
909                          * propagate the ace for a container is if
910                          * prohibit_inheritance is set and object inheritance
911                          * alone is set
912                          */
913                         if ((prohibit_inheritance
914                             && acl_objinherit
915                             && !acl_cntrinherit) == false) {
916                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
917                         }
918                 } else {
919                         /*
920                          * don't apply object/container inheritance flags to
921                          * non dirs
922                          */
923                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
924                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
925                                         | SEC_ACE_FLAG_INHERIT_ONLY);
926                         /*
927                          * only apply ace to file if object inherit
928                          */
929                         if (acl_objinherit) {
930                                 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
931                         }
932                 }
933
934                 /* if NP is specified strip NP and all OI/CI INHERIT flags */
935                 if (prohibit_inheritance) {
936                         newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
937                                         | SEC_ACE_FLAG_CONTAINER_INHERIT
938                                         | SEC_ACE_FLAG_INHERIT_ONLY
939                                         | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
940                 }
941         }
942         return newflags;
943 }
944
945 /*
946  * This function builds a new acl for 'caclfile', first it removes any
947  * existing inheritable ace(s) from the current acl of caclfile, secondly it
948  * applies any inheritable acls of the parent of caclfile ( inheritable acls of
949  * caclfile's parent are passed via acl_to_add member of cbstate )
950  *
951  */
952 static NTSTATUS propagate_inherited_aces(char *caclfile,
953                         struct cacl_callback_state *cbstate)
954 {
955         TALLOC_CTX *aclctx = NULL;
956         NTSTATUS status;
957         int result;
958         int fileattr;
959         struct security_descriptor *old = NULL;
960         bool is_container = false;
961         struct security_acl *acl_to_add = cbstate->acl_to_add;
962         struct security_acl *acl_to_remove = NULL;
963         uint32_t i, j;
964
965         aclctx = talloc_new(NULL);
966         if (aclctx == NULL) {
967                 return NT_STATUS_NO_MEMORY;
968         }
969         old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
970
971         if (!old) {
972                 status = NT_STATUS_UNSUCCESSFUL;
973                 goto out;
974         }
975
976         /* inhibit propagation? */
977         if ((old->type & SEC_DESC_DACL_PROTECTED) ==
978                 SEC_DESC_DACL_PROTECTED){
979                 status = NT_STATUS_OK;
980                 goto out;
981         }
982
983         fileattr = get_fileinfo(cbstate->cli, caclfile);
984         is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
985
986         /* find acl(s) that are inherited */
987         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
988
989                 if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
990                         if (!add_ace_with_ctx(aclctx, &acl_to_remove,
991                                               &old->dacl->aces[j])) {
992                                 status = NT_STATUS_NO_MEMORY;
993                                 goto out;
994                         }
995                 }
996         }
997
998         /* remove any acl(s) that are inherited */
999         if (acl_to_remove) {
1000                 for (i = 0; i < acl_to_remove->num_aces; i++) {
1001                         struct security_ace ace = acl_to_remove->aces[i];
1002                         for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1003
1004                                 if (security_ace_equal(&ace,
1005                                                   &old->dacl->aces[j])) {
1006                                         uint32_t k;
1007                                         for (k = j; k < old->dacl->num_aces-1;
1008                                                 k++) {
1009                                                 old->dacl->aces[k] =
1010                                                         old->dacl->aces[k+1];
1011                                         }
1012                                         old->dacl->num_aces--;
1013                                         break;
1014                                 }
1015                         }
1016                 }
1017         }
1018         /* propagate any inheritable ace to be added */
1019         if (acl_to_add) {
1020                 for (i = 0; i < acl_to_add->num_aces; i++) {
1021                         struct security_ace ace = acl_to_add->aces[i];
1022                         bool is_objectinherit = (ace.flags &
1023                                 SEC_ACE_FLAG_OBJECT_INHERIT) ==
1024                                         SEC_ACE_FLAG_OBJECT_INHERIT;
1025                         bool is_inherited;
1026                         /* don't propagate flags to a file unless OI */
1027                         if (!is_objectinherit && !is_container) {
1028                                 continue;
1029                         }
1030                         /*
1031                          * adjust flags according to inheritance
1032                          * rules
1033                          */
1034                         ace.flags = get_flags_to_propagate(is_container, &ace);
1035                         is_inherited = (ace.flags &
1036                                 SEC_ACE_FLAG_INHERITED_ACE) ==
1037                                         SEC_ACE_FLAG_INHERITED_ACE;
1038                         /* don't propagate non inherited flags */
1039                         if (!is_inherited) {
1040                                 continue;
1041                         }
1042                         if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1043                                 status = NT_STATUS_NO_MEMORY;
1044                                 goto out;
1045                         }
1046                 }
1047         }
1048
1049         result = cacl_set_from_sd(cbstate->cli, caclfile,
1050                                   old,
1051                                   SMB_ACL_SET, cbstate->numeric);
1052         if (result != EXIT_OK) {
1053                 status = NT_STATUS_UNSUCCESSFUL;
1054                 goto out;
1055         }
1056
1057         status = NT_STATUS_OK;
1058 out:
1059         TALLOC_FREE(aclctx);
1060         return status;
1061 }
1062
1063 /*
1064  * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1065  * SEC_ACE_FLAG_CONTAINER_INHERIT
1066  */
1067 static bool is_inheritable_ace(struct security_ace *ace)
1068 {
1069         uint8_t flags = ace->flags;
1070         if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1071                         | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1072                 return true;
1073         }
1074         return false;
1075 }
1076
1077 /* This method does some basic sanity checking with respect to automatic
1078  * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1079  * attempts to set inherited permissions directly. Additionally this method
1080  * does some basic initialisation for instance it parses the ACL passed on the
1081  * command line.
1082  */
1083 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1084                         struct cacl_callback_state *cbstate)
1085 {
1086         NTSTATUS result;
1087         char *the_acl = cbstate->the_acl;
1088         struct cli_state *cli = cbstate->cli;
1089         enum acl_mode mode = cbstate->mode;
1090         struct security_descriptor *sd = NULL;
1091         struct security_descriptor *old = NULL;
1092         uint32_t j;
1093         bool propagate = false;
1094
1095         old = get_secdesc_with_ctx(ctx, cli, filename);
1096         if (old == NULL) {
1097                 return NT_STATUS_NO_MEMORY;
1098         }
1099
1100         /* parse acl passed on the command line */
1101         if (sddl) {
1102                 cbstate->aclsd = sddl_decode(ctx, the_acl,
1103                                              get_global_sam_sid());
1104         } else {
1105                 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1106         }
1107
1108         if (!cbstate->aclsd) {
1109                 result = NT_STATUS_UNSUCCESSFUL;
1110                 goto out;
1111         }
1112
1113         sd = cbstate->aclsd;
1114
1115         /* set operation if inheritance is enabled doesn't make sense */
1116         if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1117                 SEC_DESC_DACL_PROTECTED)){
1118                 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1119                 result = NT_STATUS_UNSUCCESSFUL;
1120                 goto out;
1121
1122         }
1123
1124         /*
1125          * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1126          * flags that are set
1127          */
1128         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1129                 struct security_ace *ace = &sd->dacl->aces[j];
1130                 if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1131                         d_printf("Illegal parameter %s\n", the_acl);
1132                         result = NT_STATUS_UNSUCCESSFUL;
1133                         goto out;
1134                 }
1135                 if (!propagate) {
1136                         if (is_inheritable_ace(ace)) {
1137                                 propagate = true;
1138                         }
1139                 }
1140         }
1141
1142         result = NT_STATUS_OK;
1143 out:
1144         cbstate->acl_no_propagate = !propagate;
1145         return result;
1146 }
1147
1148 /*
1149  * This method builds inheritable ace(s) from filename (which should be
1150  * a container) that need propagating to children in order to provide
1151  * automatic inheritance. Those inheritable ace(s) are stored in
1152  * acl_to_add member of cbstate for later processing
1153  * (see propagate_inherited_aces)
1154  */
1155 static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1156                         struct cacl_callback_state *cbstate)
1157 {
1158         NTSTATUS result;
1159         struct cli_state *cli = NULL;
1160         struct security_descriptor *sd = NULL;
1161         struct security_acl *acl_to_add = NULL;
1162         uint32_t j;
1163
1164         cli = cbstate->cli;
1165         sd = get_secdesc_with_ctx(ctx, cli, filename);
1166
1167         if (sd == NULL) {
1168                 return NT_STATUS_NO_MEMORY;
1169         }
1170
1171         /*
1172          * Check if any inheritance related flags are used, if not then
1173          * nothing to do. At the same time populate acls for inheritance
1174          * related ace(s) that need to be added to or deleted from children as
1175          * a result of inheritance propagation.
1176          */
1177
1178         for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1179                 struct security_ace *ace = &sd->dacl->aces[j];
1180                 if (is_inheritable_ace(ace)) {
1181                         bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1182                         if (!added) {
1183                                 result = NT_STATUS_NO_MEMORY;
1184                                 goto out;
1185                         }
1186                 }
1187         }
1188         cbstate->acl_to_add = acl_to_add;
1189         result = NT_STATUS_OK;
1190 out:
1191         return result;
1192 }
1193
1194 /*
1195  * Callback handler to handle child elements processed by cli_list,  we attempt
1196  * to propagate inheritable ace(s) to each child via the function
1197  * propagate_inherited_aces. Children that are themselves directories are passed
1198  * to cli_list again ( to descend the directory structure )
1199  */
1200 static NTSTATUS cacl_set_cb(struct file_info *f,
1201                            const char *mask, void *state)
1202 {
1203         struct cacl_callback_state *cbstate =
1204                 (struct cacl_callback_state *)state;
1205         struct cli_state *cli = NULL;
1206         struct cli_credentials *creds = NULL;
1207
1208         TALLOC_CTX *dirctx = NULL;
1209         NTSTATUS status;
1210         struct cli_state *targetcli = NULL;
1211
1212         char *dir = NULL;
1213         char *dir_end = NULL;
1214         char *mask2 = NULL;
1215         char *targetpath = NULL;
1216         char *caclfile = NULL;
1217
1218         dirctx = talloc_new(NULL);
1219         if (!dirctx) {
1220                 status = NT_STATUS_NO_MEMORY;
1221                 goto out;
1222         }
1223
1224         cli = cbstate->cli;
1225         creds = cbstate->creds;
1226
1227         /* Work out the directory. */
1228         dir = talloc_strdup(dirctx, mask);
1229         if (!dir) {
1230                 status = NT_STATUS_NO_MEMORY;
1231                 goto out;
1232         }
1233
1234         dir_end = strrchr(dir, DIRSEP_CHAR);
1235         if (dir_end != NULL) {
1236                 *dir_end = '\0';
1237         }
1238
1239         if (!f->name || !f->name[0]) {
1240                 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1241                 status = NT_STATUS_UNSUCCESSFUL;
1242                 goto out;
1243         }
1244
1245         if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1246                 struct cacl_callback_state dir_cbstate;
1247                 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1248                         | FILE_ATTRIBUTE_SYSTEM
1249                         | FILE_ATTRIBUTE_HIDDEN;
1250                 dir_end = NULL;
1251
1252                 /* ignore special '.' & '..' */
1253                 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1254                         status = NT_STATUS_OK;
1255                         goto out;
1256                 }
1257
1258                 mask2 = build_dirname(dirctx, mask, dir, f->name);
1259                 if (mask2 == NULL) {
1260                         status = NT_STATUS_NO_MEMORY;
1261                         goto out;
1262                 }
1263
1264                 /* check for dfs */
1265                 status = cli_resolve_path(dirctx, "", creds, cli,
1266                         mask2, &targetcli, &targetpath);
1267                 if (!NT_STATUS_IS_OK(status)) {
1268                         goto out;
1269                 }
1270
1271                 /*
1272                  * prepare path to caclfile, remove any existing wildcard
1273                  * chars and convert path separators.
1274                  */
1275
1276                 caclfile = talloc_strdup(dirctx, targetpath);
1277                 if (!caclfile) {
1278                         status = NT_STATUS_NO_MEMORY;
1279                         goto out;
1280                 }
1281                 dir_end = strrchr(caclfile, '*');
1282                 if (dir_end != NULL) {
1283                         *dir_end = '\0';
1284                 }
1285
1286                 string_replace(caclfile, '/', '\\');
1287                 /*
1288                  * make directory specific copy of cbstate here
1289                  * (for this directory level) to be available as
1290                  * the parent cbstate for the children of this directory.
1291                  * Note: cbstate is overwritten for the current file being
1292                  *       processed.
1293                  */
1294                 dir_cbstate = *cbstate;
1295                 dir_cbstate.cli = targetcli;
1296
1297                 /*
1298                  * propagate any inherited ace from our parent
1299                  */
1300                 status = propagate_inherited_aces(caclfile, &dir_cbstate);
1301                 if (!NT_STATUS_IS_OK(status)) {
1302                         goto out;
1303                 }
1304
1305                 /*
1306                  * get inheritable ace(s) for this dir/container
1307                  * that will be propagated to its children
1308                  */
1309                 status = get_inheritable_aces(dirctx, caclfile,
1310                                                       &dir_cbstate);
1311                 if (!NT_STATUS_IS_OK(status)) {
1312                         goto out;
1313                 }
1314
1315                 /*
1316                  * ensure cacl_set_cb gets called for children
1317                  * of this directory (targetpath)
1318                  */
1319                 status = cli_list(targetcli, targetpath,
1320                         attribute, cacl_set_cb,
1321                         (void *)&dir_cbstate);
1322
1323                 if (!NT_STATUS_IS_OK(status)) {
1324                         goto out;
1325                 }
1326
1327         } else {
1328                 /*
1329                  * build full path to caclfile and replace '/' with '\' so
1330                  * other utility functions can deal with it
1331                  */
1332
1333                 targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1334                 if (!targetpath) {
1335                         status = NT_STATUS_NO_MEMORY;
1336                         goto out;
1337                 }
1338                 string_replace(targetpath, '/', '\\');
1339
1340                 /* attempt to propagate any inherited ace to file caclfile */
1341                 status = propagate_inherited_aces(targetpath, cbstate);
1342
1343                 if (!NT_STATUS_IS_OK(status)) {
1344                         goto out;
1345                 }
1346         }
1347         status = NT_STATUS_OK;
1348 out:
1349         if (!NT_STATUS_IS_OK(status)) {
1350                 d_printf("error %s: processing %s\n",
1351                         nt_errstr(status),
1352                         targetpath);
1353         }
1354         TALLOC_FREE(dirctx);
1355         return status;
1356 }
1357
1358
1359 /*
1360  * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1361  * helper callback function 'cacl_set_cb' handles the child elements processed
1362  * by cli_list.
1363  */
1364 static int inheritance_cacl_set(char *filename,
1365                         struct cacl_callback_state *cbstate)
1366 {
1367         int result;
1368         NTSTATUS ntstatus;
1369         int fileattr;
1370         char *mask = NULL;
1371         struct cli_state *cli = cbstate->cli;
1372         TALLOC_CTX *ctx = NULL;
1373         bool isdirectory = false;
1374         uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1375                                 | FILE_ATTRIBUTE_HIDDEN;
1376         ctx = talloc_init("inherit_set");
1377         if (ctx == NULL) {
1378                 d_printf("out of memory\n");
1379                 result = EXIT_FAILED;
1380                 goto out;
1381         }
1382
1383         /* ensure we have a filename that starts with '\' */
1384         if (!filename || *filename != DIRSEP_CHAR) {
1385                 /* illegal or no filename */
1386                 result = EXIT_FAILED;
1387                 d_printf("illegal or missing name '%s'\n", filename);
1388                 goto out;
1389         }
1390
1391
1392         fileattr = get_fileinfo(cli, filename);
1393         isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1394                 == FILE_ATTRIBUTE_DIRECTORY;
1395
1396         /*
1397          * if we've got as far as here then we have already evaluated
1398          * the args.
1399          */
1400         if (test_args) {
1401                 result = EXIT_OK;
1402                 goto out;
1403         }
1404
1405         mask = NULL;
1406         /* make sure we have a trailing '\*' for directory */
1407         if (!isdirectory) {
1408                 mask = talloc_strdup(ctx, filename);
1409         } else if (strlen(filename) > 1) {
1410                 /*
1411                  * if the passed file name doesn't have a trailing '\'
1412                  * append it.
1413                  */
1414                 char *name_end = strrchr(filename, DIRSEP_CHAR);
1415                 if (name_end != filename + strlen(filename) + 1) {
1416                         mask = talloc_asprintf(ctx, "%s\\*", filename);
1417                 } else {
1418                         mask = talloc_strdup(ctx, filename);
1419                 }
1420         } else {
1421                 /* filename is a single '\', just append '*' */
1422                 mask = talloc_asprintf_append(mask, "%s*", filename);
1423         }
1424
1425         if (!mask) {
1426                 result = EXIT_FAILED;
1427                 goto out;
1428         }
1429
1430         /*
1431          * prepare for automatic propagation of the acl passed on the
1432          * cmdline.
1433          */
1434
1435         ntstatus = prepare_inheritance_propagation(ctx, filename,
1436                                                            cbstate);
1437         if (!NT_STATUS_IS_OK(ntstatus)) {
1438                 d_printf("error: %s processing %s\n",
1439                          nt_errstr(ntstatus), filename);
1440                 result = EXIT_FAILED;
1441                 goto out;
1442         }
1443
1444         result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1445                                 cbstate->mode, cbstate->numeric);
1446
1447         /*
1448          * strictly speaking it could be considered an error if a file was
1449          * specified with '--propagate-inheritance'. However we really want
1450          * to eventually get rid of '--propagate-inheritance' so we will be
1451          * more forgiving here and instead just exit early.
1452          */
1453         if (!isdirectory || (result != EXIT_OK)) {
1454                 goto out;
1455         }
1456
1457         /* check if there is actually any need to propagate */
1458         if (cbstate->acl_no_propagate) {
1459                 goto out;
1460         }
1461         /* get inheritable attributes this parent container (e.g. filename) */
1462         ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1463         if (NT_STATUS_IS_OK(ntstatus)) {
1464                 /* process children */
1465                 ntstatus = cli_list(cli, mask, attribute,
1466                                 cacl_set_cb,
1467                                 (void *)cbstate);
1468         }
1469
1470         if (!NT_STATUS_IS_OK(ntstatus)) {
1471                 d_printf("error: %s processing %s\n",
1472                          nt_errstr(ntstatus), filename);
1473                 result = EXIT_FAILED;
1474                 goto out;
1475         }
1476
1477 out:
1478         TALLOC_FREE(ctx);
1479         return result;
1480 }
1481
1482 /****************************************************************************
1483   main program
1484 ****************************************************************************/
1485 int main(int argc, char *argv[])
1486 {
1487         const char **argv_const = discard_const_p(const char *, argv);
1488         char *share;
1489         int opt;
1490         enum acl_mode mode = SMB_ACL_SET;
1491         static char *the_acl = NULL;
1492         enum chown_mode change_mode = REQUEST_NONE;
1493         int result;
1494         char *path;
1495         char *filename = NULL;
1496         poptContext pc;
1497         /* numeric is set when the user wants numeric SIDs and ACEs rather
1498            than going via LSA calls to resolve them */
1499         int numeric = 0;
1500         struct cli_state *targetcli = NULL;
1501         struct cli_credentials *creds = NULL;
1502         char *targetfile = NULL;
1503         NTSTATUS status;
1504         bool ok;
1505         struct loadparm_context *lp_ctx = NULL;
1506
1507         struct poptOption long_options[] = {
1508                 POPT_AUTOHELP
1509                 {
1510                         .longName   = "delete",
1511                         .shortName  = 'D',
1512                         .argInfo    = POPT_ARG_STRING,
1513                         .arg        = NULL,
1514                         .val        = 'D',
1515                         .descrip    = "Delete an acl",
1516                         .argDescrip = "ACL",
1517                 },
1518                 {
1519                         .longName   = "modify",
1520                         .shortName  = 'M',
1521                         .argInfo    = POPT_ARG_STRING,
1522                         .arg        = NULL,
1523                         .val        = 'M',
1524                         .descrip    = "Modify an acl",
1525                         .argDescrip = "ACL",
1526                 },
1527                 {
1528                         .longName   = "add",
1529                         .shortName  = 'a',
1530                         .argInfo    = POPT_ARG_STRING,
1531                         .arg        = NULL,
1532                         .val        = 'a',
1533                         .descrip    = "Add an acl",
1534                         .argDescrip = "ACL",
1535                 },
1536                 {
1537                         .longName   = "set",
1538                         .shortName  = 'S',
1539                         .argInfo    = POPT_ARG_STRING,
1540                         .arg        = NULL,
1541                         .val        = 'S',
1542                         .descrip    = "Set acls",
1543                         .argDescrip = "ACLS",
1544                 },
1545                 {
1546                         .longName   = "chown",
1547                         .shortName  = 'C',
1548                         .argInfo    = POPT_ARG_STRING,
1549                         .arg        = NULL,
1550                         .val        = 'C',
1551                         .descrip    = "Change ownership of a file",
1552                         .argDescrip = "USERNAME",
1553                 },
1554                 {
1555                         .longName   = "chgrp",
1556                         .shortName  = 'G',
1557                         .argInfo    = POPT_ARG_STRING,
1558                         .arg        = NULL,
1559                         .val        = 'G',
1560                         .descrip    = "Change group ownership of a file",
1561                         .argDescrip = "GROUPNAME",
1562                 },
1563                 {
1564                         .longName   = "inherit",
1565                         .shortName  = 'I',
1566                         .argInfo    = POPT_ARG_STRING,
1567                         .arg        = NULL,
1568                         .val        = 'I',
1569                         .descrip    = "Inherit allow|remove|copy",
1570                 },
1571                 {
1572                         .longName   = "propagate-inheritance",
1573                         .shortName  = 0,
1574                         .argInfo    = POPT_ARG_NONE,
1575                         .arg        = &inheritance,
1576                         .val        = 1,
1577                         .descrip    = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
1578                 },
1579                 {
1580                         .longName   = "numeric",
1581                         .shortName  = 0,
1582                         .argInfo    = POPT_ARG_NONE,
1583                         .arg        = &numeric,
1584                         .val        = 1,
1585                         .descrip    = "Don't resolve sids or masks to names",
1586                 },
1587                 {
1588                         .longName   = "sddl",
1589                         .shortName  = 0,
1590                         .argInfo    = POPT_ARG_NONE,
1591                         .arg        = &sddl,
1592                         .val        = 1,
1593                         .descrip    = "Output and input acls in sddl format",
1594                 },
1595                 {
1596                         .longName   = "query-security-info",
1597                         .shortName  = 0,
1598                         .argInfo    = POPT_ARG_INT,
1599                         .arg        = &query_sec_info,
1600                         .val        = 1,
1601                         .descrip    = "The security-info flags for queries"
1602                 },
1603                 {
1604                         .longName   = "set-security-info",
1605                         .shortName  = 0,
1606                         .argInfo    = POPT_ARG_INT,
1607                         .arg        = &set_sec_info,
1608                         .val        = 1,
1609                         .descrip    = "The security-info flags for modifications"
1610                 },
1611                 {
1612                         .longName   = "test-args",
1613                         .shortName  = 't',
1614                         .argInfo    = POPT_ARG_NONE,
1615                         .arg        = &test_args,
1616                         .val        = 1,
1617                         .descrip    = "Test arguments"
1618                 },
1619                 {
1620                         .longName   = "domain-sid",
1621                         .shortName  = 0,
1622                         .argInfo    = POPT_ARG_STRING,
1623                         .arg        = &domain_sid,
1624                         .val        = 0,
1625                         .descrip    = "Domain SID for sddl",
1626                         .argDescrip = "SID"},
1627                 {
1628                         .longName   = "maximum-access",
1629                         .shortName  = 'x',
1630                         .argInfo    = POPT_ARG_NONE,
1631                         .arg        = NULL,
1632                         .val        = 'x',
1633                         .descrip    = "Query maximum permissions",
1634                 },
1635                 POPT_COMMON_SAMBA
1636                 POPT_COMMON_CONNECTION
1637                 POPT_COMMON_CREDENTIALS
1638                 POPT_LEGACY_S3
1639                 POPT_COMMON_VERSION
1640                 POPT_TABLEEND
1641         };
1642
1643         struct cli_state *cli;
1644         TALLOC_CTX *frame = talloc_stackframe();
1645         const char *owner_username = "";
1646         char *server;
1647
1648         smb_init_locale();
1649
1650         ok = samba_cmdline_init(frame,
1651                                 SAMBA_CMDLINE_CONFIG_CLIENT,
1652                                 false /* require_smbconf */);
1653         if (!ok) {
1654                 DBG_ERR("Failed to init cmdline parser!\n");
1655                 TALLOC_FREE(frame);
1656                 exit(1);
1657         }
1658         lp_ctx = samba_cmdline_get_lp_ctx();
1659         /* set default debug level to 1 regardless of what smb.conf sets */
1660         lpcfg_set_cmdline(lp_ctx, "log level", "1");
1661
1662         setlinebuf(stdout);
1663
1664         pc = samba_popt_get_context(getprogname(),
1665                                     argc,
1666                                     argv_const,
1667                                     long_options,
1668                                     0);
1669         if (pc == NULL) {
1670                 DBG_ERR("Failed to setup popt context!\n");
1671                 TALLOC_FREE(frame);
1672                 exit(1);
1673         }
1674
1675         poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
1676                 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
1677
1678         while ((opt = poptGetNextOpt(pc)) != -1) {
1679                 switch (opt) {
1680                 case 'S':
1681                         the_acl = smb_xstrdup(poptGetOptArg(pc));
1682                         mode = SMB_ACL_SET;
1683                         break;
1684
1685                 case 'D':
1686                         the_acl = smb_xstrdup(poptGetOptArg(pc));
1687                         mode = SMB_ACL_DELETE;
1688                         break;
1689
1690                 case 'M':
1691                         the_acl = smb_xstrdup(poptGetOptArg(pc));
1692                         mode = SMB_ACL_MODIFY;
1693                         break;
1694
1695                 case 'a':
1696                         the_acl = smb_xstrdup(poptGetOptArg(pc));
1697                         mode = SMB_ACL_ADD;
1698                         break;
1699
1700                 case 'C':
1701                         owner_username = poptGetOptArg(pc);
1702                         change_mode = REQUEST_CHOWN;
1703                         break;
1704
1705                 case 'G':
1706                         owner_username = poptGetOptArg(pc);
1707                         change_mode = REQUEST_CHGRP;
1708                         break;
1709
1710                 case 'I':
1711                         owner_username = poptGetOptArg(pc);
1712                         change_mode = REQUEST_INHERIT;
1713                         break;
1714                 case 'm':
1715                         lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
1716                         break;
1717                 case 'x':
1718                         want_mxac = true;
1719                         break;
1720                 case POPT_ERROR_BADOPT:
1721                         fprintf(stderr, "\nInvalid option %s: %s\n\n",
1722                                 poptBadOption(pc, 0), poptStrerror(opt));
1723                         poptPrintUsage(pc, stderr, 0);
1724                         exit(1);
1725                 }
1726         }
1727         if (inheritance && !the_acl) {
1728                 poptPrintUsage(pc, stderr, 0);
1729                 return -1;
1730         }
1731
1732         if(!poptPeekArg(pc)) {
1733                 poptPrintUsage(pc, stderr, 0);
1734                 return -1;
1735         }
1736
1737         path = talloc_strdup(frame, poptGetArg(pc));
1738         if (!path) {
1739                 return -1;
1740         }
1741
1742         if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
1743                 printf("Invalid argument: %s\n", path);
1744                 return -1;
1745         }
1746
1747         if(!poptPeekArg(pc)) {
1748                 poptPrintUsage(pc, stderr, 0);
1749                 return -1;
1750         }
1751
1752         filename = talloc_strdup(frame, poptGetArg(pc));
1753         if (!filename) {
1754                 return -1;
1755         }
1756
1757         poptFreeContext(pc);
1758         samba_cmdline_burn(argc, argv);
1759
1760         string_replace(path,'/','\\');
1761
1762         server = talloc_strdup(frame, path+2);
1763         if (!server) {
1764                 return -1;
1765         }
1766         share = strchr_m(server,'\\');
1767         if (share == NULL) {
1768                 printf("Invalid argument\n");
1769                 return -1;
1770         }
1771
1772         *share = 0;
1773         share++;
1774
1775         creds = samba_cmdline_get_creds();
1776
1777         /* Make connection to server */
1778         if (!test_args) {
1779                 cli = connect_one(creds, server, share);
1780                 if (!cli) {
1781                         exit(EXIT_FAILED);
1782                 }
1783         } else {
1784                 exit(0);
1785         }
1786
1787         string_replace(filename, '/', '\\');
1788         if (filename[0] != '\\') {
1789                 filename = talloc_asprintf(frame,
1790                                 "\\%s",
1791                                 filename);
1792                 if (!filename) {
1793                         return -1;
1794                 }
1795         }
1796
1797         status = cli_resolve_path(frame,
1798                                   "",
1799                                   creds,
1800                                   cli,
1801                                   filename,
1802                                   &targetcli,
1803                                   &targetfile);
1804         if (!NT_STATUS_IS_OK(status)) {
1805                 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
1806                 return -1;
1807         }
1808
1809         /* Perform requested action */
1810
1811         if (change_mode == REQUEST_INHERIT) {
1812                 result = inherit(targetcli, targetfile, owner_username);
1813         } else if (change_mode != REQUEST_NONE) {
1814                 result = owner_set(targetcli, change_mode, targetfile, owner_username);
1815         } else if (the_acl) {
1816                 if (inheritance) {
1817                         struct cacl_callback_state cbstate = {
1818                                 .creds = creds,
1819                                 .cli = targetcli,
1820                                 .mode = mode,
1821                                 .the_acl = the_acl,
1822                                 .numeric = numeric,
1823                         };
1824                         result = inheritance_cacl_set(targetfile, &cbstate);
1825                 } else {
1826                         result =  cacl_set(targetcli,
1827                                            targetfile,
1828                                            the_acl,
1829                                            mode,
1830                                            numeric);
1831                 }
1832         } else {
1833                 result = cacl_dump(targetcli, targetfile, numeric);
1834         }
1835
1836         TALLOC_FREE(frame);
1837
1838         return result;
1839 }