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