Describe implication of upstream ICU-22610
[samba.git] / source3 / utils / smbcacls.c
blobc157e9433fa6ad8dc9526b3a709d5554ed608ac8
1 /*
2 Unix SMB/CIFS implementation.
3 ACL get/set utility
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
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.
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.
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/>.
25 #include "includes.h"
26 #include "lib/cmdline/cmdline.h"
27 #include "rpc_client/cli_pipe.h"
28 #include "../librpc/gen_ndr/ndr_lsa.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../libcli/security/security.h"
31 #include "libsmb/libsmb.h"
32 #include "libsmb/clirap.h"
33 #include "passdb/machine_sid.h"
34 #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 #include "util_sd.h"
36 #include "lib/param/param.h"
37 #include "lib/util/util_file.h"
39 static char DIRSEP_CHAR = '\\';
41 static int inheritance = 0;
42 static const char *save_file = NULL;
43 static const char *restore_file = NULL;
44 static int recurse;
45 static int test_args;
46 static int sddl;
47 static int query_sec_info = -1;
48 static int set_sec_info = -1;
49 static bool want_mxac;
51 static const char *domain_sid = NULL;
53 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
54 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
55 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
57 struct cacl_callback_state {
58 struct cli_credentials *creds;
59 struct cli_state *cli;
60 struct security_descriptor *aclsd;
61 struct security_acl *acl_to_add;
62 enum acl_mode mode;
63 char *the_acl;
64 bool acl_no_propagate;
65 bool numeric;
68 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
69 struct dom_sid *sid)
71 union lsa_PolicyInformation *info = NULL;
72 struct smbXcli_tcon *orig_tcon = NULL;
73 char *orig_share = NULL;
74 struct rpc_pipe_client *rpc_pipe = NULL;
75 struct policy_handle handle;
76 NTSTATUS status, result;
77 TALLOC_CTX *frame = talloc_stackframe();
79 if (cli_state_has_tcon(cli)) {
80 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
83 status = cli_tree_connect(cli, "IPC$", "?????", NULL);
84 if (!NT_STATUS_IS_OK(status)) {
85 goto done;
88 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
89 if (!NT_STATUS_IS_OK(status)) {
90 goto tdis;
93 status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
94 GENERIC_EXECUTE_ACCESS, &handle);
95 if (!NT_STATUS_IS_OK(status)) {
96 goto tdis;
99 status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
100 frame, &handle,
101 LSA_POLICY_INFO_DOMAIN,
102 &info, &result);
104 if (any_nt_status_not_ok(status, result, &status)) {
105 goto tdis;
108 *sid = *info->domain.sid;
110 tdis:
111 TALLOC_FREE(rpc_pipe);
112 cli_tdis(cli);
113 done:
114 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
115 TALLOC_FREE(frame);
116 return status;
119 static struct dom_sid *get_domain_sid(struct cli_state *cli)
121 NTSTATUS status;
122 struct dom_sid_buf buf;
124 struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
125 if (sid == NULL) {
126 DEBUG(0, ("Out of memory\n"));
127 return NULL;
130 if (domain_sid) {
131 if (!dom_sid_parse(domain_sid, sid)) {
132 DEBUG(0,("failed to parse domain sid\n"));
133 TALLOC_FREE(sid);
135 } else {
136 status = cli_lsa_lookup_domain_sid(cli, sid);
138 if (!NT_STATUS_IS_OK(status)) {
139 DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
140 TALLOC_FREE(sid);
145 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
146 return sid;
149 /* add an ACE to a list of ACEs in a struct security_acl */
150 static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
151 const struct security_ace *ace)
154 struct security_acl *acl = *the_acl;
156 if (acl == NULL) {
157 acl = make_sec_acl(ctx, 3, 0, NULL);
158 if (acl == NULL) {
159 return false;
163 if (acl->num_aces == UINT32_MAX) {
164 return false;
166 ADD_TO_ARRAY(
167 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
168 *the_acl = acl;
169 return True;
172 static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
174 return add_ace_with_ctx(talloc_tos(), the_acl, ace);
177 /* parse a ascii version of a security descriptor */
178 static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
180 const char *p = str;
181 char *tok;
182 struct security_descriptor *ret = NULL;
183 size_t sd_size;
184 struct dom_sid owner_sid = { .num_auths = 0 };
185 bool have_owner = false;
186 struct dom_sid group_sid = { .num_auths = 0 };
187 bool have_group = false;
188 struct security_acl *dacl=NULL;
189 int revision=1;
191 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
192 if (strncmp(tok,"REVISION:", 9) == 0) {
193 revision = strtol(tok+9, NULL, 16);
194 continue;
197 if (strncmp(tok,"OWNER:", 6) == 0) {
198 if (have_owner) {
199 printf("Only specify owner once\n");
200 goto done;
202 if (!StringToSid(cli, &owner_sid, tok+6)) {
203 printf("Failed to parse owner sid\n");
204 goto done;
206 have_owner = true;
207 continue;
210 if (strncmp(tok,"GROUP:", 6) == 0) {
211 if (have_group) {
212 printf("Only specify group once\n");
213 goto done;
215 if (!StringToSid(cli, &group_sid, tok+6)) {
216 printf("Failed to parse group sid\n");
217 goto done;
219 have_group = true;
220 continue;
223 if (strncmp(tok,"ACL:", 4) == 0) {
224 struct security_ace ace;
225 if (!parse_ace(cli, &ace, tok+4)) {
226 goto done;
228 if(!add_ace(&dacl, &ace)) {
229 printf("Failed to add ACL %s\n", tok);
230 goto done;
232 continue;
235 printf("Failed to parse token '%s' in security descriptor,\n", tok);
236 goto done;
239 ret = make_sec_desc(
240 ctx,
241 revision,
242 SEC_DESC_SELF_RELATIVE,
243 have_owner ? &owner_sid : NULL,
244 have_group ? &group_sid : NULL,
245 NULL,
246 dacl,
247 &sd_size);
249 done:
250 return ret;
253 /*****************************************************
254 get fileinfo for filename
255 *******************************************************/
256 static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
258 uint16_t fnum = (uint16_t)-1;
259 NTSTATUS status;
260 struct smb_create_returns cr = {0};
262 /* The desired access below is the only one I could find that works
263 with NT4, W2KP and Samba */
265 status = cli_ntcreate(
266 cli, /* cli */
267 filename, /* fname */
268 0, /* CreatFlags */
269 READ_CONTROL_ACCESS, /* CreatFlags */
270 0, /* FileAttributes */
271 FILE_SHARE_READ|
272 FILE_SHARE_WRITE, /* ShareAccess */
273 FILE_OPEN, /* CreateDisposition */
274 0x0, /* CreateOptions */
275 0x0, /* SecurityFlags */
276 &fnum, /* pfid */
277 &cr); /* cr */
278 if (!NT_STATUS_IS_OK(status)) {
279 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
280 return 0;
283 cli_close(cli, fnum);
284 return cr.file_attributes;
287 /*****************************************************
288 get sec desc for filename
289 *******************************************************/
290 static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
291 struct cli_state *cli,
292 const char *filename)
294 uint16_t fnum = (uint16_t)-1;
295 struct security_descriptor *sd;
296 NTSTATUS status;
297 uint32_t sec_info;
298 uint32_t desired_access = 0;
300 if (query_sec_info == -1) {
301 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
302 } else {
303 sec_info = query_sec_info;
306 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
307 desired_access |= SEC_STD_READ_CONTROL;
309 if (sec_info & SECINFO_SACL) {
310 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
313 if (desired_access == 0) {
314 desired_access |= SEC_STD_READ_CONTROL;
317 status = cli_ntcreate(cli, filename, 0, desired_access,
318 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
319 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
320 if (!NT_STATUS_IS_OK(status)) {
321 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
322 return NULL;
325 status = cli_query_security_descriptor(cli, fnum, sec_info,
326 ctx, &sd);
328 cli_close(cli, fnum);
330 if (!NT_STATUS_IS_OK(status)) {
331 printf("Failed to get security descriptor: %s\n",
332 nt_errstr(status));
333 return NULL;
335 return sd;
338 static struct security_descriptor *get_secdesc(struct cli_state *cli,
339 const char *filename)
341 return get_secdesc_with_ctx(talloc_tos(), cli, filename);
343 /*****************************************************
344 set sec desc for filename
345 *******************************************************/
346 static bool set_secdesc(struct cli_state *cli, const char *filename,
347 struct security_descriptor *sd)
349 uint16_t fnum = (uint16_t)-1;
350 bool result=true;
351 NTSTATUS status;
352 uint32_t desired_access = 0;
353 uint32_t sec_info;
355 if (set_sec_info == -1) {
356 sec_info = 0;
358 if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
359 sec_info |= SECINFO_DACL;
361 if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
362 sec_info |= SECINFO_SACL;
364 if (sd->owner_sid) {
365 sec_info |= SECINFO_OWNER;
367 if (sd->group_sid) {
368 sec_info |= SECINFO_GROUP;
370 } else {
371 sec_info = set_sec_info;
374 /* Make the desired_access more specific. */
375 if (sec_info & SECINFO_DACL) {
376 desired_access |= SEC_STD_WRITE_DAC;
378 if (sec_info & SECINFO_SACL) {
379 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
381 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
382 desired_access |= SEC_STD_WRITE_OWNER;
385 status = cli_ntcreate(cli, filename, 0,
386 desired_access,
387 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
388 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
389 if (!NT_STATUS_IS_OK(status)) {
390 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
391 return false;
394 status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
395 if (!NT_STATUS_IS_OK(status)) {
396 printf("ERROR: security descriptor set failed: %s\n",
397 nt_errstr(status));
398 result=false;
401 cli_close(cli, fnum);
402 return result;
405 /*****************************************************
406 get maximum access for a file
407 *******************************************************/
408 static int cacl_mxac(struct cli_state *cli, const char *filename)
410 NTSTATUS status;
411 uint32_t mxac;
413 status = cli_query_mxac(cli, filename, &mxac);
414 if (!NT_STATUS_IS_OK(status)) {
415 printf("Failed to get mxac: %s\n", nt_errstr(status));
416 return EXIT_FAILED;
419 printf("Maximum access: 0x%x\n", mxac);
421 return EXIT_OK;
425 /*****************************************************
426 dump the acls for a file
427 *******************************************************/
428 static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
430 struct security_descriptor *sd;
431 int ret;
433 if (test_args) {
434 return EXIT_OK;
437 sd = get_secdesc(cli, filename);
438 if (sd == NULL) {
439 return EXIT_FAILED;
442 if (sddl) {
443 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
444 if (str == NULL) {
445 return EXIT_FAILED;
447 printf("%s\n", str);
448 TALLOC_FREE(str);
449 } else {
450 sec_desc_print(cli, stdout, sd, numeric);
453 if (want_mxac) {
454 ret = cacl_mxac(cli, filename);
455 if (ret != EXIT_OK) {
456 return ret;
460 return EXIT_OK;
463 /*****************************************************
464 Change the ownership or group ownership of a file. Just
465 because the NT docs say this can't be done :-). JRA.
466 *******************************************************/
468 static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
469 const char *filename, const char *new_username)
471 struct dom_sid sid;
472 struct security_descriptor *sd;
473 size_t sd_size;
475 if (!StringToSid(cli, &sid, new_username))
476 return EXIT_PARSE_ERROR;
478 sd = make_sec_desc(talloc_tos(),
479 SECURITY_DESCRIPTOR_REVISION_1,
480 SEC_DESC_SELF_RELATIVE,
481 (change_mode == REQUEST_CHOWN) ? &sid : NULL,
482 (change_mode == REQUEST_CHGRP) ? &sid : NULL,
483 NULL, NULL, &sd_size);
485 if (!set_secdesc(cli, filename, sd)) {
486 return EXIT_FAILED;
489 return EXIT_OK;
493 /* The MSDN is contradictory over the ordering of ACE entries in an
494 ACL. However NT4 gives a "The information may have been modified
495 by a computer running Windows NT 5.0" if denied ACEs do not appear
496 before allowed ACEs. At
497 http://technet.microsoft.com/en-us/library/cc781716.aspx the
498 canonical order is specified as "Explicit Deny, Explicit Allow,
499 Inherited ACEs unchanged" */
501 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
503 if (security_ace_equal(ace1, ace2))
504 return 0;
506 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
507 !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
508 return 1;
509 if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
510 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
511 return -1;
512 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
513 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
514 return NUMERIC_CMP(ace2->type, ace1->type);
516 if (ace1->type != ace2->type) {
517 /* note the reverse order */
518 return NUMERIC_CMP(ace2->type, ace1->type);
520 if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
521 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
523 if (ace1->flags != ace2->flags)
524 return NUMERIC_CMP(ace1->flags, ace2->flags);
526 if (ace1->access_mask != ace2->access_mask)
527 return NUMERIC_CMP(ace1->access_mask, ace2->access_mask);
529 if (ace1->size != ace2->size)
530 return NUMERIC_CMP(ace1->size, ace2->size);
532 return memcmp(ace1, ace2, sizeof(struct security_ace));
535 static void sort_acl(struct security_acl *the_acl)
537 uint32_t i;
538 if (!the_acl) return;
540 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
542 for (i=1;i<the_acl->num_aces;) {
543 if (security_ace_equal(&the_acl->aces[i-1],
544 &the_acl->aces[i])) {
545 ARRAY_DEL_ELEMENT(
546 the_acl->aces, i, the_acl->num_aces);
547 the_acl->num_aces--;
548 } else {
549 i++;
554 /*****************************************************
555 set the ACLs on a file given a security descriptor
556 *******************************************************/
558 static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
559 struct security_descriptor *sd, enum acl_mode mode,
560 bool numeric)
562 struct security_descriptor *old = NULL;
563 uint32_t i, j;
564 size_t sd_size;
565 int result = EXIT_OK;
567 if (!sd) return EXIT_PARSE_ERROR;
568 if (test_args) return EXIT_OK;
570 if (mode != SMB_ACL_SET) {
572 * Do not fetch old ACL when it will be overwritten
573 * completely with a new one.
575 old = get_secdesc(cli, filename);
577 if (!old) {
578 return EXIT_FAILED;
582 /* the logic here is rather more complex than I would like */
583 switch (mode) {
584 case SMB_ACL_DELETE:
585 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
586 bool found = False;
588 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
589 if (security_ace_equal(&sd->dacl->aces[i],
590 &old->dacl->aces[j])) {
591 uint32_t k;
592 for (k=j; k<old->dacl->num_aces-1;k++) {
593 old->dacl->aces[k] = old->dacl->aces[k+1];
595 old->dacl->num_aces--;
596 found = True;
597 break;
601 if (!found) {
602 printf("ACL for ACE:");
603 print_ace(cli, stdout, &sd->dacl->aces[i],
604 numeric);
605 printf(" not found\n");
608 break;
610 case SMB_ACL_MODIFY:
611 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
612 bool found = False;
614 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
615 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
616 &old->dacl->aces[j].trustee)) {
617 old->dacl->aces[j] = sd->dacl->aces[i];
618 found = True;
622 if (!found) {
623 fstring str;
625 SidToString(cli, str,
626 &sd->dacl->aces[i].trustee,
627 numeric);
628 printf("ACL for SID %s not found\n", str);
632 if (sd->owner_sid) {
633 old->owner_sid = sd->owner_sid;
636 if (sd->group_sid) {
637 old->group_sid = sd->group_sid;
640 break;
642 case SMB_ACL_ADD:
643 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
644 add_ace(&old->dacl, &sd->dacl->aces[i]);
646 break;
648 case SMB_ACL_SET:
649 old = sd;
650 break;
653 /* Denied ACE entries must come before allowed ones */
654 sort_acl(old->dacl);
656 /* Create new security descriptor and set it */
658 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
659 But if we're sending an owner, even if it's the same as the one
660 that already exists then W2K3 insists we open with WRITE_OWNER access.
661 I need to check that setting a SD with no owner set works against WNT
662 and W2K. JRA.
665 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
666 old->owner_sid, old->group_sid,
667 NULL, old->dacl, &sd_size);
669 if (!set_secdesc(cli, filename, sd)) {
670 result = EXIT_FAILED;
673 return result;
676 /*****************************************************
677 set the ACLs on a file given an ascii description
678 *******************************************************/
680 static int cacl_set(struct cli_state *cli, const char *filename,
681 char *the_acl, enum acl_mode mode, bool numeric)
683 struct security_descriptor *sd = NULL;
685 if (sddl) {
686 const char *msg = NULL;
687 size_t msg_offset = 0;
688 enum ace_condition_flags flags =
689 ACE_CONDITION_FLAG_ALLOW_DEVICE;
690 sd = sddl_decode_err_msg(talloc_tos(),
691 the_acl,
692 get_domain_sid(cli),
693 flags,
694 &msg,
695 &msg_offset);
696 if (sd == NULL) {
697 DBG_ERR("could not decode '%s'\n", the_acl);
698 if (msg != NULL) {
699 DBG_ERR(" %*c\n",
700 (int)msg_offset, '^');
701 DBG_ERR("error '%s'\n", msg);
704 } else {
705 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
708 if (sd == NULL) {
709 return EXIT_PARSE_ERROR;
711 if (test_args) {
712 return EXIT_OK;
714 return cacl_set_from_sd(cli, filename, sd, mode, numeric);
717 /*****************************************************
718 set the inherit on a file
719 *******************************************************/
720 static int inherit(struct cli_state *cli, const char *filename,
721 const char *type)
723 struct security_descriptor *old,*sd;
724 uint32_t oldattr;
725 size_t sd_size;
726 int result = EXIT_OK;
728 old = get_secdesc(cli, filename);
730 if (!old) {
731 return EXIT_FAILED;
734 oldattr = get_fileinfo(cli,filename);
736 if (strcmp(type,"allow")==0) {
737 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
738 SEC_DESC_DACL_PROTECTED) {
739 uint32_t i;
740 char *parentname,*temp;
741 struct security_descriptor *parent;
742 temp = talloc_strdup(talloc_tos(), filename);
744 old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
746 /* look at parent and copy in all its inheritable ACL's. */
747 string_replace(temp, '\\', '/');
748 if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
749 return EXIT_FAILED;
751 string_replace(parentname, '/', '\\');
752 parent = get_secdesc(cli,parentname);
753 if (parent == NULL) {
754 return EXIT_FAILED;
756 for (i=0;i<parent->dacl->num_aces;i++) {
757 struct security_ace *ace=&parent->dacl->aces[i];
758 /* Add inherited flag to all aces */
759 ace->flags=ace->flags|
760 SEC_ACE_FLAG_INHERITED_ACE;
761 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
762 if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
763 SEC_ACE_FLAG_CONTAINER_INHERIT) {
764 add_ace(&old->dacl, ace);
766 } else {
767 if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
768 SEC_ACE_FLAG_OBJECT_INHERIT) {
769 /* clear flags for files */
770 ace->flags=0;
771 add_ace(&old->dacl, ace);
775 } else {
776 printf("Already set to inheritable permissions.\n");
777 return EXIT_FAILED;
779 } else if (strcmp(type,"remove")==0) {
780 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
781 SEC_DESC_DACL_PROTECTED) {
782 old->type=old->type | SEC_DESC_DACL_PROTECTED;
784 /* remove all inherited ACL's. */
785 if (old->dacl) {
786 int i;
787 struct security_acl *temp=old->dacl;
788 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
789 for (i=temp->num_aces-1;i>=0;i--) {
790 struct security_ace *ace=&temp->aces[i];
791 /* Remove all ace with INHERITED flag set */
792 if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
793 SEC_ACE_FLAG_INHERITED_ACE) {
794 add_ace(&old->dacl,ace);
798 } else {
799 printf("Already set to no inheritable permissions.\n");
800 return EXIT_FAILED;
802 } else if (strcmp(type,"copy")==0) {
803 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
804 SEC_DESC_DACL_PROTECTED) {
805 old->type=old->type | SEC_DESC_DACL_PROTECTED;
808 * convert all inherited ACL's to non
809 * inherited ACL's.
811 if (old->dacl) {
812 uint32_t i;
813 for (i=0;i<old->dacl->num_aces;i++) {
814 struct security_ace *ace=&old->dacl->aces[i];
815 /* Remove INHERITED FLAG from all aces */
816 ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
819 } else {
820 printf("Already set to no inheritable permissions.\n");
821 return EXIT_FAILED;
825 /* Denied ACE entries must come before allowed ones */
826 sort_acl(old->dacl);
828 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
829 old->owner_sid, old->group_sid,
830 NULL, old->dacl, &sd_size);
832 if (!set_secdesc(cli, filename, sd)) {
833 result = EXIT_FAILED;
836 return result;
839 /*****************************************************
840 Return a connection to a server.
841 *******************************************************/
842 static struct cli_state *connect_one(struct cli_credentials *creds,
843 const char *server, const char *share)
845 struct cli_state *c = NULL;
846 NTSTATUS nt_status;
847 uint32_t flags = 0;
849 nt_status = cli_full_connection_creds(talloc_tos(),
851 lp_netbios_name(),
852 server,
853 NULL,
855 share,
856 "?????",
857 creds,
858 flags);
859 if (!NT_STATUS_IS_OK(nt_status)) {
860 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
861 return NULL;
864 return c;
868 * Process resulting combination of mask & fname ensuring
869 * terminated with wildcard
871 static char *build_dirname(TALLOC_CTX *ctx,
872 const char *mask, char *dir, char *fname)
874 char *mask2 = NULL;
875 char *p = NULL;
877 mask2 = talloc_strdup(ctx, mask);
878 if (!mask2) {
879 return NULL;
881 p = strrchr_m(mask2, DIRSEP_CHAR);
882 if (p) {
883 p[1] = 0;
884 } else {
885 mask2[0] = '\0';
887 mask2 = talloc_asprintf_append(mask2,
888 "%s\\*",
889 fname);
890 return mask2;
894 * Returns a copy of the ACL flags in ace modified according
895 * to some inheritance rules.
896 * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
897 * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
898 * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
899 * stripped from flags to be propagated to non-container children
900 * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
901 * stripped from flags to be propagated if the NP flag
902 * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
905 static uint8_t get_flags_to_propagate(bool is_container,
906 struct security_ace *ace)
908 uint8_t newflags = ace->flags;
909 /* OBJECT inheritance */
910 bool acl_objinherit = (ace->flags &
911 SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
912 /* CONTAINER inheritance */
913 bool acl_cntrinherit = (ace->flags &
914 SEC_ACE_FLAG_CONTAINER_INHERIT) ==
915 SEC_ACE_FLAG_CONTAINER_INHERIT;
916 /* PROHIBIT inheritance */
917 bool prohibit_inheritance = ((ace->flags &
918 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
919 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
921 /* Assume we are not propagating the ACE */
923 newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
925 /* Inherit-only flag is not propagated to children */
927 newflags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
928 /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
929 if (acl_cntrinherit || acl_objinherit) {
931 * object inherit ( alone ) on a container needs
932 * SEC_ACE_FLAG_INHERIT_ONLY
934 if (is_container) {
935 if (acl_objinherit && !acl_cntrinherit) {
936 newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
939 * this is tricky, the only time we would not
940 * propagate the ace for a container is if
941 * prohibit_inheritance is set and object inheritance
942 * alone is set
944 if ((prohibit_inheritance
945 && acl_objinherit
946 && !acl_cntrinherit) == false) {
947 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
949 } else {
951 * don't apply object/container inheritance flags to
952 * non dirs
954 newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
955 | SEC_ACE_FLAG_CONTAINER_INHERIT
956 | SEC_ACE_FLAG_INHERIT_ONLY);
958 * only apply ace to file if object inherit
960 if (acl_objinherit) {
961 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
965 /* if NP is specified strip NP and all OI/CI INHERIT flags */
966 if (prohibit_inheritance) {
967 newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
968 | SEC_ACE_FLAG_CONTAINER_INHERIT
969 | SEC_ACE_FLAG_INHERIT_ONLY
970 | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
973 return newflags;
977 * This function builds a new acl for 'caclfile', first it removes any
978 * existing inheritable ace(s) from the current acl of caclfile, secondly it
979 * applies any inheritable acls of the parent of caclfile ( inheritable acls of
980 * caclfile's parent are passed via acl_to_add member of cbstate )
983 static NTSTATUS propagate_inherited_aces(char *caclfile,
984 struct cacl_callback_state *cbstate)
986 TALLOC_CTX *aclctx = NULL;
987 NTSTATUS status;
988 int result;
989 int fileattr;
990 struct security_descriptor *old = NULL;
991 bool is_container = false;
992 struct security_acl *acl_to_add = cbstate->acl_to_add;
993 struct security_acl *acl_to_remove = NULL;
994 uint32_t i, j;
996 aclctx = talloc_new(NULL);
997 if (aclctx == NULL) {
998 return NT_STATUS_NO_MEMORY;
1000 old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
1002 if (!old) {
1003 status = NT_STATUS_UNSUCCESSFUL;
1004 goto out;
1007 /* inhibit propagation? */
1008 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
1009 SEC_DESC_DACL_PROTECTED){
1010 status = NT_STATUS_OK;
1011 goto out;
1014 fileattr = get_fileinfo(cbstate->cli, caclfile);
1015 is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
1017 /* find acl(s) that are inherited */
1018 for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1020 if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
1021 if (!add_ace_with_ctx(aclctx, &acl_to_remove,
1022 &old->dacl->aces[j])) {
1023 status = NT_STATUS_NO_MEMORY;
1024 goto out;
1029 /* remove any acl(s) that are inherited */
1030 if (acl_to_remove) {
1031 for (i = 0; i < acl_to_remove->num_aces; i++) {
1032 struct security_ace ace = acl_to_remove->aces[i];
1033 for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1035 if (security_ace_equal(&ace,
1036 &old->dacl->aces[j])) {
1037 uint32_t k;
1038 for (k = j; k < old->dacl->num_aces-1;
1039 k++) {
1040 old->dacl->aces[k] =
1041 old->dacl->aces[k+1];
1043 old->dacl->num_aces--;
1044 break;
1049 /* propagate any inheritable ace to be added */
1050 if (acl_to_add) {
1051 for (i = 0; i < acl_to_add->num_aces; i++) {
1052 struct security_ace ace = acl_to_add->aces[i];
1053 bool is_objectinherit = (ace.flags &
1054 SEC_ACE_FLAG_OBJECT_INHERIT) ==
1055 SEC_ACE_FLAG_OBJECT_INHERIT;
1056 bool is_inherited;
1057 /* don't propagate flags to a file unless OI */
1058 if (!is_objectinherit && !is_container) {
1059 continue;
1062 * adjust flags according to inheritance
1063 * rules
1065 ace.flags = get_flags_to_propagate(is_container, &ace);
1066 is_inherited = (ace.flags &
1067 SEC_ACE_FLAG_INHERITED_ACE) ==
1068 SEC_ACE_FLAG_INHERITED_ACE;
1069 /* don't propagate non inherited flags */
1070 if (!is_inherited) {
1071 continue;
1073 if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1074 status = NT_STATUS_NO_MEMORY;
1075 goto out;
1080 result = cacl_set_from_sd(cbstate->cli, caclfile,
1081 old,
1082 SMB_ACL_SET, cbstate->numeric);
1083 if (result != EXIT_OK) {
1084 status = NT_STATUS_UNSUCCESSFUL;
1085 goto out;
1088 status = NT_STATUS_OK;
1089 out:
1090 TALLOC_FREE(aclctx);
1091 return status;
1095 * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1096 * SEC_ACE_FLAG_CONTAINER_INHERIT
1098 static bool is_inheritable_ace(struct security_ace *ace)
1100 uint8_t flags = ace->flags;
1101 if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1102 | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1103 return true;
1105 return false;
1108 /* This method does some basic sanity checking with respect to automatic
1109 * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1110 * attempts to set inherited permissions directly. Additionally this method
1111 * does some basic initialisation for instance it parses the ACL passed on the
1112 * command line.
1114 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1115 struct cacl_callback_state *cbstate)
1117 NTSTATUS result;
1118 char *the_acl = cbstate->the_acl;
1119 struct cli_state *cli = cbstate->cli;
1120 enum acl_mode mode = cbstate->mode;
1121 struct security_descriptor *sd = NULL;
1122 struct security_descriptor *old = NULL;
1123 uint32_t j;
1124 bool propagate = false;
1126 old = get_secdesc_with_ctx(ctx, cli, filename);
1127 if (old == NULL) {
1128 return NT_STATUS_NO_MEMORY;
1131 /* parse acl passed on the command line */
1132 if (sddl) {
1133 const char *msg = NULL;
1134 size_t msg_offset = 0;
1135 enum ace_condition_flags flags =
1136 ACE_CONDITION_FLAG_ALLOW_DEVICE;
1138 cbstate->aclsd = sddl_decode_err_msg(ctx,
1139 the_acl,
1140 get_domain_sid(cli),
1141 flags,
1142 &msg,
1143 &msg_offset);
1144 if (cbstate->aclsd == NULL) {
1145 DBG_ERR("could not decode '%s'\n", the_acl);
1146 if (msg != NULL) {
1147 DBG_ERR(" %*c\n",
1148 (int)msg_offset, '^');
1149 DBG_ERR("error '%s'\n", msg);
1152 } else {
1153 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1156 if (!cbstate->aclsd) {
1157 result = NT_STATUS_UNSUCCESSFUL;
1158 goto out;
1161 sd = cbstate->aclsd;
1163 /* set operation if inheritance is enabled doesn't make sense */
1164 if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1165 SEC_DESC_DACL_PROTECTED)){
1166 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1167 result = NT_STATUS_UNSUCCESSFUL;
1168 goto out;
1173 * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1174 * flags that are set
1176 for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1177 struct security_ace *ace = &sd->dacl->aces[j];
1178 if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1179 d_printf("Illegal parameter %s\n", the_acl);
1180 result = NT_STATUS_UNSUCCESSFUL;
1181 goto out;
1183 if (!propagate) {
1184 if (is_inheritable_ace(ace)) {
1185 propagate = true;
1190 result = NT_STATUS_OK;
1191 out:
1192 cbstate->acl_no_propagate = !propagate;
1193 return result;
1197 * This method builds inheritable ace(s) from filename (which should be
1198 * a container) that need propagating to children in order to provide
1199 * automatic inheritance. Those inheritable ace(s) are stored in
1200 * acl_to_add member of cbstate for later processing
1201 * (see propagate_inherited_aces)
1203 static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1204 struct cacl_callback_state *cbstate)
1206 NTSTATUS result;
1207 struct cli_state *cli = NULL;
1208 struct security_descriptor *sd = NULL;
1209 struct security_acl *acl_to_add = NULL;
1210 uint32_t j;
1212 cli = cbstate->cli;
1213 sd = get_secdesc_with_ctx(ctx, cli, filename);
1215 if (sd == NULL) {
1216 return NT_STATUS_NO_MEMORY;
1220 * Check if any inheritance related flags are used, if not then
1221 * nothing to do. At the same time populate acls for inheritance
1222 * related ace(s) that need to be added to or deleted from children as
1223 * a result of inheritance propagation.
1226 for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1227 struct security_ace *ace = &sd->dacl->aces[j];
1228 if (is_inheritable_ace(ace)) {
1229 bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1230 if (!added) {
1231 result = NT_STATUS_NO_MEMORY;
1232 goto out;
1236 cbstate->acl_to_add = acl_to_add;
1237 result = NT_STATUS_OK;
1238 out:
1239 return result;
1243 * Callback handler to handle child elements processed by cli_list, we attempt
1244 * to propagate inheritable ace(s) to each child via the function
1245 * propagate_inherited_aces. Children that are themselves directories are passed
1246 * to cli_list again ( to descend the directory structure )
1248 static NTSTATUS cacl_set_cb(struct file_info *f,
1249 const char *mask, void *state)
1251 struct cacl_callback_state *cbstate =
1252 (struct cacl_callback_state *)state;
1253 struct cli_state *cli = NULL;
1254 struct cli_credentials *creds = NULL;
1256 TALLOC_CTX *dirctx = NULL;
1257 NTSTATUS status;
1258 struct cli_state *targetcli = NULL;
1260 char *dir = NULL;
1261 char *dir_end = NULL;
1262 char *mask2 = NULL;
1263 char *targetpath = NULL;
1264 char *caclfile = NULL;
1266 dirctx = talloc_new(NULL);
1267 if (!dirctx) {
1268 status = NT_STATUS_NO_MEMORY;
1269 goto out;
1272 cli = cbstate->cli;
1273 creds = cbstate->creds;
1275 /* Work out the directory. */
1276 dir = talloc_strdup(dirctx, mask);
1277 if (!dir) {
1278 status = NT_STATUS_NO_MEMORY;
1279 goto out;
1282 dir_end = strrchr(dir, DIRSEP_CHAR);
1283 if (dir_end != NULL) {
1284 *dir_end = '\0';
1287 if (!f->name || !f->name[0]) {
1288 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1289 status = NT_STATUS_UNSUCCESSFUL;
1290 goto out;
1293 if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1294 struct cacl_callback_state dir_cbstate;
1295 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1296 | FILE_ATTRIBUTE_SYSTEM
1297 | FILE_ATTRIBUTE_HIDDEN;
1298 dir_end = NULL;
1300 /* ignore special '.' & '..' */
1301 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1302 status = NT_STATUS_OK;
1303 goto out;
1306 mask2 = build_dirname(dirctx, mask, dir, f->name);
1307 if (mask2 == NULL) {
1308 status = NT_STATUS_NO_MEMORY;
1309 goto out;
1312 /* check for dfs */
1313 status = cli_resolve_path(dirctx, "", creds, cli,
1314 mask2, &targetcli, &targetpath);
1315 if (!NT_STATUS_IS_OK(status)) {
1316 goto out;
1320 * prepare path to caclfile, remove any existing wildcard
1321 * chars and convert path separators.
1324 caclfile = talloc_strdup(dirctx, targetpath);
1325 if (!caclfile) {
1326 status = NT_STATUS_NO_MEMORY;
1327 goto out;
1329 dir_end = strrchr(caclfile, '*');
1330 if (dir_end != NULL) {
1331 *dir_end = '\0';
1334 string_replace(caclfile, '/', '\\');
1336 * make directory specific copy of cbstate here
1337 * (for this directory level) to be available as
1338 * the parent cbstate for the children of this directory.
1339 * Note: cbstate is overwritten for the current file being
1340 * processed.
1342 dir_cbstate = *cbstate;
1343 dir_cbstate.cli = targetcli;
1346 * propagate any inherited ace from our parent
1348 status = propagate_inherited_aces(caclfile, &dir_cbstate);
1349 if (!NT_STATUS_IS_OK(status)) {
1350 goto out;
1354 * get inheritable ace(s) for this dir/container
1355 * that will be propagated to its children
1357 status = get_inheritable_aces(dirctx, caclfile,
1358 &dir_cbstate);
1359 if (!NT_STATUS_IS_OK(status)) {
1360 goto out;
1364 * ensure cacl_set_cb gets called for children
1365 * of this directory (targetpath)
1367 status = cli_list(targetcli, targetpath,
1368 attribute, cacl_set_cb,
1369 (void *)&dir_cbstate);
1371 if (!NT_STATUS_IS_OK(status)) {
1372 goto out;
1375 } else {
1377 * build full path to caclfile and replace '/' with '\' so
1378 * other utility functions can deal with it
1381 targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1382 if (!targetpath) {
1383 status = NT_STATUS_NO_MEMORY;
1384 goto out;
1386 string_replace(targetpath, '/', '\\');
1388 /* attempt to propagate any inherited ace to file caclfile */
1389 status = propagate_inherited_aces(targetpath, cbstate);
1391 if (!NT_STATUS_IS_OK(status)) {
1392 goto out;
1395 status = NT_STATUS_OK;
1396 out:
1397 if (!NT_STATUS_IS_OK(status)) {
1398 d_printf("error %s: processing %s\n",
1399 nt_errstr(status),
1400 targetpath);
1402 TALLOC_FREE(dirctx);
1403 return status;
1408 * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1409 * helper callback function 'cacl_set_cb' handles the child elements processed
1410 * by cli_list.
1412 static int inheritance_cacl_set(char *filename,
1413 struct cacl_callback_state *cbstate)
1415 int result;
1416 NTSTATUS ntstatus;
1417 int fileattr;
1418 char *mask = NULL;
1419 struct cli_state *cli = cbstate->cli;
1420 TALLOC_CTX *ctx = NULL;
1421 bool isdirectory = false;
1422 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1423 | FILE_ATTRIBUTE_HIDDEN;
1424 ctx = talloc_init("inherit_set");
1425 if (ctx == NULL) {
1426 d_printf("out of memory\n");
1427 result = EXIT_FAILED;
1428 goto out;
1431 /* ensure we have a filename that starts with '\' */
1432 if (!filename || *filename != DIRSEP_CHAR) {
1433 /* illegal or no filename */
1434 result = EXIT_FAILED;
1435 d_printf("illegal or missing name '%s'\n", filename);
1436 goto out;
1440 fileattr = get_fileinfo(cli, filename);
1441 isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1442 == FILE_ATTRIBUTE_DIRECTORY;
1445 * if we've got as far as here then we have already evaluated
1446 * the args.
1448 if (test_args) {
1449 result = EXIT_OK;
1450 goto out;
1453 mask = NULL;
1454 /* make sure we have a trailing '\*' for directory */
1455 if (!isdirectory) {
1456 mask = talloc_strdup(ctx, filename);
1457 } else if (strlen(filename) > 1) {
1459 * if the passed file name doesn't have a trailing '\'
1460 * append it.
1462 char *name_end = strrchr(filename, DIRSEP_CHAR);
1463 if (name_end != filename + strlen(filename) + 1) {
1464 mask = talloc_asprintf(ctx, "%s\\*", filename);
1465 } else {
1466 mask = talloc_strdup(ctx, filename);
1468 } else {
1469 /* filename is a single '\', just append '*' */
1470 mask = talloc_asprintf_append(mask, "%s*", filename);
1473 if (!mask) {
1474 result = EXIT_FAILED;
1475 goto out;
1479 * prepare for automatic propagation of the acl passed on the
1480 * cmdline.
1483 ntstatus = prepare_inheritance_propagation(ctx, filename,
1484 cbstate);
1485 if (!NT_STATUS_IS_OK(ntstatus)) {
1486 d_printf("error: %s processing %s\n",
1487 nt_errstr(ntstatus), filename);
1488 result = EXIT_FAILED;
1489 goto out;
1492 result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1493 cbstate->mode, cbstate->numeric);
1496 * strictly speaking it could be considered an error if a file was
1497 * specified with '--propagate-inheritance'. However we really want
1498 * to eventually get rid of '--propagate-inheritance' so we will be
1499 * more forgiving here and instead just exit early.
1501 if (!isdirectory || (result != EXIT_OK)) {
1502 goto out;
1505 /* check if there is actually any need to propagate */
1506 if (cbstate->acl_no_propagate) {
1507 goto out;
1509 /* get inheritable attributes this parent container (e.g. filename) */
1510 ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1511 if (NT_STATUS_IS_OK(ntstatus)) {
1512 /* process children */
1513 ntstatus = cli_list(cli, mask, attribute,
1514 cacl_set_cb,
1515 (void *)cbstate);
1518 if (!NT_STATUS_IS_OK(ntstatus)) {
1519 d_printf("error: %s processing %s\n",
1520 nt_errstr(ntstatus), filename);
1521 result = EXIT_FAILED;
1522 goto out;
1525 out:
1526 TALLOC_FREE(ctx);
1527 return result;
1530 struct diritem {
1531 struct diritem *prev, *next;
1533 * dirname and targetpath below are sanitized,
1534 * e.g.
1535 * + start and end with '\'
1536 * + have no trailing '*'
1537 * + all '/' have been converted to '\'
1539 char *dirname;
1540 char *targetpath;
1541 struct cli_state *targetcli;
1544 struct save_restore_stats
1546 int success;
1547 int failure;
1550 struct dump_context {
1551 struct diritem *list;
1552 struct cli_credentials *creds;
1553 struct cli_state *cli;
1554 struct save_restore_stats *stats;
1555 int save_fd;
1556 struct diritem *dir;
1557 NTSTATUS status;
1560 static int write_dacl(struct dump_context *ctx,
1561 struct cli_state *cli,
1562 const char *filename,
1563 const char *origfname)
1565 struct security_descriptor *sd = NULL;
1566 char *str = NULL;
1567 const char *output_fmt = "%s\r\n%s\r\n";
1568 const char *tmp = NULL;
1569 char *out_str = NULL;
1570 uint8_t *dest = NULL;
1571 ssize_t s_len;
1572 size_t d_len;
1573 bool ok;
1574 int result;
1575 TALLOC_CTX *frame = talloc_stackframe();
1577 if (test_args) {
1578 return EXIT_OK;
1581 if (ctx->save_fd < 0) {
1582 DBG_ERR("error processing %s no file descriptor\n", filename);
1583 result = EXIT_FAILED;
1584 goto out;
1587 sd = get_secdesc(cli, filename);
1588 if (sd == NULL) {
1589 result = EXIT_FAILED;
1590 goto out;
1593 sd->owner_sid = NULL;
1594 sd->group_sid = NULL;
1596 str = sddl_encode(frame, sd, get_domain_sid(cli));
1597 if (str == NULL) {
1598 DBG_ERR("error processing %s couldn't encode DACL\n", filename);
1599 result = EXIT_FAILED;
1600 goto out;
1603 * format of icacls save file is
1604 * a line containing the path of the file/dir
1605 * followed by a line containing the sddl format
1606 * of the dacl.
1607 * The format of the strings are null terminated
1608 * 16-bit Unicode. Each line is terminated by "\r\n"
1611 tmp = origfname;
1612 /* skip leading '\' */
1613 if (tmp[0] == '\\') {
1614 tmp++;
1616 out_str = talloc_asprintf(frame, output_fmt, tmp, str);
1618 if (out_str == NULL) {
1619 result = EXIT_FAILED;
1620 goto out;
1623 s_len = strlen(out_str);
1625 ok = convert_string_talloc(out_str,
1626 CH_UNIX,
1627 CH_UTF16,
1628 out_str,
1629 s_len, (void **)(void *)&dest, &d_len);
1630 if (!ok) {
1631 DBG_ERR("error processing %s out of memory\n", tmp);
1632 result = EXIT_FAILED;
1633 goto out;
1636 if (write(ctx->save_fd, dest, d_len) != d_len) {
1637 DBG_ERR("error processing %s failed to write to file.\n", tmp);
1638 result = EXIT_FAILED;
1639 goto out;
1641 fsync(ctx->save_fd);
1643 result = EXIT_OK;
1644 ctx->stats->success += 1;
1645 fprintf(stdout, "Successfully processed file: %s\n", tmp);
1646 out:
1647 TALLOC_FREE(frame);
1648 if (result != EXIT_OK) {
1649 ctx->stats->failure += 1;
1651 return result;
1655 * Sanitize directory name.
1656 * Given a directory name 'dir' ensure it;
1657 * o starts with '\'
1658 * o ends with '\'
1659 * o doesn't end with trailing '*'
1660 * o ensure all '/' are converted to '\'
1663 static char *sanitize_dirname(TALLOC_CTX *ctx,
1664 const char *dir)
1666 char *mask = NULL;
1667 char *name_end = NULL;
1669 mask = talloc_strdup(ctx, dir);
1670 name_end = strrchr(mask, '*');
1671 if (name_end) {
1672 *name_end = '\0';
1675 name_end = strrchr(mask, DIRSEP_CHAR);
1677 if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) {
1678 mask = talloc_asprintf(ctx, "%s\\", mask);
1681 string_replace(mask, '/', '\\');
1682 return mask;
1686 * Process each entry (child) of a directory.
1687 * Each entry, regardless of whether it is itself a file or directory
1688 * has it's dacl written to the restore/save file.
1689 * Each directory is saved to context->list (for further processing)
1690 * write_dacl will update the stats (success/fail)
1692 static NTSTATUS cacl_dump_dacl_cb(struct file_info *f,
1693 const char *mask, void *state)
1695 struct dump_context *ctx = talloc_get_type_abort(state,
1696 struct dump_context);
1698 NTSTATUS status;
1700 char *mask2 = NULL;
1701 char *targetpath = NULL;
1702 char *unresolved = NULL;
1705 * if we have already encountered an error
1706 * bail out
1708 if (!NT_STATUS_IS_OK(ctx->status)) {
1709 return ctx->status;
1712 if (!f->name || !f->name[0]) {
1713 DBG_ERR("Empty dir name returned. Possible server "
1714 "misconfiguration.\n");
1715 status = NT_STATUS_UNSUCCESSFUL;
1716 goto out;
1719 mask2 = sanitize_dirname(ctx, mask);
1720 if (!mask2) {
1721 status = NT_STATUS_NO_MEMORY;
1722 goto out;
1724 if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1725 struct diritem *item = NULL;
1727 /* ignore special '.' & '..' */
1728 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1729 status = NT_STATUS_OK;
1730 goto out;
1733 /* Work out the directory. */
1734 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1735 if (!unresolved) {
1736 status = NT_STATUS_NO_MEMORY;
1737 goto out;
1740 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1742 if (unresolved == NULL) {
1743 status = NT_STATUS_NO_MEMORY;
1744 goto out;
1747 item = talloc_zero(ctx, struct diritem);
1748 if (item == NULL) {
1749 status = NT_STATUS_NO_MEMORY;
1750 goto out;
1753 item->dirname = unresolved;
1755 mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1756 if (!mask2) {
1757 status = NT_STATUS_NO_MEMORY;
1758 goto out;
1761 status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli,
1762 mask2, &item->targetcli, &targetpath);
1764 if (!NT_STATUS_IS_OK(status)) {
1765 DBG_ERR("error failed to resolve: %s\n",
1766 nt_errstr(status));
1767 goto out;
1770 item->targetpath = sanitize_dirname(ctx, targetpath);
1771 if (!item->targetpath) {
1772 status = NT_STATUS_NO_MEMORY;
1773 goto out;
1776 if (write_dacl(ctx,
1777 item->targetcli,
1778 item->targetpath, unresolved) != EXIT_OK) {
1779 status = NT_STATUS_UNSUCCESSFUL;
1781 * cli_list happily ignores error encountered
1782 * when processing the callback so we need
1783 * to save any error status encountered while
1784 * processing directories (so we can stop recursing
1785 * those as soon as possible).
1786 * Changing the current behaviour of the callback
1787 * handling by cli_list would be I think be too
1788 * risky.
1790 ctx->status = status;
1791 goto out;
1794 DLIST_ADD_END(ctx->list, item);
1796 } else {
1797 unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1798 if (!unresolved) {
1799 status = NT_STATUS_NO_MEMORY;
1800 goto out;
1803 unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1805 if (!unresolved) {
1806 status = NT_STATUS_NO_MEMORY;
1807 goto out;
1810 * build full path to the file and replace '/' with '\' so
1811 * other utility functions can deal with it
1814 targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1816 if (!targetpath) {
1817 status = NT_STATUS_NO_MEMORY;
1818 goto out;
1821 if (write_dacl(ctx,
1822 ctx->dir->targetcli,
1823 targetpath, unresolved) != EXIT_OK) {
1824 status = NT_STATUS_UNSUCCESSFUL;
1826 * cli_list happily ignores error encountered
1827 * when processing the callback so we need
1828 * to save any error status encountered while
1829 * processing directories (so we can stop recursing
1830 * those as soon as possible).
1831 * Changing the current behaviour of the callback
1832 * handling by cli_list would be I think be too
1833 * risky.
1835 ctx->status = status;
1836 goto out;
1839 status = NT_STATUS_OK;
1840 out:
1841 if (!NT_STATUS_IS_OK(status)) {
1842 DBG_ERR("error %s: processing %s\n",
1843 nt_errstr(status), targetpath);
1845 return status;
1849 * dump_ctx contains a list of directories to be processed
1850 * + each directory 'dir' is scanned by cli_list, the cli_list
1851 * callback 'cacl_dump_dacl_cb' writes out the dacl of each
1852 * child of 'dir' (regardless of whether it is a dir or file)
1853 * to the restore/save file. Additionally any directories encountered
1854 * are returned in the passed in dump_ctx->list member
1855 * + the directory list returned from cli_list is passed and processed
1856 * by recursively calling dump_dacl_dirtree
1859 static int dump_dacl_dirtree(struct dump_context *dump_ctx)
1861 struct diritem *item = NULL;
1862 struct dump_context *new_dump_ctx = NULL;
1863 int result;
1864 for (item = dump_ctx->list; item; item = item->next) {
1865 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1866 | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1867 NTSTATUS status;
1868 char *mask = NULL;
1870 new_dump_ctx = talloc_zero(dump_ctx, struct dump_context);
1872 if (new_dump_ctx == NULL) {
1873 DBG_ERR("out of memory\n");
1874 result = EXIT_FAILED;
1875 goto out;
1878 if (item->targetcli == NULL) {
1879 status = cli_resolve_path(new_dump_ctx,
1881 dump_ctx->creds,
1882 dump_ctx->cli,
1883 item->dirname,
1884 &item->targetcli,
1885 &item->targetpath);
1886 if (!NT_STATUS_IS_OK(status)) {
1887 DBG_ERR("failed to resolve path %s "
1888 "error: %s\n",
1889 item->dirname, nt_errstr(status));
1890 result = EXIT_FAILED;
1891 goto out;
1894 new_dump_ctx->creds = dump_ctx->creds;
1895 new_dump_ctx->save_fd = dump_ctx->save_fd;
1896 new_dump_ctx->stats = dump_ctx->stats;
1897 new_dump_ctx->dir = item;
1898 new_dump_ctx->cli = item->targetcli;
1900 mask = talloc_asprintf(new_dump_ctx, "%s*",
1901 new_dump_ctx->dir->targetpath);
1902 status = cli_list(new_dump_ctx->dir->targetcli,
1903 mask,
1904 attribute, cacl_dump_dacl_cb, new_dump_ctx);
1906 if (!NT_STATUS_IS_OK(status) ||
1907 !NT_STATUS_IS_OK(new_dump_ctx->status)) {
1908 NTSTATUS tmpstatus;
1909 if (!NT_STATUS_IS_OK(status)) {
1911 * cli_list failed for some reason
1912 * so we need to update the failure stat
1914 new_dump_ctx->stats->failure += 1;
1915 tmpstatus = status;
1916 } else {
1917 /* cacl_dump_dacl_cb should have updated stat */
1918 tmpstatus = new_dump_ctx->status;
1920 DBG_ERR("error %s: processing %s\n",
1921 nt_errstr(tmpstatus), item->dirname);
1922 result = EXIT_FAILED;
1923 goto out;
1925 result = dump_dacl_dirtree(new_dump_ctx);
1926 if (result != EXIT_OK) {
1927 goto out;
1931 result = EXIT_OK;
1932 out:
1933 TALLOC_FREE(new_dump_ctx);
1934 return result;
1937 static int cacl_dump_dacl(struct cli_state *cli,
1938 struct cli_credentials *creds,
1939 char *filename)
1941 int fileattr;
1942 char *mask = NULL;
1943 TALLOC_CTX *ctx = NULL;
1944 bool isdirectory = false;
1945 int result;
1946 struct dump_context *dump_ctx = NULL;
1947 struct save_restore_stats stats = {0};
1948 struct diritem *item = NULL;
1949 struct cli_state *targetcli = NULL;
1950 char *targetpath = NULL;
1951 NTSTATUS status;
1953 ctx = talloc_init("cacl_dump");
1954 if (ctx == NULL) {
1955 DBG_ERR("out of memory\n");
1956 result = EXIT_FAILED;
1957 goto out;
1960 dump_ctx = talloc_zero(ctx, struct dump_context);
1961 if (dump_ctx == NULL) {
1962 DBG_ERR("out of memory\n");
1963 result = EXIT_FAILED;
1964 goto out;
1967 dump_ctx->save_fd = open(save_file,
1968 O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
1970 if (dump_ctx->save_fd < 0) {
1971 result = EXIT_FAILED;
1972 goto out;
1975 dump_ctx->creds = creds;
1976 dump_ctx->cli = cli;
1977 dump_ctx->stats = &stats;
1979 /* ensure we have a filename that starts with '\' */
1980 if (!filename || *filename != DIRSEP_CHAR) {
1981 /* illegal or no filename */
1982 result = EXIT_FAILED;
1983 DBG_ERR("illegal or missing name '%s'\n", filename);
1984 goto out;
1987 status = cli_resolve_path(dump_ctx, "",
1988 dump_ctx->creds,
1989 dump_ctx->cli,
1990 filename, &targetcli, &targetpath);
1991 if (!NT_STATUS_IS_OK(status)) {
1992 DBG_ERR("failed resolve %s\n", filename);
1993 result = EXIT_FAILED;
1994 goto out;
1997 fileattr = get_fileinfo(targetcli, targetpath);
1998 isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1999 == FILE_ATTRIBUTE_DIRECTORY;
2002 * if we've got as far as here then we have already evaluated
2003 * the args.
2005 if (test_args) {
2006 result = EXIT_OK;
2007 goto out;
2010 mask = NULL;
2011 /* make sure we have a trailing '\*' for directory */
2012 if (!isdirectory) {
2013 mask = talloc_strdup(ctx, filename);
2014 } else if (strlen(filename) > 1) {
2015 mask = sanitize_dirname(ctx, filename);
2016 } else {
2017 /* filename is a single '\' */
2018 mask = talloc_strdup(ctx, filename);
2020 if (!mask) {
2021 result = EXIT_FAILED;
2022 goto out;
2025 write_dacl(dump_ctx, targetcli, targetpath, filename);
2026 if (isdirectory && recurse) {
2027 item = talloc_zero(dump_ctx, struct diritem);
2028 if (!item) {
2029 result = EXIT_FAILED;
2030 goto out;
2032 item->dirname = mask;
2033 DLIST_ADD_END(dump_ctx->list, item);
2034 dump_dacl_dirtree(dump_ctx);
2037 fprintf(stdout, "Successfully processed %d files: "
2038 "Failed processing %d files\n",
2039 dump_ctx->stats->success, dump_ctx->stats->failure);
2040 result = EXIT_OK;
2041 out:
2042 if (dump_ctx && dump_ctx->save_fd > 0) {
2043 close(dump_ctx->save_fd);
2045 TALLOC_FREE(ctx);
2046 return result;
2049 struct restore_dacl {
2050 const char *path;
2051 struct security_descriptor *sd;
2055 * Restore dacls from 'savefile' produced by
2056 * 'icacls name /save' or 'smbcacls --save'
2058 static int cacl_restore(struct cli_state *cli,
2059 struct cli_credentials *creds,
2060 bool numeric, const char *restorefile)
2062 int restore_fd;
2063 int result;
2064 struct save_restore_stats stats = { 0 };
2066 char **lines = NULL;
2067 char *content = NULL;
2068 char *convert_content = NULL;
2069 size_t content_size;
2070 struct restore_dacl *entries = NULL;
2071 int numlines, i = 0;
2072 bool ok;
2073 struct dom_sid *sid = NULL;
2075 if (restorefile == NULL) {
2076 DBG_ERR("No restore file specified\n");
2077 result = EXIT_FAILED;
2078 goto out;
2081 if (test_args) {
2082 result = EXIT_OK;
2083 goto out;
2086 restore_fd = open(restorefile, O_RDONLY, S_IRUSR | S_IWUSR);
2087 if (restore_fd < 0) {
2088 DBG_ERR("Failed to open %s.\n", restorefile);
2089 result = EXIT_FAILED;
2090 goto out;
2093 content = fd_load(restore_fd, &content_size, 0, talloc_tos());
2095 close(restore_fd);
2097 if (content == NULL) {
2098 DBG_ERR("Failed to load content from %s.\n", restorefile);
2099 result = EXIT_FAILED;
2100 goto out;
2103 ok = convert_string_talloc(talloc_tos(),
2104 CH_UTF16,
2105 CH_UNIX,
2106 content,
2107 utf16_len_n(content, content_size),
2108 (void **)(void *)&convert_content,
2109 &content_size);
2111 TALLOC_FREE(content);
2113 if (!ok) {
2114 DBG_ERR("Failed to convert content from %s "
2115 "to CH_UNIX.\n", restorefile);
2116 result = EXIT_FAILED;
2117 goto out;
2120 lines = file_lines_parse(convert_content,
2121 content_size, &numlines, talloc_tos());
2123 if (lines == NULL) {
2124 DBG_ERR("Failed to parse lines from content of %s.",
2125 restorefile);
2126 result = EXIT_FAILED;
2127 goto out;
2130 entries = talloc_zero_array(lines, struct restore_dacl, numlines / 2);
2132 if (entries == NULL) {
2133 DBG_ERR("error processing %s, out of memory\n", restorefile);
2134 result = EXIT_FAILED;
2135 goto out;
2138 sid = get_domain_sid(cli);
2140 while (i < numlines) {
2141 int index = i / 2;
2142 int first_line = (i % 2) == 0;
2144 if (first_line) {
2145 char *tmp = NULL;
2146 tmp = lines[i];
2147 /* line can be blank if root of share */
2148 if (strlen(tmp) == 0) {
2149 entries[index].path = talloc_strdup(lines,
2150 "\\");
2151 } else {
2152 entries[index].path = lines[i];
2154 } else {
2155 const char *msg = NULL;
2156 size_t msg_offset = 0;
2157 enum ace_condition_flags flags =
2158 ACE_CONDITION_FLAG_ALLOW_DEVICE;
2159 entries[index].sd = sddl_decode_err_msg(lines,
2160 lines[i],
2161 sid,
2162 flags,
2163 &msg,
2164 &msg_offset);
2165 if(entries[index].sd == NULL) {
2166 DBG_ERR("could not decode '%s'\n", lines[i]);
2167 if (msg != NULL) {
2168 DBG_ERR(" %*c\n",
2169 (int)msg_offset, '^');
2170 DBG_ERR("error '%s'\n", msg);
2172 result = EXIT_FAILED;
2173 goto out;
2175 entries[index].sd->type |=
2176 SEC_DESC_DACL_AUTO_INHERIT_REQ;
2177 entries[index].sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
2179 i++;
2181 for (i = 0; i < (numlines / 2); i++) {
2182 int mode = SMB_ACL_SET;
2183 int set_result;
2184 struct cli_state *targetcli = NULL;
2185 char *targetpath = NULL;
2186 NTSTATUS status;
2188 /* check for dfs */
2189 status = cli_resolve_path(talloc_tos(),
2191 creds,
2192 cli,
2193 entries[i].path,
2194 &targetcli, &targetpath);
2196 if (!NT_STATUS_IS_OK(status)) {
2197 printf("Error failed to process file: %s\n",
2198 entries[i].path);
2199 stats.failure += 1;
2200 continue;
2203 set_result = cacl_set_from_sd(targetcli,
2204 targetpath,
2205 entries[i].sd, mode, numeric);
2207 if (set_result == EXIT_OK) {
2208 printf("Successfully processed file: %s\n",
2209 entries[i].path);
2210 stats.success += 1;
2211 } else {
2212 printf("Error failed to process file: %s\n",
2213 entries[i].path);
2214 stats.failure += 1;
2218 result = EXIT_OK;
2219 out:
2220 TALLOC_FREE(lines);
2221 fprintf(stdout, "Successfully processed %d files: "
2222 "Failed processing %d files\n", stats.success, stats.failure);
2223 return result;
2226 /****************************************************************************
2227 main program
2228 ****************************************************************************/
2229 int main(int argc, char *argv[])
2231 const char **argv_const = discard_const_p(const char *, argv);
2232 char *share;
2233 int opt;
2234 enum acl_mode mode = SMB_ACL_SET;
2235 static char *the_acl = NULL;
2236 enum chown_mode change_mode = REQUEST_NONE;
2237 int result;
2238 char *path;
2239 char *filename = NULL;
2240 poptContext pc;
2241 /* numeric is set when the user wants numeric SIDs and ACEs rather
2242 than going via LSA calls to resolve them */
2243 int numeric = 0;
2244 struct cli_state *targetcli = NULL;
2245 struct cli_credentials *creds = NULL;
2246 char *targetfile = NULL;
2247 NTSTATUS status;
2248 bool ok;
2249 struct loadparm_context *lp_ctx = NULL;
2251 struct poptOption long_options[] = {
2252 POPT_AUTOHELP
2254 .longName = "delete",
2255 .shortName = 'D',
2256 .argInfo = POPT_ARG_STRING,
2257 .arg = NULL,
2258 .val = 'D',
2259 .descrip = "Delete an acl",
2260 .argDescrip = "ACL",
2263 .longName = "modify",
2264 .shortName = 'M',
2265 .argInfo = POPT_ARG_STRING,
2266 .arg = NULL,
2267 .val = 'M',
2268 .descrip = "Modify an acl",
2269 .argDescrip = "ACL",
2272 .longName = "add",
2273 .shortName = 'a',
2274 .argInfo = POPT_ARG_STRING,
2275 .arg = NULL,
2276 .val = 'a',
2277 .descrip = "Add an acl",
2278 .argDescrip = "ACL",
2281 .longName = "set",
2282 .shortName = 'S',
2283 .argInfo = POPT_ARG_STRING,
2284 .arg = NULL,
2285 .val = 'S',
2286 .descrip = "Set acls",
2287 .argDescrip = "ACLS",
2290 .longName = "chown",
2291 .shortName = 'C',
2292 .argInfo = POPT_ARG_STRING,
2293 .arg = NULL,
2294 .val = 'C',
2295 .descrip = "Change ownership of a file",
2296 .argDescrip = "USERNAME",
2299 .longName = "chgrp",
2300 .shortName = 'G',
2301 .argInfo = POPT_ARG_STRING,
2302 .arg = NULL,
2303 .val = 'G',
2304 .descrip = "Change group ownership of a file",
2305 .argDescrip = "GROUPNAME",
2308 .longName = "inherit",
2309 .shortName = 'I',
2310 .argInfo = POPT_ARG_STRING,
2311 .arg = NULL,
2312 .val = 'I',
2313 .descrip = "Inherit allow|remove|copy",
2316 .longName = "propagate-inheritance",
2317 .shortName = 0,
2318 .argInfo = POPT_ARG_NONE,
2319 .arg = &inheritance,
2320 .val = 1,
2321 .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
2324 .longName = "save",
2325 .shortName = 0,
2326 .argInfo = POPT_ARG_STRING,
2327 .arg = &save_file,
2328 .val = 1,
2329 .descrip = "stores the DACLs in sddl format of the "
2330 "specified file or folder for later use "
2331 "with restore. SACLS, owner or integrity"
2332 " labels are not stored",
2335 .longName = "restore",
2336 .shortName = 0,
2337 .argInfo = POPT_ARG_STRING,
2338 .arg = &restore_file,
2339 .val = 1,
2340 .descrip = "applies the stored DACLS to files in "
2341 "directory.",
2344 .longName = "recurse",
2345 .shortName = 0,
2346 .argInfo = POPT_ARG_NONE,
2347 .arg = &recurse,
2348 .val = 1,
2349 .descrip = "indicates the operation is performed "
2350 "on directory and all files/directories"
2351 " below. (only applies to save option)",
2354 .longName = "numeric",
2355 .shortName = 0,
2356 .argInfo = POPT_ARG_NONE,
2357 .arg = &numeric,
2358 .val = 1,
2359 .descrip = "Don't resolve sids or masks to names",
2362 .longName = "sddl",
2363 .shortName = 0,
2364 .argInfo = POPT_ARG_NONE,
2365 .arg = &sddl,
2366 .val = 1,
2367 .descrip = "Output and input acls in sddl format",
2370 .longName = "query-security-info",
2371 .shortName = 0,
2372 .argInfo = POPT_ARG_INT,
2373 .arg = &query_sec_info,
2374 .val = 1,
2375 .descrip = "The security-info flags for queries"
2378 .longName = "set-security-info",
2379 .shortName = 0,
2380 .argInfo = POPT_ARG_INT,
2381 .arg = &set_sec_info,
2382 .val = 1,
2383 .descrip = "The security-info flags for modifications"
2386 .longName = "test-args",
2387 .shortName = 't',
2388 .argInfo = POPT_ARG_NONE,
2389 .arg = &test_args,
2390 .val = 1,
2391 .descrip = "Test arguments"
2394 .longName = "domain-sid",
2395 .shortName = 0,
2396 .argInfo = POPT_ARG_STRING,
2397 .arg = &domain_sid,
2398 .val = 0,
2399 .descrip = "Domain SID for sddl",
2400 .argDescrip = "SID"},
2402 .longName = "maximum-access",
2403 .shortName = 'x',
2404 .argInfo = POPT_ARG_NONE,
2405 .arg = NULL,
2406 .val = 'x',
2407 .descrip = "Query maximum permissions",
2409 POPT_COMMON_SAMBA
2410 POPT_COMMON_CONNECTION
2411 POPT_COMMON_CREDENTIALS
2412 POPT_LEGACY_S3
2413 POPT_COMMON_VERSION
2414 POPT_TABLEEND
2417 struct cli_state *cli;
2418 TALLOC_CTX *frame = talloc_stackframe();
2419 const char *owner_username = "";
2420 char *server;
2422 smb_init_locale();
2424 ok = samba_cmdline_init(frame,
2425 SAMBA_CMDLINE_CONFIG_CLIENT,
2426 false /* require_smbconf */);
2427 if (!ok) {
2428 DBG_ERR("Failed to init cmdline parser!\n");
2429 TALLOC_FREE(frame);
2430 exit(1);
2432 lp_ctx = samba_cmdline_get_lp_ctx();
2433 /* set default debug level to 1 regardless of what smb.conf sets */
2434 lpcfg_set_cmdline(lp_ctx, "log level", "1");
2436 setlinebuf(stdout);
2438 pc = samba_popt_get_context(getprogname(),
2439 argc,
2440 argv_const,
2441 long_options,
2443 if (pc == NULL) {
2444 DBG_ERR("Failed to setup popt context!\n");
2445 TALLOC_FREE(frame);
2446 exit(1);
2449 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
2450 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
2452 while ((opt = poptGetNextOpt(pc)) != -1) {
2453 switch (opt) {
2454 case 'S':
2455 the_acl = smb_xstrdup(poptGetOptArg(pc));
2456 mode = SMB_ACL_SET;
2457 break;
2459 case 'D':
2460 the_acl = smb_xstrdup(poptGetOptArg(pc));
2461 mode = SMB_ACL_DELETE;
2462 break;
2464 case 'M':
2465 the_acl = smb_xstrdup(poptGetOptArg(pc));
2466 mode = SMB_ACL_MODIFY;
2467 break;
2469 case 'a':
2470 the_acl = smb_xstrdup(poptGetOptArg(pc));
2471 mode = SMB_ACL_ADD;
2472 break;
2474 case 'C':
2475 owner_username = poptGetOptArg(pc);
2476 change_mode = REQUEST_CHOWN;
2477 break;
2479 case 'G':
2480 owner_username = poptGetOptArg(pc);
2481 change_mode = REQUEST_CHGRP;
2482 break;
2484 case 'I':
2485 owner_username = poptGetOptArg(pc);
2486 change_mode = REQUEST_INHERIT;
2487 break;
2488 case 'm':
2489 lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
2490 break;
2491 case 'x':
2492 want_mxac = true;
2493 break;
2494 case POPT_ERROR_BADOPT:
2495 fprintf(stderr, "\nInvalid option %s: %s\n\n",
2496 poptBadOption(pc, 0), poptStrerror(opt));
2497 poptPrintUsage(pc, stderr, 0);
2498 exit(1);
2501 if (inheritance && !the_acl) {
2502 poptPrintUsage(pc, stderr, 0);
2503 return -1;
2506 if(!poptPeekArg(pc)) {
2507 poptPrintUsage(pc, stderr, 0);
2508 return -1;
2511 path = talloc_strdup(frame, poptGetArg(pc));
2512 if (!path) {
2513 return -1;
2516 if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
2517 printf("Invalid argument: %s\n", path);
2518 return -1;
2521 if(!poptPeekArg(pc)) {
2522 poptPrintUsage(pc, stderr, 0);
2523 return -1;
2526 filename = talloc_strdup(frame, poptGetArg(pc));
2527 if (!filename) {
2528 return -1;
2531 poptFreeContext(pc);
2532 samba_cmdline_burn(argc, argv);
2534 string_replace(path,'/','\\');
2536 server = talloc_strdup(frame, path+2);
2537 if (!server) {
2538 return -1;
2540 share = strchr_m(server,'\\');
2541 if (share == NULL) {
2542 printf("Invalid argument\n");
2543 return -1;
2546 *share = 0;
2547 share++;
2549 creds = samba_cmdline_get_creds();
2551 /* Make connection to server */
2552 if (!test_args) {
2553 cli = connect_one(creds, server, share);
2554 if (!cli) {
2555 exit(EXIT_FAILED);
2557 } else {
2558 exit(0);
2561 string_replace(filename, '/', '\\');
2562 if (filename[0] != '\\') {
2563 filename = talloc_asprintf(frame,
2564 "\\%s",
2565 filename);
2566 if (!filename) {
2567 return -1;
2571 status = cli_resolve_path(frame,
2573 creds,
2574 cli,
2575 filename,
2576 &targetcli,
2577 &targetfile);
2578 if (!NT_STATUS_IS_OK(status)) {
2579 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
2580 return -1;
2583 /* Perform requested action */
2585 if (change_mode == REQUEST_INHERIT) {
2586 result = inherit(targetcli, targetfile, owner_username);
2587 } else if (change_mode != REQUEST_NONE) {
2588 result = owner_set(targetcli, change_mode, targetfile, owner_username);
2589 } else if (the_acl) {
2590 if (inheritance) {
2591 struct cacl_callback_state cbstate = {
2592 .creds = creds,
2593 .cli = targetcli,
2594 .mode = mode,
2595 .the_acl = the_acl,
2596 .numeric = numeric,
2598 result = inheritance_cacl_set(targetfile, &cbstate);
2599 } else {
2600 result = cacl_set(targetcli,
2601 targetfile,
2602 the_acl,
2603 mode,
2604 numeric);
2606 } else {
2607 if (save_file || restore_file) {
2608 sddl = 1;
2609 if (save_file) {
2610 result = cacl_dump_dacl(cli, creds, filename);
2611 } else {
2612 result = cacl_restore(targetcli,
2613 creds,
2614 numeric, restore_file);
2616 } else {
2617 result = cacl_dump(targetcli, targetfile, numeric);
2621 gfree_all();
2622 TALLOC_FREE(frame);
2624 return result;