2 Unix SMB/CIFS implementation.
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/>.
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"
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
;
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
;
64 bool acl_no_propagate
;
68 static NTSTATUS
cli_lsa_lookup_domain_sid(struct cli_state
*cli
,
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
)) {
88 status
= cli_rpc_pipe_open_noauth(cli
, &ndr_table_lsarpc
, &rpc_pipe
);
89 if (!NT_STATUS_IS_OK(status
)) {
93 status
= rpccli_lsa_open_policy(rpc_pipe
, frame
, True
,
94 GENERIC_EXECUTE_ACCESS
, &handle
);
95 if (!NT_STATUS_IS_OK(status
)) {
99 status
= dcerpc_lsa_QueryInfoPolicy2(rpc_pipe
->binding_handle
,
101 LSA_POLICY_INFO_DOMAIN
,
104 if (any_nt_status_not_ok(status
, result
, &status
)) {
108 *sid
= *info
->domain
.sid
;
111 TALLOC_FREE(rpc_pipe
);
114 cli_state_restore_tcon_share(cli
, orig_tcon
, orig_share
);
119 static struct dom_sid
*get_domain_sid(struct cli_state
*cli
)
122 struct dom_sid_buf buf
;
124 struct dom_sid
*sid
= talloc(talloc_tos(), struct dom_sid
);
126 DEBUG(0, ("Out of memory\n"));
131 if (!dom_sid_parse(domain_sid
, sid
)) {
132 DEBUG(0,("failed to parse domain sid\n"));
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
)));
145 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid
, &buf
)));
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
;
157 acl
= make_sec_acl(ctx
, 3, 0, NULL
);
163 if (acl
->num_aces
== UINT32_MAX
) {
167 acl
, struct security_ace
, *ace
, &acl
->aces
, &acl
->num_aces
);
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
)
182 struct security_descriptor
*ret
= NULL
;
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
;
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);
197 if (strncmp(tok
,"OWNER:", 6) == 0) {
199 printf("Only specify owner once\n");
202 if (!StringToSid(cli
, &owner_sid
, tok
+6)) {
203 printf("Failed to parse owner sid\n");
210 if (strncmp(tok
,"GROUP:", 6) == 0) {
212 printf("Only specify group once\n");
215 if (!StringToSid(cli
, &group_sid
, tok
+6)) {
216 printf("Failed to parse group sid\n");
223 if (strncmp(tok
,"ACL:", 4) == 0) {
224 struct security_ace ace
;
225 if (!parse_ace(cli
, &ace
, tok
+4)) {
228 if(!add_ace(&dacl
, &ace
)) {
229 printf("Failed to add ACL %s\n", tok
);
235 printf("Failed to parse token '%s' in security descriptor,\n", tok
);
242 SEC_DESC_SELF_RELATIVE
,
243 have_owner
? &owner_sid
: NULL
,
244 have_group
? &group_sid
: NULL
,
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;
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(
267 filename
, /* fname */
269 READ_CONTROL_ACCESS
, /* CreatFlags */
270 0, /* FileAttributes */
272 FILE_SHARE_WRITE
, /* ShareAccess */
273 FILE_OPEN
, /* CreateDisposition */
274 0x0, /* CreateOptions */
275 0x0, /* SecurityFlags */
278 if (!NT_STATUS_IS_OK(status
)) {
279 printf("Failed to open %s: %s\n", filename
, nt_errstr(status
));
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
;
298 uint32_t desired_access
= 0;
300 if (query_sec_info
== -1) {
301 sec_info
= SECINFO_OWNER
| SECINFO_GROUP
| SECINFO_DACL
;
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
));
325 status
= cli_query_security_descriptor(cli
, fnum
, sec_info
,
328 cli_close(cli
, fnum
);
330 if (!NT_STATUS_IS_OK(status
)) {
331 printf("Failed to get security descriptor: %s\n",
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;
352 uint32_t desired_access
= 0;
355 if (set_sec_info
== -1) {
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
;
365 sec_info
|= SECINFO_OWNER
;
368 sec_info
|= SECINFO_GROUP
;
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,
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
));
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",
401 cli_close(cli
, fnum
);
405 /*****************************************************
406 get maximum access for a file
407 *******************************************************/
408 static int cacl_mxac(struct cli_state
*cli
, const char *filename
)
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
));
419 printf("Maximum access: 0x%x\n", mxac
);
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
;
437 sd
= get_secdesc(cli
, filename
);
443 char *str
= sddl_encode(talloc_tos(), sd
, get_domain_sid(cli
));
450 sec_desc_print(cli
, stdout
, sd
, numeric
);
454 ret
= cacl_mxac(cli
, filename
);
455 if (ret
!= 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
)
472 struct security_descriptor
*sd
;
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
)) {
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
))
506 if ((ace1
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) &&
507 !(ace2
->flags
& SEC_ACE_FLAG_INHERITED_ACE
))
509 if (!(ace1
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) &&
510 (ace2
->flags
& SEC_ACE_FLAG_INHERITED_ACE
))
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
)
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
])) {
546 the_acl
->aces
, i
, the_acl
->num_aces
);
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
,
562 struct security_descriptor
*old
= NULL
;
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
);
582 /* the logic here is rather more complex than I would like */
585 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
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
])) {
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
--;
602 printf("ACL for ACE:");
603 print_ace(cli
, stdout
, &sd
->dacl
->aces
[i
],
605 printf(" not found\n");
611 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
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
];
625 SidToString(cli
, str
,
626 &sd
->dacl
->aces
[i
].trustee
,
628 printf("ACL for SID %s not found\n", str
);
633 old
->owner_sid
= sd
->owner_sid
;
637 old
->group_sid
= sd
->group_sid
;
643 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
644 add_ace(&old
->dacl
, &sd
->dacl
->aces
[i
]);
653 /* Denied ACE entries must come before allowed ones */
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
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
;
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
;
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(),
697 DBG_ERR("could not decode '%s'\n", the_acl
);
700 (int)msg_offset
, '^');
701 DBG_ERR("error '%s'\n", msg
);
705 sd
= sec_desc_parse(talloc_tos(), cli
, the_acl
);
709 return EXIT_PARSE_ERROR
;
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
,
723 struct security_descriptor
*old
,*sd
;
726 int result
= EXIT_OK
;
728 old
= get_secdesc(cli
, filename
);
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
) {
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
)) {
751 string_replace(parentname
, '/', '\\');
752 parent
= get_secdesc(cli
,parentname
);
753 if (parent
== NULL
) {
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
);
767 if ((ace
->flags
& SEC_ACE_FLAG_OBJECT_INHERIT
) ==
768 SEC_ACE_FLAG_OBJECT_INHERIT
) {
769 /* clear flags for files */
771 add_ace(&old
->dacl
, ace
);
776 printf("Already set to inheritable permissions.\n");
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. */
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
);
799 printf("Already set to no inheritable permissions.\n");
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
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
);
820 printf("Already set to no inheritable permissions.\n");
825 /* Denied ACE entries must come before allowed ones */
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
;
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
;
849 nt_status
= cli_full_connection_creds(talloc_tos(),
859 if (!NT_STATUS_IS_OK(nt_status
)) {
860 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status
)));
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
)
877 mask2
= talloc_strdup(ctx
, mask
);
881 p
= strrchr_m(mask2
, DIRSEP_CHAR
);
887 mask2
= talloc_asprintf_append(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
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
944 if ((prohibit_inheritance
946 && !acl_cntrinherit
) == false) {
947 newflags
|= SEC_ACE_FLAG_INHERITED_ACE
;
951 * don't apply object/container inheritance flags to
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
);
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
;
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
;
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
);
1003 status
= NT_STATUS_UNSUCCESSFUL
;
1007 /* inhibit propagation? */
1008 if ((old
->type
& SEC_DESC_DACL_PROTECTED
) ==
1009 SEC_DESC_DACL_PROTECTED
){
1010 status
= NT_STATUS_OK
;
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
;
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
])) {
1038 for (k
= j
; k
< old
->dacl
->num_aces
-1;
1040 old
->dacl
->aces
[k
] =
1041 old
->dacl
->aces
[k
+1];
1043 old
->dacl
->num_aces
--;
1049 /* propagate any inheritable ace to be added */
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
;
1057 /* don't propagate flags to a file unless OI */
1058 if (!is_objectinherit
&& !is_container
) {
1062 * adjust flags according to inheritance
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
) {
1073 if (!add_ace_with_ctx(aclctx
, &old
->dacl
, &ace
)) {
1074 status
= NT_STATUS_NO_MEMORY
;
1080 result
= cacl_set_from_sd(cbstate
->cli
, caclfile
,
1082 SMB_ACL_SET
, cbstate
->numeric
);
1083 if (result
!= EXIT_OK
) {
1084 status
= NT_STATUS_UNSUCCESSFUL
;
1088 status
= NT_STATUS_OK
;
1090 TALLOC_FREE(aclctx
);
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
)) {
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
1114 static NTSTATUS
prepare_inheritance_propagation(TALLOC_CTX
*ctx
, char *filename
,
1115 struct cacl_callback_state
*cbstate
)
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
;
1124 bool propagate
= false;
1126 old
= get_secdesc_with_ctx(ctx
, cli
, filename
);
1128 return NT_STATUS_NO_MEMORY
;
1131 /* parse acl passed on the command line */
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
,
1140 get_domain_sid(cli
),
1144 if (cbstate
->aclsd
== NULL
) {
1145 DBG_ERR("could not decode '%s'\n", the_acl
);
1148 (int)msg_offset
, '^');
1149 DBG_ERR("error '%s'\n", msg
);
1153 cbstate
->aclsd
= sec_desc_parse(ctx
, cli
, the_acl
);
1156 if (!cbstate
->aclsd
) {
1157 result
= NT_STATUS_UNSUCCESSFUL
;
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
;
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
;
1184 if (is_inheritable_ace(ace
)) {
1190 result
= NT_STATUS_OK
;
1192 cbstate
->acl_no_propagate
= !propagate
;
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
)
1207 struct cli_state
*cli
= NULL
;
1208 struct security_descriptor
*sd
= NULL
;
1209 struct security_acl
*acl_to_add
= NULL
;
1213 sd
= get_secdesc_with_ctx(ctx
, cli
, filename
);
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
);
1231 result
= NT_STATUS_NO_MEMORY
;
1236 cbstate
->acl_to_add
= acl_to_add
;
1237 result
= NT_STATUS_OK
;
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
;
1258 struct cli_state
*targetcli
= NULL
;
1261 char *dir_end
= NULL
;
1263 char *targetpath
= NULL
;
1264 char *caclfile
= NULL
;
1266 dirctx
= talloc_new(NULL
);
1268 status
= NT_STATUS_NO_MEMORY
;
1273 creds
= cbstate
->creds
;
1275 /* Work out the directory. */
1276 dir
= talloc_strdup(dirctx
, mask
);
1278 status
= NT_STATUS_NO_MEMORY
;
1282 dir_end
= strrchr(dir
, DIRSEP_CHAR
);
1283 if (dir_end
!= NULL
) {
1287 if (!f
->name
|| !f
->name
[0]) {
1288 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1289 status
= NT_STATUS_UNSUCCESSFUL
;
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
;
1300 /* ignore special '.' & '..' */
1301 if ((f
->name
== NULL
) || ISDOT(f
->name
) || ISDOTDOT(f
->name
)) {
1302 status
= NT_STATUS_OK
;
1306 mask2
= build_dirname(dirctx
, mask
, dir
, f
->name
);
1307 if (mask2
== NULL
) {
1308 status
= NT_STATUS_NO_MEMORY
;
1313 status
= cli_resolve_path(dirctx
, "", creds
, cli
,
1314 mask2
, &targetcli
, &targetpath
);
1315 if (!NT_STATUS_IS_OK(status
)) {
1320 * prepare path to caclfile, remove any existing wildcard
1321 * chars and convert path separators.
1324 caclfile
= talloc_strdup(dirctx
, targetpath
);
1326 status
= NT_STATUS_NO_MEMORY
;
1329 dir_end
= strrchr(caclfile
, '*');
1330 if (dir_end
!= NULL
) {
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
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
)) {
1354 * get inheritable ace(s) for this dir/container
1355 * that will be propagated to its children
1357 status
= get_inheritable_aces(dirctx
, caclfile
,
1359 if (!NT_STATUS_IS_OK(status
)) {
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
)) {
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
);
1383 status
= NT_STATUS_NO_MEMORY
;
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
)) {
1395 status
= NT_STATUS_OK
;
1397 if (!NT_STATUS_IS_OK(status
)) {
1398 d_printf("error %s: processing %s\n",
1402 TALLOC_FREE(dirctx
);
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
1412 static int inheritance_cacl_set(char *filename
,
1413 struct cacl_callback_state
*cbstate
)
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");
1426 d_printf("out of memory\n");
1427 result
= EXIT_FAILED
;
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
);
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
1454 /* make sure we have a trailing '\*' for directory */
1456 mask
= talloc_strdup(ctx
, filename
);
1457 } else if (strlen(filename
) > 1) {
1459 * if the passed file name doesn't have a trailing '\'
1462 char *name_end
= strrchr(filename
, DIRSEP_CHAR
);
1463 if (name_end
!= filename
+ strlen(filename
) + 1) {
1464 mask
= talloc_asprintf(ctx
, "%s\\*", filename
);
1466 mask
= talloc_strdup(ctx
, filename
);
1469 /* filename is a single '\', just append '*' */
1470 mask
= talloc_asprintf_append(mask
, "%s*", filename
);
1474 result
= EXIT_FAILED
;
1479 * prepare for automatic propagation of the acl passed on the
1483 ntstatus
= prepare_inheritance_propagation(ctx
, filename
,
1485 if (!NT_STATUS_IS_OK(ntstatus
)) {
1486 d_printf("error: %s processing %s\n",
1487 nt_errstr(ntstatus
), filename
);
1488 result
= EXIT_FAILED
;
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
)) {
1505 /* check if there is actually any need to propagate */
1506 if (cbstate
->acl_no_propagate
) {
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
,
1518 if (!NT_STATUS_IS_OK(ntstatus
)) {
1519 d_printf("error: %s processing %s\n",
1520 nt_errstr(ntstatus
), filename
);
1521 result
= EXIT_FAILED
;
1531 struct diritem
*prev
, *next
;
1533 * dirname and targetpath below are sanitized,
1535 * + start and end with '\'
1536 * + have no trailing '*'
1537 * + all '/' have been converted to '\'
1541 struct cli_state
*targetcli
;
1544 struct save_restore_stats
1550 struct dump_context
{
1551 struct diritem
*list
;
1552 struct cli_credentials
*creds
;
1553 struct cli_state
*cli
;
1554 struct save_restore_stats
*stats
;
1556 struct diritem
*dir
;
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
;
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
;
1575 TALLOC_CTX
*frame
= talloc_stackframe();
1581 if (ctx
->save_fd
< 0) {
1582 DBG_ERR("error processing %s no file descriptor\n", filename
);
1583 result
= EXIT_FAILED
;
1587 sd
= get_secdesc(cli
, filename
);
1589 result
= EXIT_FAILED
;
1593 sd
->owner_sid
= NULL
;
1594 sd
->group_sid
= NULL
;
1596 str
= sddl_encode(frame
, sd
, get_domain_sid(cli
));
1598 DBG_ERR("error processing %s couldn't encode DACL\n", filename
);
1599 result
= EXIT_FAILED
;
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
1607 * The format of the strings are null terminated
1608 * 16-bit Unicode. Each line is terminated by "\r\n"
1612 /* skip leading '\' */
1613 if (tmp
[0] == '\\') {
1616 out_str
= talloc_asprintf(frame
, output_fmt
, tmp
, str
);
1618 if (out_str
== NULL
) {
1619 result
= EXIT_FAILED
;
1623 s_len
= strlen(out_str
);
1625 ok
= convert_string_talloc(out_str
,
1629 s_len
, (void **)(void *)&dest
, &d_len
);
1631 DBG_ERR("error processing %s out of memory\n", tmp
);
1632 result
= EXIT_FAILED
;
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
;
1641 fsync(ctx
->save_fd
);
1644 ctx
->stats
->success
+= 1;
1645 fprintf(stdout
, "Successfully processed file: %s\n", tmp
);
1648 if (result
!= EXIT_OK
) {
1649 ctx
->stats
->failure
+= 1;
1655 * Sanitize directory name.
1656 * Given a directory name 'dir' ensure it;
1659 * o doesn't end with trailing '*'
1660 * o ensure all '/' are converted to '\'
1663 static char *sanitize_dirname(TALLOC_CTX
*ctx
,
1667 char *name_end
= NULL
;
1669 mask
= talloc_strdup(ctx
, dir
);
1670 name_end
= strrchr(mask
, '*');
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
, '/', '\\');
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
);
1701 char *targetpath
= NULL
;
1702 char *unresolved
= NULL
;
1705 * if we have already encountered an error
1708 if (!NT_STATUS_IS_OK(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
;
1719 mask2
= sanitize_dirname(ctx
, mask
);
1721 status
= NT_STATUS_NO_MEMORY
;
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
;
1733 /* Work out the directory. */
1734 unresolved
= sanitize_dirname(ctx
, ctx
->dir
->dirname
);
1736 status
= NT_STATUS_NO_MEMORY
;
1740 unresolved
= talloc_asprintf(ctx
, "%s%s", unresolved
, f
->name
);
1742 if (unresolved
== NULL
) {
1743 status
= NT_STATUS_NO_MEMORY
;
1747 item
= talloc_zero(ctx
, struct diritem
);
1749 status
= NT_STATUS_NO_MEMORY
;
1753 item
->dirname
= unresolved
;
1755 mask2
= talloc_asprintf(ctx
, "%s%s", mask2
, f
->name
);
1757 status
= NT_STATUS_NO_MEMORY
;
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",
1770 item
->targetpath
= sanitize_dirname(ctx
, targetpath
);
1771 if (!item
->targetpath
) {
1772 status
= NT_STATUS_NO_MEMORY
;
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
1790 ctx
->status
= status
;
1794 DLIST_ADD_END(ctx
->list
, item
);
1797 unresolved
= sanitize_dirname(ctx
, ctx
->dir
->dirname
);
1799 status
= NT_STATUS_NO_MEMORY
;
1803 unresolved
= talloc_asprintf(ctx
, "%s%s", unresolved
, f
->name
);
1806 status
= NT_STATUS_NO_MEMORY
;
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
);
1817 status
= NT_STATUS_NO_MEMORY
;
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
1835 ctx
->status
= status
;
1839 status
= NT_STATUS_OK
;
1841 if (!NT_STATUS_IS_OK(status
)) {
1842 DBG_ERR("error %s: processing %s\n",
1843 nt_errstr(status
), targetpath
);
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
;
1864 for (item
= dump_ctx
->list
; item
; item
= item
->next
) {
1865 uint16_t attribute
= FILE_ATTRIBUTE_DIRECTORY
1866 | FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
;
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
;
1878 if (item
->targetcli
== NULL
) {
1879 status
= cli_resolve_path(new_dump_ctx
,
1886 if (!NT_STATUS_IS_OK(status
)) {
1887 DBG_ERR("failed to resolve path %s "
1889 item
->dirname
, nt_errstr(status
));
1890 result
= EXIT_FAILED
;
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
,
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
)) {
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;
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
;
1925 result
= dump_dacl_dirtree(new_dump_ctx
);
1926 if (result
!= EXIT_OK
) {
1933 TALLOC_FREE(new_dump_ctx
);
1937 static int cacl_dump_dacl(struct cli_state
*cli
,
1938 struct cli_credentials
*creds
,
1943 TALLOC_CTX
*ctx
= NULL
;
1944 bool isdirectory
= false;
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
;
1953 ctx
= talloc_init("cacl_dump");
1955 DBG_ERR("out of memory\n");
1956 result
= EXIT_FAILED
;
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
;
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
;
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
);
1987 status
= cli_resolve_path(dump_ctx
, "",
1990 filename
, &targetcli
, &targetpath
);
1991 if (!NT_STATUS_IS_OK(status
)) {
1992 DBG_ERR("failed resolve %s\n", filename
);
1993 result
= EXIT_FAILED
;
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
2011 /* make sure we have a trailing '\*' for directory */
2013 mask
= talloc_strdup(ctx
, filename
);
2014 } else if (strlen(filename
) > 1) {
2015 mask
= sanitize_dirname(ctx
, filename
);
2017 /* filename is a single '\' */
2018 mask
= talloc_strdup(ctx
, filename
);
2021 result
= EXIT_FAILED
;
2025 write_dacl(dump_ctx
, targetcli
, targetpath
, filename
);
2026 if (isdirectory
&& recurse
) {
2027 item
= talloc_zero(dump_ctx
, struct diritem
);
2029 result
= EXIT_FAILED
;
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
);
2042 if (dump_ctx
&& dump_ctx
->save_fd
> 0) {
2043 close(dump_ctx
->save_fd
);
2049 struct restore_dacl
{
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
)
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;
2073 struct dom_sid
*sid
= NULL
;
2075 if (restorefile
== NULL
) {
2076 DBG_ERR("No restore file specified\n");
2077 result
= EXIT_FAILED
;
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
;
2093 content
= fd_load(restore_fd
, &content_size
, 0, talloc_tos());
2097 if (content
== NULL
) {
2098 DBG_ERR("Failed to load content from %s.\n", restorefile
);
2099 result
= EXIT_FAILED
;
2103 ok
= convert_string_talloc(talloc_tos(),
2107 utf16_len_n(content
, content_size
),
2108 (void **)(void *)&convert_content
,
2111 TALLOC_FREE(content
);
2114 DBG_ERR("Failed to convert content from %s "
2115 "to CH_UNIX.\n", restorefile
);
2116 result
= EXIT_FAILED
;
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.",
2126 result
= EXIT_FAILED
;
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
;
2138 sid
= get_domain_sid(cli
);
2140 while (i
< numlines
) {
2142 int first_line
= (i
% 2) == 0;
2147 /* line can be blank if root of share */
2148 if (strlen(tmp
) == 0) {
2149 entries
[index
].path
= talloc_strdup(lines
,
2152 entries
[index
].path
= lines
[i
];
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
,
2165 if(entries
[index
].sd
== NULL
) {
2166 DBG_ERR("could not decode '%s'\n", lines
[i
]);
2169 (int)msg_offset
, '^');
2170 DBG_ERR("error '%s'\n", msg
);
2172 result
= EXIT_FAILED
;
2175 entries
[index
].sd
->type
|=
2176 SEC_DESC_DACL_AUTO_INHERIT_REQ
;
2177 entries
[index
].sd
->type
|= SEC_DESC_SACL_AUTO_INHERITED
;
2181 for (i
= 0; i
< (numlines
/ 2); i
++) {
2182 int mode
= SMB_ACL_SET
;
2184 struct cli_state
*targetcli
= NULL
;
2185 char *targetpath
= NULL
;
2189 status
= cli_resolve_path(talloc_tos(),
2194 &targetcli
, &targetpath
);
2196 if (!NT_STATUS_IS_OK(status
)) {
2197 printf("Error failed to process file: %s\n",
2203 set_result
= cacl_set_from_sd(targetcli
,
2205 entries
[i
].sd
, mode
, numeric
);
2207 if (set_result
== EXIT_OK
) {
2208 printf("Successfully processed file: %s\n",
2212 printf("Error failed to process file: %s\n",
2221 fprintf(stdout
, "Successfully processed %d files: "
2222 "Failed processing %d files\n", stats
.success
, stats
.failure
);
2226 /****************************************************************************
2228 ****************************************************************************/
2229 int main(int argc
, char *argv
[])
2231 const char **argv_const
= discard_const_p(const char *, argv
);
2234 enum acl_mode mode
= SMB_ACL_SET
;
2235 static char *the_acl
= NULL
;
2236 enum chown_mode change_mode
= REQUEST_NONE
;
2239 char *filename
= NULL
;
2241 /* numeric is set when the user wants numeric SIDs and ACEs rather
2242 than going via LSA calls to resolve them */
2244 struct cli_state
*targetcli
= NULL
;
2245 struct cli_credentials
*creds
= NULL
;
2246 char *targetfile
= NULL
;
2249 struct loadparm_context
*lp_ctx
= NULL
;
2251 struct poptOption long_options
[] = {
2254 .longName
= "delete",
2256 .argInfo
= POPT_ARG_STRING
,
2259 .descrip
= "Delete an acl",
2260 .argDescrip
= "ACL",
2263 .longName
= "modify",
2265 .argInfo
= POPT_ARG_STRING
,
2268 .descrip
= "Modify an acl",
2269 .argDescrip
= "ACL",
2274 .argInfo
= POPT_ARG_STRING
,
2277 .descrip
= "Add an acl",
2278 .argDescrip
= "ACL",
2283 .argInfo
= POPT_ARG_STRING
,
2286 .descrip
= "Set acls",
2287 .argDescrip
= "ACLS",
2290 .longName
= "chown",
2292 .argInfo
= POPT_ARG_STRING
,
2295 .descrip
= "Change ownership of a file",
2296 .argDescrip
= "USERNAME",
2299 .longName
= "chgrp",
2301 .argInfo
= POPT_ARG_STRING
,
2304 .descrip
= "Change group ownership of a file",
2305 .argDescrip
= "GROUPNAME",
2308 .longName
= "inherit",
2310 .argInfo
= POPT_ARG_STRING
,
2313 .descrip
= "Inherit allow|remove|copy",
2316 .longName
= "propagate-inheritance",
2318 .argInfo
= POPT_ARG_NONE
,
2319 .arg
= &inheritance
,
2321 .descrip
= "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
2326 .argInfo
= POPT_ARG_STRING
,
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",
2337 .argInfo
= POPT_ARG_STRING
,
2338 .arg
= &restore_file
,
2340 .descrip
= "applies the stored DACLS to files in "
2344 .longName
= "recurse",
2346 .argInfo
= POPT_ARG_NONE
,
2349 .descrip
= "indicates the operation is performed "
2350 "on directory and all files/directories"
2351 " below. (only applies to save option)",
2354 .longName
= "numeric",
2356 .argInfo
= POPT_ARG_NONE
,
2359 .descrip
= "Don't resolve sids or masks to names",
2364 .argInfo
= POPT_ARG_NONE
,
2367 .descrip
= "Output and input acls in sddl format",
2370 .longName
= "query-security-info",
2372 .argInfo
= POPT_ARG_INT
,
2373 .arg
= &query_sec_info
,
2375 .descrip
= "The security-info flags for queries"
2378 .longName
= "set-security-info",
2380 .argInfo
= POPT_ARG_INT
,
2381 .arg
= &set_sec_info
,
2383 .descrip
= "The security-info flags for modifications"
2386 .longName
= "test-args",
2388 .argInfo
= POPT_ARG_NONE
,
2391 .descrip
= "Test arguments"
2394 .longName
= "domain-sid",
2396 .argInfo
= POPT_ARG_STRING
,
2399 .descrip
= "Domain SID for sddl",
2400 .argDescrip
= "SID"},
2402 .longName
= "maximum-access",
2404 .argInfo
= POPT_ARG_NONE
,
2407 .descrip
= "Query maximum permissions",
2410 POPT_COMMON_CONNECTION
2411 POPT_COMMON_CREDENTIALS
2417 struct cli_state
*cli
;
2418 TALLOC_CTX
*frame
= talloc_stackframe();
2419 const char *owner_username
= "";
2424 ok
= samba_cmdline_init(frame
,
2425 SAMBA_CMDLINE_CONFIG_CLIENT
,
2426 false /* require_smbconf */);
2428 DBG_ERR("Failed to init cmdline parser!\n");
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");
2438 pc
= samba_popt_get_context(getprogname(),
2444 DBG_ERR("Failed to setup popt context!\n");
2449 poptSetOtherOptionHelp(pc
, "//server1/share1 filename\nACLs look like: "
2450 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
2452 while ((opt
= poptGetNextOpt(pc
)) != -1) {
2455 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
2460 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
2461 mode
= SMB_ACL_DELETE
;
2465 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
2466 mode
= SMB_ACL_MODIFY
;
2470 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
2475 owner_username
= poptGetOptArg(pc
);
2476 change_mode
= REQUEST_CHOWN
;
2480 owner_username
= poptGetOptArg(pc
);
2481 change_mode
= REQUEST_CHGRP
;
2485 owner_username
= poptGetOptArg(pc
);
2486 change_mode
= REQUEST_INHERIT
;
2489 lpcfg_set_cmdline(lp_ctx
, "client max protocol", poptGetOptArg(pc
));
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);
2501 if (inheritance
&& !the_acl
) {
2502 poptPrintUsage(pc
, stderr
, 0);
2506 if(!poptPeekArg(pc
)) {
2507 poptPrintUsage(pc
, stderr
, 0);
2511 path
= talloc_strdup(frame
, poptGetArg(pc
));
2516 if (strncmp(path
, "\\\\", 2) && strncmp(path
, "//", 2)) {
2517 printf("Invalid argument: %s\n", path
);
2521 if(!poptPeekArg(pc
)) {
2522 poptPrintUsage(pc
, stderr
, 0);
2526 filename
= talloc_strdup(frame
, poptGetArg(pc
));
2531 poptFreeContext(pc
);
2532 samba_cmdline_burn(argc
, argv
);
2534 string_replace(path
,'/','\\');
2536 server
= talloc_strdup(frame
, path
+2);
2540 share
= strchr_m(server
,'\\');
2541 if (share
== NULL
) {
2542 printf("Invalid argument\n");
2549 creds
= samba_cmdline_get_creds();
2551 /* Make connection to server */
2553 cli
= connect_one(creds
, server
, share
);
2561 string_replace(filename
, '/', '\\');
2562 if (filename
[0] != '\\') {
2563 filename
= talloc_asprintf(frame
,
2571 status
= cli_resolve_path(frame
,
2578 if (!NT_STATUS_IS_OK(status
)) {
2579 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename
, nt_errstr(status
)));
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
) {
2591 struct cacl_callback_state cbstate
= {
2598 result
= inheritance_cacl_set(targetfile
, &cbstate
);
2600 result
= cacl_set(targetcli
,
2607 if (save_file
|| restore_file
) {
2610 result
= cacl_dump_dacl(cli
, creds
, filename
);
2612 result
= cacl_restore(targetcli
,
2614 numeric
, restore_file
);
2617 result
= cacl_dump(targetcli
, targetfile
, numeric
);