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"
38 static char DIRSEP_CHAR
= '\\';
40 static int inheritance
= 0;
43 static int query_sec_info
= -1;
44 static int set_sec_info
= -1;
45 static bool want_mxac
;
47 static const char *domain_sid
= NULL
;
49 enum acl_mode
{SMB_ACL_SET
, SMB_ACL_DELETE
, SMB_ACL_MODIFY
, SMB_ACL_ADD
};
50 enum chown_mode
{REQUEST_NONE
, REQUEST_CHOWN
, REQUEST_CHGRP
, REQUEST_INHERIT
};
51 enum exit_values
{EXIT_OK
, EXIT_FAILED
, EXIT_PARSE_ERROR
};
53 struct cacl_callback_state
{
54 struct cli_credentials
*creds
;
55 struct cli_state
*cli
;
56 struct security_descriptor
*aclsd
;
57 struct security_acl
*acl_to_add
;
60 bool acl_no_propagate
;
64 static NTSTATUS
cli_lsa_lookup_domain_sid(struct cli_state
*cli
,
67 union lsa_PolicyInformation
*info
= NULL
;
68 struct smbXcli_tcon
*orig_tcon
= NULL
;
69 char *orig_share
= NULL
;
70 struct rpc_pipe_client
*rpc_pipe
= NULL
;
71 struct policy_handle handle
;
72 NTSTATUS status
, result
;
73 TALLOC_CTX
*frame
= talloc_stackframe();
75 if (cli_state_has_tcon(cli
)) {
76 cli_state_save_tcon_share(cli
, &orig_tcon
, &orig_share
);
79 status
= cli_tree_connect(cli
, "IPC$", "?????", NULL
);
80 if (!NT_STATUS_IS_OK(status
)) {
84 status
= cli_rpc_pipe_open_noauth(cli
, &ndr_table_lsarpc
, &rpc_pipe
);
85 if (!NT_STATUS_IS_OK(status
)) {
89 status
= rpccli_lsa_open_policy(rpc_pipe
, frame
, True
,
90 GENERIC_EXECUTE_ACCESS
, &handle
);
91 if (!NT_STATUS_IS_OK(status
)) {
95 status
= dcerpc_lsa_QueryInfoPolicy2(rpc_pipe
->binding_handle
,
97 LSA_POLICY_INFO_DOMAIN
,
100 if (any_nt_status_not_ok(status
, result
, &status
)) {
104 *sid
= *info
->domain
.sid
;
107 TALLOC_FREE(rpc_pipe
);
110 cli_state_restore_tcon_share(cli
, orig_tcon
, orig_share
);
115 static struct dom_sid
*get_domain_sid(struct cli_state
*cli
)
118 struct dom_sid_buf buf
;
120 struct dom_sid
*sid
= talloc(talloc_tos(), struct dom_sid
);
122 DEBUG(0, ("Out of memory\n"));
127 if (!dom_sid_parse(domain_sid
, sid
)) {
128 DEBUG(0,("failed to parse domain sid\n"));
132 status
= cli_lsa_lookup_domain_sid(cli
, sid
);
134 if (!NT_STATUS_IS_OK(status
)) {
135 DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status
)));
141 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid
, &buf
)));
145 /* add an ACE to a list of ACEs in a struct security_acl */
146 static bool add_ace_with_ctx(TALLOC_CTX
*ctx
, struct security_acl
**the_acl
,
147 const struct security_ace
*ace
)
150 struct security_acl
*acl
= *the_acl
;
153 acl
= make_sec_acl(ctx
, 3, 0, NULL
);
159 if (acl
->num_aces
== UINT32_MAX
) {
163 acl
, struct security_ace
, *ace
, &acl
->aces
, &acl
->num_aces
);
168 static bool add_ace(struct security_acl
**the_acl
, struct security_ace
*ace
)
170 return add_ace_with_ctx(talloc_tos(), the_acl
, ace
);
173 /* parse a ascii version of a security descriptor */
174 static struct security_descriptor
*sec_desc_parse(TALLOC_CTX
*ctx
, struct cli_state
*cli
, char *str
)
178 struct security_descriptor
*ret
= NULL
;
180 struct dom_sid owner_sid
= { .num_auths
= 0 };
181 bool have_owner
= false;
182 struct dom_sid group_sid
= { .num_auths
= 0 };
183 bool have_group
= false;
184 struct security_acl
*dacl
=NULL
;
187 while (next_token_talloc(ctx
, &p
, &tok
, "\t,\r\n")) {
188 if (strncmp(tok
,"REVISION:", 9) == 0) {
189 revision
= strtol(tok
+9, NULL
, 16);
193 if (strncmp(tok
,"OWNER:", 6) == 0) {
195 printf("Only specify owner once\n");
198 if (!StringToSid(cli
, &owner_sid
, tok
+6)) {
199 printf("Failed to parse owner sid\n");
206 if (strncmp(tok
,"GROUP:", 6) == 0) {
208 printf("Only specify group once\n");
211 if (!StringToSid(cli
, &group_sid
, tok
+6)) {
212 printf("Failed to parse group sid\n");
219 if (strncmp(tok
,"ACL:", 4) == 0) {
220 struct security_ace ace
;
221 if (!parse_ace(cli
, &ace
, tok
+4)) {
224 if(!add_ace(&dacl
, &ace
)) {
225 printf("Failed to add ACL %s\n", tok
);
231 printf("Failed to parse token '%s' in security descriptor,\n", tok
);
238 SEC_DESC_SELF_RELATIVE
,
239 have_owner
? &owner_sid
: NULL
,
240 have_group
? &group_sid
: NULL
,
249 /*****************************************************
250 get fileinfo for filename
251 *******************************************************/
252 static uint16_t get_fileinfo(struct cli_state
*cli
, const char *filename
)
254 uint16_t fnum
= (uint16_t)-1;
256 struct smb_create_returns cr
= {0};
258 /* The desired access below is the only one I could find that works
259 with NT4, W2KP and Samba */
261 status
= cli_ntcreate(
263 filename
, /* fname */
265 READ_CONTROL_ACCESS
, /* CreatFlags */
266 0, /* FileAttributes */
268 FILE_SHARE_WRITE
, /* ShareAccess */
269 FILE_OPEN
, /* CreateDisposition */
270 0x0, /* CreateOptions */
271 0x0, /* SecurityFlags */
274 if (!NT_STATUS_IS_OK(status
)) {
275 printf("Failed to open %s: %s\n", filename
, nt_errstr(status
));
279 cli_close(cli
, fnum
);
280 return cr
.file_attributes
;
283 /*****************************************************
284 get sec desc for filename
285 *******************************************************/
286 static struct security_descriptor
*get_secdesc_with_ctx(TALLOC_CTX
*ctx
,
287 struct cli_state
*cli
,
288 const char *filename
)
290 uint16_t fnum
= (uint16_t)-1;
291 struct security_descriptor
*sd
;
294 uint32_t desired_access
= 0;
296 if (query_sec_info
== -1) {
297 sec_info
= SECINFO_OWNER
| SECINFO_GROUP
| SECINFO_DACL
;
299 sec_info
= query_sec_info
;
302 if (sec_info
& (SECINFO_OWNER
| SECINFO_GROUP
| SECINFO_DACL
)) {
303 desired_access
|= SEC_STD_READ_CONTROL
;
305 if (sec_info
& SECINFO_SACL
) {
306 desired_access
|= SEC_FLAG_SYSTEM_SECURITY
;
309 if (desired_access
== 0) {
310 desired_access
|= SEC_STD_READ_CONTROL
;
313 status
= cli_ntcreate(cli
, filename
, 0, desired_access
,
314 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
315 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
316 if (!NT_STATUS_IS_OK(status
)) {
317 printf("Failed to open %s: %s\n", filename
, nt_errstr(status
));
321 status
= cli_query_security_descriptor(cli
, fnum
, sec_info
,
324 cli_close(cli
, fnum
);
326 if (!NT_STATUS_IS_OK(status
)) {
327 printf("Failed to get security descriptor: %s\n",
334 static struct security_descriptor
*get_secdesc(struct cli_state
*cli
,
335 const char *filename
)
337 return get_secdesc_with_ctx(talloc_tos(), cli
, filename
);
339 /*****************************************************
340 set sec desc for filename
341 *******************************************************/
342 static bool set_secdesc(struct cli_state
*cli
, const char *filename
,
343 struct security_descriptor
*sd
)
345 uint16_t fnum
= (uint16_t)-1;
348 uint32_t desired_access
= 0;
351 if (set_sec_info
== -1) {
354 if (sd
->dacl
|| (sd
->type
& SEC_DESC_DACL_PRESENT
)) {
355 sec_info
|= SECINFO_DACL
;
357 if (sd
->sacl
|| (sd
->type
& SEC_DESC_SACL_PRESENT
)) {
358 sec_info
|= SECINFO_SACL
;
361 sec_info
|= SECINFO_OWNER
;
364 sec_info
|= SECINFO_GROUP
;
367 sec_info
= set_sec_info
;
370 /* Make the desired_access more specific. */
371 if (sec_info
& SECINFO_DACL
) {
372 desired_access
|= SEC_STD_WRITE_DAC
;
374 if (sec_info
& SECINFO_SACL
) {
375 desired_access
|= SEC_FLAG_SYSTEM_SECURITY
;
377 if (sec_info
& (SECINFO_OWNER
| SECINFO_GROUP
)) {
378 desired_access
|= SEC_STD_WRITE_OWNER
;
381 status
= cli_ntcreate(cli
, filename
, 0,
383 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
384 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
385 if (!NT_STATUS_IS_OK(status
)) {
386 printf("Failed to open %s: %s\n", filename
, nt_errstr(status
));
390 status
= cli_set_security_descriptor(cli
, fnum
, sec_info
, sd
);
391 if (!NT_STATUS_IS_OK(status
)) {
392 printf("ERROR: security descriptor set failed: %s\n",
397 cli_close(cli
, fnum
);
401 /*****************************************************
402 get maximum access for a file
403 *******************************************************/
404 static int cacl_mxac(struct cli_state
*cli
, const char *filename
)
409 status
= cli_query_mxac(cli
, filename
, &mxac
);
410 if (!NT_STATUS_IS_OK(status
)) {
411 printf("Failed to get mxac: %s\n", nt_errstr(status
));
415 printf("Maximum access: 0x%x\n", mxac
);
421 /*****************************************************
422 dump the acls for a file
423 *******************************************************/
424 static int cacl_dump(struct cli_state
*cli
, const char *filename
, bool numeric
)
426 struct security_descriptor
*sd
;
433 sd
= get_secdesc(cli
, filename
);
439 char *str
= sddl_encode(talloc_tos(), sd
, get_domain_sid(cli
));
446 sec_desc_print(cli
, stdout
, sd
, numeric
);
450 ret
= cacl_mxac(cli
, filename
);
451 if (ret
!= EXIT_OK
) {
459 /*****************************************************
460 Change the ownership or group ownership of a file. Just
461 because the NT docs say this can't be done :-). JRA.
462 *******************************************************/
464 static int owner_set(struct cli_state
*cli
, enum chown_mode change_mode
,
465 const char *filename
, const char *new_username
)
468 struct security_descriptor
*sd
;
471 if (!StringToSid(cli
, &sid
, new_username
))
472 return EXIT_PARSE_ERROR
;
474 sd
= make_sec_desc(talloc_tos(),
475 SECURITY_DESCRIPTOR_REVISION_1
,
476 SEC_DESC_SELF_RELATIVE
,
477 (change_mode
== REQUEST_CHOWN
) ? &sid
: NULL
,
478 (change_mode
== REQUEST_CHGRP
) ? &sid
: NULL
,
479 NULL
, NULL
, &sd_size
);
481 if (!set_secdesc(cli
, filename
, sd
)) {
489 /* The MSDN is contradictory over the ordering of ACE entries in an
490 ACL. However NT4 gives a "The information may have been modified
491 by a computer running Windows NT 5.0" if denied ACEs do not appear
492 before allowed ACEs. At
493 http://technet.microsoft.com/en-us/library/cc781716.aspx the
494 canonical order is specified as "Explicit Deny, Explicit Allow,
495 Inherited ACEs unchanged" */
497 static int ace_compare(struct security_ace
*ace1
, struct security_ace
*ace2
)
499 if (security_ace_equal(ace1
, ace2
))
502 if ((ace1
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) &&
503 !(ace2
->flags
& SEC_ACE_FLAG_INHERITED_ACE
))
505 if (!(ace1
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) &&
506 (ace2
->flags
& SEC_ACE_FLAG_INHERITED_ACE
))
508 if ((ace1
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) &&
509 (ace2
->flags
& SEC_ACE_FLAG_INHERITED_ACE
))
512 if (ace1
->type
!= ace2
->type
)
513 return ace2
->type
- ace1
->type
;
515 if (dom_sid_compare(&ace1
->trustee
, &ace2
->trustee
))
516 return dom_sid_compare(&ace1
->trustee
, &ace2
->trustee
);
518 if (ace1
->flags
!= ace2
->flags
)
519 return ace1
->flags
- ace2
->flags
;
521 if (ace1
->access_mask
!= ace2
->access_mask
)
522 return ace1
->access_mask
- ace2
->access_mask
;
524 if (ace1
->size
!= ace2
->size
)
525 return ace1
->size
- ace2
->size
;
527 return memcmp(ace1
, ace2
, sizeof(struct security_ace
));
530 static void sort_acl(struct security_acl
*the_acl
)
533 if (!the_acl
) return;
535 TYPESAFE_QSORT(the_acl
->aces
, the_acl
->num_aces
, ace_compare
);
537 for (i
=1;i
<the_acl
->num_aces
;) {
538 if (security_ace_equal(&the_acl
->aces
[i
-1],
539 &the_acl
->aces
[i
])) {
541 the_acl
->aces
, i
, the_acl
->num_aces
);
549 /*****************************************************
550 set the ACLs on a file given a security descriptor
551 *******************************************************/
553 static int cacl_set_from_sd(struct cli_state
*cli
, const char *filename
,
554 struct security_descriptor
*sd
, enum acl_mode mode
,
557 struct security_descriptor
*old
= NULL
;
560 int result
= EXIT_OK
;
562 if (!sd
) return EXIT_PARSE_ERROR
;
563 if (test_args
) return EXIT_OK
;
565 if (mode
!= SMB_ACL_SET
) {
567 * Do not fetch old ACL when it will be overwritten
568 * completely with a new one.
570 old
= get_secdesc(cli
, filename
);
577 /* the logic here is rather more complex than I would like */
580 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
583 for (j
=0;old
->dacl
&& j
<old
->dacl
->num_aces
;j
++) {
584 if (security_ace_equal(&sd
->dacl
->aces
[i
],
585 &old
->dacl
->aces
[j
])) {
587 for (k
=j
; k
<old
->dacl
->num_aces
-1;k
++) {
588 old
->dacl
->aces
[k
] = old
->dacl
->aces
[k
+1];
590 old
->dacl
->num_aces
--;
597 printf("ACL for ACE:");
598 print_ace(cli
, stdout
, &sd
->dacl
->aces
[i
],
600 printf(" not found\n");
606 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
609 for (j
=0;old
->dacl
&& j
<old
->dacl
->num_aces
;j
++) {
610 if (dom_sid_equal(&sd
->dacl
->aces
[i
].trustee
,
611 &old
->dacl
->aces
[j
].trustee
)) {
612 old
->dacl
->aces
[j
] = sd
->dacl
->aces
[i
];
620 SidToString(cli
, str
,
621 &sd
->dacl
->aces
[i
].trustee
,
623 printf("ACL for SID %s not found\n", str
);
628 old
->owner_sid
= sd
->owner_sid
;
632 old
->group_sid
= sd
->group_sid
;
638 for (i
=0;sd
->dacl
&& i
<sd
->dacl
->num_aces
;i
++) {
639 add_ace(&old
->dacl
, &sd
->dacl
->aces
[i
]);
648 /* Denied ACE entries must come before allowed ones */
651 /* Create new security descriptor and set it */
653 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
654 But if we're sending an owner, even if it's the same as the one
655 that already exists then W2K3 insists we open with WRITE_OWNER access.
656 I need to check that setting a SD with no owner set works against WNT
660 sd
= make_sec_desc(talloc_tos(),old
->revision
, old
->type
,
661 old
->owner_sid
, old
->group_sid
,
662 NULL
, old
->dacl
, &sd_size
);
664 if (!set_secdesc(cli
, filename
, sd
)) {
665 result
= EXIT_FAILED
;
671 /*****************************************************
672 set the ACLs on a file given an ascii description
673 *******************************************************/
675 static int cacl_set(struct cli_state
*cli
, const char *filename
,
676 char *the_acl
, enum acl_mode mode
, bool numeric
)
678 struct security_descriptor
*sd
= NULL
;
681 sd
= sddl_decode(talloc_tos(), the_acl
, get_global_sam_sid());
683 sd
= sec_desc_parse(talloc_tos(), cli
, the_acl
);
687 return EXIT_PARSE_ERROR
;
692 return cacl_set_from_sd(cli
, filename
, sd
, mode
, numeric
);
695 /*****************************************************
696 set the inherit on a file
697 *******************************************************/
698 static int inherit(struct cli_state
*cli
, const char *filename
,
701 struct security_descriptor
*old
,*sd
;
704 int result
= EXIT_OK
;
706 old
= get_secdesc(cli
, filename
);
712 oldattr
= get_fileinfo(cli
,filename
);
714 if (strcmp(type
,"allow")==0) {
715 if ((old
->type
& SEC_DESC_DACL_PROTECTED
) ==
716 SEC_DESC_DACL_PROTECTED
) {
718 char *parentname
,*temp
;
719 struct security_descriptor
*parent
;
720 temp
= talloc_strdup(talloc_tos(), filename
);
722 old
->type
=old
->type
& (~SEC_DESC_DACL_PROTECTED
);
724 /* look at parent and copy in all its inheritable ACL's. */
725 string_replace(temp
, '\\', '/');
726 if (!parent_dirname(talloc_tos(),temp
,&parentname
,NULL
)) {
729 string_replace(parentname
, '/', '\\');
730 parent
= get_secdesc(cli
,parentname
);
731 if (parent
== NULL
) {
734 for (i
=0;i
<parent
->dacl
->num_aces
;i
++) {
735 struct security_ace
*ace
=&parent
->dacl
->aces
[i
];
736 /* Add inherited flag to all aces */
737 ace
->flags
=ace
->flags
|
738 SEC_ACE_FLAG_INHERITED_ACE
;
739 if ((oldattr
& FILE_ATTRIBUTE_DIRECTORY
) == FILE_ATTRIBUTE_DIRECTORY
) {
740 if ((ace
->flags
& SEC_ACE_FLAG_CONTAINER_INHERIT
) ==
741 SEC_ACE_FLAG_CONTAINER_INHERIT
) {
742 add_ace(&old
->dacl
, ace
);
745 if ((ace
->flags
& SEC_ACE_FLAG_OBJECT_INHERIT
) ==
746 SEC_ACE_FLAG_OBJECT_INHERIT
) {
747 /* clear flags for files */
749 add_ace(&old
->dacl
, ace
);
754 printf("Already set to inheritable permissions.\n");
757 } else if (strcmp(type
,"remove")==0) {
758 if ((old
->type
& SEC_DESC_DACL_PROTECTED
) !=
759 SEC_DESC_DACL_PROTECTED
) {
760 old
->type
=old
->type
| SEC_DESC_DACL_PROTECTED
;
762 /* remove all inherited ACL's. */
765 struct security_acl
*temp
=old
->dacl
;
766 old
->dacl
=make_sec_acl(talloc_tos(), 3, 0, NULL
);
767 for (i
=temp
->num_aces
-1;i
>=0;i
--) {
768 struct security_ace
*ace
=&temp
->aces
[i
];
769 /* Remove all ace with INHERITED flag set */
770 if ((ace
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) !=
771 SEC_ACE_FLAG_INHERITED_ACE
) {
772 add_ace(&old
->dacl
,ace
);
777 printf("Already set to no inheritable permissions.\n");
780 } else if (strcmp(type
,"copy")==0) {
781 if ((old
->type
& SEC_DESC_DACL_PROTECTED
) !=
782 SEC_DESC_DACL_PROTECTED
) {
783 old
->type
=old
->type
| SEC_DESC_DACL_PROTECTED
;
786 * convert all inherited ACL's to non
791 for (i
=0;i
<old
->dacl
->num_aces
;i
++) {
792 struct security_ace
*ace
=&old
->dacl
->aces
[i
];
793 /* Remove INHERITED FLAG from all aces */
794 ace
->flags
=ace
->flags
&(~SEC_ACE_FLAG_INHERITED_ACE
);
798 printf("Already set to no inheritable permissions.\n");
803 /* Denied ACE entries must come before allowed ones */
806 sd
= make_sec_desc(talloc_tos(),old
->revision
, old
->type
,
807 old
->owner_sid
, old
->group_sid
,
808 NULL
, old
->dacl
, &sd_size
);
810 if (!set_secdesc(cli
, filename
, sd
)) {
811 result
= EXIT_FAILED
;
817 /*****************************************************
818 Return a connection to a server.
819 *******************************************************/
820 static struct cli_state
*connect_one(struct cli_credentials
*creds
,
821 const char *server
, const char *share
)
823 struct cli_state
*c
= NULL
;
827 nt_status
= cli_full_connection_creds(&c
, lp_netbios_name(), server
,
832 if (!NT_STATUS_IS_OK(nt_status
)) {
833 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status
)));
841 * Process resulting combination of mask & fname ensuring
842 * terminated with wildcard
844 static char *build_dirname(TALLOC_CTX
*ctx
,
845 const char *mask
, char *dir
, char *fname
)
850 mask2
= talloc_strdup(ctx
, mask
);
854 p
= strrchr_m(mask2
, DIRSEP_CHAR
);
860 mask2
= talloc_asprintf_append(mask2
,
867 * Returns a copy of the ACL flags in ace modified according
868 * to some inheritance rules.
869 * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
870 * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
871 * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
872 * stripped from flags to be propagated to non-container children
873 * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
874 * stripped from flags to be propagated if the NP flag
875 * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
878 static uint8_t get_flags_to_propagate(bool is_container
,
879 struct security_ace
*ace
)
881 uint8_t newflags
= ace
->flags
;
882 /* OBJECT inheritance */
883 bool acl_objinherit
= (ace
->flags
&
884 SEC_ACE_FLAG_OBJECT_INHERIT
) == SEC_ACE_FLAG_OBJECT_INHERIT
;
885 /* CONTAINER inheritance */
886 bool acl_cntrinherit
= (ace
->flags
&
887 SEC_ACE_FLAG_CONTAINER_INHERIT
) ==
888 SEC_ACE_FLAG_CONTAINER_INHERIT
;
889 /* PROHIBIT inheritance */
890 bool prohibit_inheritance
= ((ace
->flags
&
891 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT
) ==
892 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT
);
894 /* Assume we are not propagating the ACE */
896 newflags
&= ~SEC_ACE_FLAG_INHERITED_ACE
;
897 /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
898 if (acl_cntrinherit
|| acl_objinherit
) {
900 * object inherit ( alone ) on a container needs
901 * SEC_ACE_FLAG_INHERIT_ONLY
904 if (acl_objinherit
&& !acl_cntrinherit
) {
905 newflags
|= SEC_ACE_FLAG_INHERIT_ONLY
;
908 * this is tricky, the only time we would not
909 * propagate the ace for a container is if
910 * prohibit_inheritance is set and object inheritance
913 if ((prohibit_inheritance
915 && !acl_cntrinherit
) == false) {
916 newflags
|= SEC_ACE_FLAG_INHERITED_ACE
;
920 * don't apply object/container inheritance flags to
923 newflags
&= ~(SEC_ACE_FLAG_OBJECT_INHERIT
924 | SEC_ACE_FLAG_CONTAINER_INHERIT
925 | SEC_ACE_FLAG_INHERIT_ONLY
);
927 * only apply ace to file if object inherit
929 if (acl_objinherit
) {
930 newflags
|= SEC_ACE_FLAG_INHERITED_ACE
;
934 /* if NP is specified strip NP and all OI/CI INHERIT flags */
935 if (prohibit_inheritance
) {
936 newflags
&= ~(SEC_ACE_FLAG_OBJECT_INHERIT
937 | SEC_ACE_FLAG_CONTAINER_INHERIT
938 | SEC_ACE_FLAG_INHERIT_ONLY
939 | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT
);
946 * This function builds a new acl for 'caclfile', first it removes any
947 * existing inheritable ace(s) from the current acl of caclfile, secondly it
948 * applies any inheritable acls of the parent of caclfile ( inheritable acls of
949 * caclfile's parent are passed via acl_to_add member of cbstate )
952 static NTSTATUS
propagate_inherited_aces(char *caclfile
,
953 struct cacl_callback_state
*cbstate
)
955 TALLOC_CTX
*aclctx
= NULL
;
959 struct security_descriptor
*old
= NULL
;
960 bool is_container
= false;
961 struct security_acl
*acl_to_add
= cbstate
->acl_to_add
;
962 struct security_acl
*acl_to_remove
= NULL
;
965 aclctx
= talloc_new(NULL
);
966 if (aclctx
== NULL
) {
967 return NT_STATUS_NO_MEMORY
;
969 old
= get_secdesc_with_ctx(aclctx
, cbstate
->cli
, caclfile
);
972 status
= NT_STATUS_UNSUCCESSFUL
;
976 /* inhibit propagation? */
977 if ((old
->type
& SEC_DESC_DACL_PROTECTED
) ==
978 SEC_DESC_DACL_PROTECTED
){
979 status
= NT_STATUS_OK
;
983 fileattr
= get_fileinfo(cbstate
->cli
, caclfile
);
984 is_container
= (fileattr
& FILE_ATTRIBUTE_DIRECTORY
);
986 /* find acl(s) that are inherited */
987 for (j
= 0; old
->dacl
&& j
< old
->dacl
->num_aces
; j
++) {
989 if (old
->dacl
->aces
[j
].flags
& SEC_ACE_FLAG_INHERITED_ACE
) {
990 if (!add_ace_with_ctx(aclctx
, &acl_to_remove
,
991 &old
->dacl
->aces
[j
])) {
992 status
= NT_STATUS_NO_MEMORY
;
998 /* remove any acl(s) that are inherited */
1000 for (i
= 0; i
< acl_to_remove
->num_aces
; i
++) {
1001 struct security_ace ace
= acl_to_remove
->aces
[i
];
1002 for (j
= 0; old
->dacl
&& j
< old
->dacl
->num_aces
; j
++) {
1004 if (security_ace_equal(&ace
,
1005 &old
->dacl
->aces
[j
])) {
1007 for (k
= j
; k
< old
->dacl
->num_aces
-1;
1009 old
->dacl
->aces
[k
] =
1010 old
->dacl
->aces
[k
+1];
1012 old
->dacl
->num_aces
--;
1018 /* propagate any inheritable ace to be added */
1020 for (i
= 0; i
< acl_to_add
->num_aces
; i
++) {
1021 struct security_ace ace
= acl_to_add
->aces
[i
];
1022 bool is_objectinherit
= (ace
.flags
&
1023 SEC_ACE_FLAG_OBJECT_INHERIT
) ==
1024 SEC_ACE_FLAG_OBJECT_INHERIT
;
1026 /* don't propagate flags to a file unless OI */
1027 if (!is_objectinherit
&& !is_container
) {
1031 * adjust flags according to inheritance
1034 ace
.flags
= get_flags_to_propagate(is_container
, &ace
);
1035 is_inherited
= (ace
.flags
&
1036 SEC_ACE_FLAG_INHERITED_ACE
) ==
1037 SEC_ACE_FLAG_INHERITED_ACE
;
1038 /* don't propagate non inherited flags */
1039 if (!is_inherited
) {
1042 if (!add_ace_with_ctx(aclctx
, &old
->dacl
, &ace
)) {
1043 status
= NT_STATUS_NO_MEMORY
;
1049 result
= cacl_set_from_sd(cbstate
->cli
, caclfile
,
1051 SMB_ACL_SET
, cbstate
->numeric
);
1052 if (result
!= EXIT_OK
) {
1053 status
= NT_STATUS_UNSUCCESSFUL
;
1057 status
= NT_STATUS_OK
;
1059 TALLOC_FREE(aclctx
);
1064 * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1065 * SEC_ACE_FLAG_CONTAINER_INHERIT
1067 static bool is_inheritable_ace(struct security_ace
*ace
)
1069 uint8_t flags
= ace
->flags
;
1070 if (flags
& (SEC_ACE_FLAG_OBJECT_INHERIT
1071 | SEC_ACE_FLAG_CONTAINER_INHERIT
)) {
1077 /* This method does some basic sanity checking with respect to automatic
1078 * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1079 * attempts to set inherited permissions directly. Additionally this method
1080 * does some basic initialisation for instance it parses the ACL passed on the
1083 static NTSTATUS
prepare_inheritance_propagation(TALLOC_CTX
*ctx
, char *filename
,
1084 struct cacl_callback_state
*cbstate
)
1087 char *the_acl
= cbstate
->the_acl
;
1088 struct cli_state
*cli
= cbstate
->cli
;
1089 enum acl_mode mode
= cbstate
->mode
;
1090 struct security_descriptor
*sd
= NULL
;
1091 struct security_descriptor
*old
= NULL
;
1093 bool propagate
= false;
1095 old
= get_secdesc_with_ctx(ctx
, cli
, filename
);
1097 return NT_STATUS_NO_MEMORY
;
1100 /* parse acl passed on the command line */
1102 cbstate
->aclsd
= sddl_decode(ctx
, the_acl
,
1103 get_global_sam_sid());
1105 cbstate
->aclsd
= sec_desc_parse(ctx
, cli
, the_acl
);
1108 if (!cbstate
->aclsd
) {
1109 result
= NT_STATUS_UNSUCCESSFUL
;
1113 sd
= cbstate
->aclsd
;
1115 /* set operation if inheritance is enabled doesn't make sense */
1116 if (mode
== SMB_ACL_SET
&& ((old
->type
& SEC_DESC_DACL_PROTECTED
) !=
1117 SEC_DESC_DACL_PROTECTED
)){
1118 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename
);
1119 result
= NT_STATUS_UNSUCCESSFUL
;
1125 * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1126 * flags that are set
1128 for (j
= 0; sd
->dacl
&& j
< sd
->dacl
->num_aces
; j
++) {
1129 struct security_ace
*ace
= &sd
->dacl
->aces
[j
];
1130 if (ace
->flags
& SEC_ACE_FLAG_INHERITED_ACE
) {
1131 d_printf("Illegal parameter %s\n", the_acl
);
1132 result
= NT_STATUS_UNSUCCESSFUL
;
1136 if (is_inheritable_ace(ace
)) {
1142 result
= NT_STATUS_OK
;
1144 cbstate
->acl_no_propagate
= !propagate
;
1149 * This method builds inheritable ace(s) from filename (which should be
1150 * a container) that need propagating to children in order to provide
1151 * automatic inheritance. Those inheritable ace(s) are stored in
1152 * acl_to_add member of cbstate for later processing
1153 * (see propagate_inherited_aces)
1155 static NTSTATUS
get_inheritable_aces(TALLOC_CTX
*ctx
, char *filename
,
1156 struct cacl_callback_state
*cbstate
)
1159 struct cli_state
*cli
= NULL
;
1160 struct security_descriptor
*sd
= NULL
;
1161 struct security_acl
*acl_to_add
= NULL
;
1165 sd
= get_secdesc_with_ctx(ctx
, cli
, filename
);
1168 return NT_STATUS_NO_MEMORY
;
1172 * Check if any inheritance related flags are used, if not then
1173 * nothing to do. At the same time populate acls for inheritance
1174 * related ace(s) that need to be added to or deleted from children as
1175 * a result of inheritance propagation.
1178 for (j
= 0; sd
->dacl
&& j
< sd
->dacl
->num_aces
; j
++) {
1179 struct security_ace
*ace
= &sd
->dacl
->aces
[j
];
1180 if (is_inheritable_ace(ace
)) {
1181 bool added
= add_ace_with_ctx(ctx
, &acl_to_add
, ace
);
1183 result
= NT_STATUS_NO_MEMORY
;
1188 cbstate
->acl_to_add
= acl_to_add
;
1189 result
= NT_STATUS_OK
;
1195 * Callback handler to handle child elements processed by cli_list, we attempt
1196 * to propagate inheritable ace(s) to each child via the function
1197 * propagate_inherited_aces. Children that are themselves directories are passed
1198 * to cli_list again ( to descend the directory structure )
1200 static NTSTATUS
cacl_set_cb(struct file_info
*f
,
1201 const char *mask
, void *state
)
1203 struct cacl_callback_state
*cbstate
=
1204 (struct cacl_callback_state
*)state
;
1205 struct cli_state
*cli
= NULL
;
1206 struct cli_credentials
*creds
= NULL
;
1208 TALLOC_CTX
*dirctx
= NULL
;
1210 struct cli_state
*targetcli
= NULL
;
1213 char *dir_end
= NULL
;
1215 char *targetpath
= NULL
;
1216 char *caclfile
= NULL
;
1218 dirctx
= talloc_new(NULL
);
1220 status
= NT_STATUS_NO_MEMORY
;
1225 creds
= cbstate
->creds
;
1227 /* Work out the directory. */
1228 dir
= talloc_strdup(dirctx
, mask
);
1230 status
= NT_STATUS_NO_MEMORY
;
1234 dir_end
= strrchr(dir
, DIRSEP_CHAR
);
1235 if (dir_end
!= NULL
) {
1239 if (!f
->name
|| !f
->name
[0]) {
1240 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1241 status
= NT_STATUS_UNSUCCESSFUL
;
1245 if (f
->attr
& FILE_ATTRIBUTE_DIRECTORY
) {
1246 struct cacl_callback_state dir_cbstate
;
1247 uint16_t attribute
= FILE_ATTRIBUTE_DIRECTORY
1248 | FILE_ATTRIBUTE_SYSTEM
1249 | FILE_ATTRIBUTE_HIDDEN
;
1252 /* ignore special '.' & '..' */
1253 if ((f
->name
== NULL
) || ISDOT(f
->name
) || ISDOTDOT(f
->name
)) {
1254 status
= NT_STATUS_OK
;
1258 mask2
= build_dirname(dirctx
, mask
, dir
, f
->name
);
1259 if (mask2
== NULL
) {
1260 status
= NT_STATUS_NO_MEMORY
;
1265 status
= cli_resolve_path(dirctx
, "", creds
, cli
,
1266 mask2
, &targetcli
, &targetpath
);
1267 if (!NT_STATUS_IS_OK(status
)) {
1272 * prepare path to caclfile, remove any existing wildcard
1273 * chars and convert path separators.
1276 caclfile
= talloc_strdup(dirctx
, targetpath
);
1278 status
= NT_STATUS_NO_MEMORY
;
1281 dir_end
= strrchr(caclfile
, '*');
1282 if (dir_end
!= NULL
) {
1286 string_replace(caclfile
, '/', '\\');
1288 * make directory specific copy of cbstate here
1289 * (for this directory level) to be available as
1290 * the parent cbstate for the children of this directory.
1291 * Note: cbstate is overwritten for the current file being
1294 dir_cbstate
= *cbstate
;
1295 dir_cbstate
.cli
= targetcli
;
1298 * propagate any inherited ace from our parent
1300 status
= propagate_inherited_aces(caclfile
, &dir_cbstate
);
1301 if (!NT_STATUS_IS_OK(status
)) {
1306 * get inheritable ace(s) for this dir/container
1307 * that will be propagated to its children
1309 status
= get_inheritable_aces(dirctx
, caclfile
,
1311 if (!NT_STATUS_IS_OK(status
)) {
1316 * ensure cacl_set_cb gets called for children
1317 * of this directory (targetpath)
1319 status
= cli_list(targetcli
, targetpath
,
1320 attribute
, cacl_set_cb
,
1321 (void *)&dir_cbstate
);
1323 if (!NT_STATUS_IS_OK(status
)) {
1329 * build full path to caclfile and replace '/' with '\' so
1330 * other utility functions can deal with it
1333 targetpath
= talloc_asprintf(dirctx
, "%s/%s", dir
, f
->name
);
1335 status
= NT_STATUS_NO_MEMORY
;
1338 string_replace(targetpath
, '/', '\\');
1340 /* attempt to propagate any inherited ace to file caclfile */
1341 status
= propagate_inherited_aces(targetpath
, cbstate
);
1343 if (!NT_STATUS_IS_OK(status
)) {
1347 status
= NT_STATUS_OK
;
1349 if (!NT_STATUS_IS_OK(status
)) {
1350 d_printf("error %s: processing %s\n",
1354 TALLOC_FREE(dirctx
);
1360 * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1361 * helper callback function 'cacl_set_cb' handles the child elements processed
1364 static int inheritance_cacl_set(char *filename
,
1365 struct cacl_callback_state
*cbstate
)
1371 struct cli_state
*cli
= cbstate
->cli
;
1372 TALLOC_CTX
*ctx
= NULL
;
1373 bool isdirectory
= false;
1374 uint16_t attribute
= FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_SYSTEM
1375 | FILE_ATTRIBUTE_HIDDEN
;
1376 ctx
= talloc_init("inherit_set");
1378 d_printf("out of memory\n");
1379 result
= EXIT_FAILED
;
1383 /* ensure we have a filename that starts with '\' */
1384 if (!filename
|| *filename
!= DIRSEP_CHAR
) {
1385 /* illegal or no filename */
1386 result
= EXIT_FAILED
;
1387 d_printf("illegal or missing name '%s'\n", filename
);
1392 fileattr
= get_fileinfo(cli
, filename
);
1393 isdirectory
= (fileattr
& FILE_ATTRIBUTE_DIRECTORY
)
1394 == FILE_ATTRIBUTE_DIRECTORY
;
1397 * if we've got as far as here then we have already evaluated
1406 /* make sure we have a trailing '\*' for directory */
1408 mask
= talloc_strdup(ctx
, filename
);
1409 } else if (strlen(filename
) > 1) {
1411 * if the passed file name doesn't have a trailing '\'
1414 char *name_end
= strrchr(filename
, DIRSEP_CHAR
);
1415 if (name_end
!= filename
+ strlen(filename
) + 1) {
1416 mask
= talloc_asprintf(ctx
, "%s\\*", filename
);
1418 mask
= talloc_strdup(ctx
, filename
);
1421 /* filename is a single '\', just append '*' */
1422 mask
= talloc_asprintf_append(mask
, "%s*", filename
);
1426 result
= EXIT_FAILED
;
1431 * prepare for automatic propagation of the acl passed on the
1435 ntstatus
= prepare_inheritance_propagation(ctx
, filename
,
1437 if (!NT_STATUS_IS_OK(ntstatus
)) {
1438 d_printf("error: %s processing %s\n",
1439 nt_errstr(ntstatus
), filename
);
1440 result
= EXIT_FAILED
;
1444 result
= cacl_set_from_sd(cli
, filename
, cbstate
->aclsd
,
1445 cbstate
->mode
, cbstate
->numeric
);
1448 * strictly speaking it could be considered an error if a file was
1449 * specified with '--propagate-inheritance'. However we really want
1450 * to eventually get rid of '--propagate-inheritance' so we will be
1451 * more forgiving here and instead just exit early.
1453 if (!isdirectory
|| (result
!= EXIT_OK
)) {
1457 /* check if there is actually any need to propagate */
1458 if (cbstate
->acl_no_propagate
) {
1461 /* get inheritable attributes this parent container (e.g. filename) */
1462 ntstatus
= get_inheritable_aces(ctx
, filename
, cbstate
);
1463 if (NT_STATUS_IS_OK(ntstatus
)) {
1464 /* process children */
1465 ntstatus
= cli_list(cli
, mask
, attribute
,
1470 if (!NT_STATUS_IS_OK(ntstatus
)) {
1471 d_printf("error: %s processing %s\n",
1472 nt_errstr(ntstatus
), filename
);
1473 result
= EXIT_FAILED
;
1482 /****************************************************************************
1484 ****************************************************************************/
1485 int main(int argc
, char *argv
[])
1487 const char **argv_const
= discard_const_p(const char *, argv
);
1490 enum acl_mode mode
= SMB_ACL_SET
;
1491 static char *the_acl
= NULL
;
1492 enum chown_mode change_mode
= REQUEST_NONE
;
1495 char *filename
= NULL
;
1497 /* numeric is set when the user wants numeric SIDs and ACEs rather
1498 than going via LSA calls to resolve them */
1500 struct cli_state
*targetcli
= NULL
;
1501 struct cli_credentials
*creds
= NULL
;
1502 char *targetfile
= NULL
;
1505 struct loadparm_context
*lp_ctx
= NULL
;
1507 struct poptOption long_options
[] = {
1510 .longName
= "delete",
1512 .argInfo
= POPT_ARG_STRING
,
1515 .descrip
= "Delete an acl",
1516 .argDescrip
= "ACL",
1519 .longName
= "modify",
1521 .argInfo
= POPT_ARG_STRING
,
1524 .descrip
= "Modify an acl",
1525 .argDescrip
= "ACL",
1530 .argInfo
= POPT_ARG_STRING
,
1533 .descrip
= "Add an acl",
1534 .argDescrip
= "ACL",
1539 .argInfo
= POPT_ARG_STRING
,
1542 .descrip
= "Set acls",
1543 .argDescrip
= "ACLS",
1546 .longName
= "chown",
1548 .argInfo
= POPT_ARG_STRING
,
1551 .descrip
= "Change ownership of a file",
1552 .argDescrip
= "USERNAME",
1555 .longName
= "chgrp",
1557 .argInfo
= POPT_ARG_STRING
,
1560 .descrip
= "Change group ownership of a file",
1561 .argDescrip
= "GROUPNAME",
1564 .longName
= "inherit",
1566 .argInfo
= POPT_ARG_STRING
,
1569 .descrip
= "Inherit allow|remove|copy",
1572 .longName
= "propagate-inheritance",
1574 .argInfo
= POPT_ARG_NONE
,
1575 .arg
= &inheritance
,
1577 .descrip
= "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
1580 .longName
= "numeric",
1582 .argInfo
= POPT_ARG_NONE
,
1585 .descrip
= "Don't resolve sids or masks to names",
1590 .argInfo
= POPT_ARG_NONE
,
1593 .descrip
= "Output and input acls in sddl format",
1596 .longName
= "query-security-info",
1598 .argInfo
= POPT_ARG_INT
,
1599 .arg
= &query_sec_info
,
1601 .descrip
= "The security-info flags for queries"
1604 .longName
= "set-security-info",
1606 .argInfo
= POPT_ARG_INT
,
1607 .arg
= &set_sec_info
,
1609 .descrip
= "The security-info flags for modifications"
1612 .longName
= "test-args",
1614 .argInfo
= POPT_ARG_NONE
,
1617 .descrip
= "Test arguments"
1620 .longName
= "domain-sid",
1622 .argInfo
= POPT_ARG_STRING
,
1625 .descrip
= "Domain SID for sddl",
1626 .argDescrip
= "SID"},
1628 .longName
= "maximum-access",
1630 .argInfo
= POPT_ARG_NONE
,
1633 .descrip
= "Query maximum permissions",
1636 POPT_COMMON_CONNECTION
1637 POPT_COMMON_CREDENTIALS
1643 struct cli_state
*cli
;
1644 TALLOC_CTX
*frame
= talloc_stackframe();
1645 const char *owner_username
= "";
1650 ok
= samba_cmdline_init(frame
,
1651 SAMBA_CMDLINE_CONFIG_CLIENT
,
1652 false /* require_smbconf */);
1654 DBG_ERR("Failed to init cmdline parser!\n");
1658 lp_ctx
= samba_cmdline_get_lp_ctx();
1659 /* set default debug level to 1 regardless of what smb.conf sets */
1660 lpcfg_set_cmdline(lp_ctx
, "log level", "1");
1664 pc
= samba_popt_get_context(getprogname(),
1670 DBG_ERR("Failed to setup popt context!\n");
1675 poptSetOtherOptionHelp(pc
, "//server1/share1 filename\nACLs look like: "
1676 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
1678 while ((opt
= poptGetNextOpt(pc
)) != -1) {
1681 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
1686 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
1687 mode
= SMB_ACL_DELETE
;
1691 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
1692 mode
= SMB_ACL_MODIFY
;
1696 the_acl
= smb_xstrdup(poptGetOptArg(pc
));
1701 owner_username
= poptGetOptArg(pc
);
1702 change_mode
= REQUEST_CHOWN
;
1706 owner_username
= poptGetOptArg(pc
);
1707 change_mode
= REQUEST_CHGRP
;
1711 owner_username
= poptGetOptArg(pc
);
1712 change_mode
= REQUEST_INHERIT
;
1715 lpcfg_set_cmdline(lp_ctx
, "client max protocol", poptGetOptArg(pc
));
1720 case POPT_ERROR_BADOPT
:
1721 fprintf(stderr
, "\nInvalid option %s: %s\n\n",
1722 poptBadOption(pc
, 0), poptStrerror(opt
));
1723 poptPrintUsage(pc
, stderr
, 0);
1727 if (inheritance
&& !the_acl
) {
1728 poptPrintUsage(pc
, stderr
, 0);
1732 if(!poptPeekArg(pc
)) {
1733 poptPrintUsage(pc
, stderr
, 0);
1737 path
= talloc_strdup(frame
, poptGetArg(pc
));
1742 if (strncmp(path
, "\\\\", 2) && strncmp(path
, "//", 2)) {
1743 printf("Invalid argument: %s\n", path
);
1747 if(!poptPeekArg(pc
)) {
1748 poptPrintUsage(pc
, stderr
, 0);
1752 filename
= talloc_strdup(frame
, poptGetArg(pc
));
1757 poptFreeContext(pc
);
1758 samba_cmdline_burn(argc
, argv
);
1760 string_replace(path
,'/','\\');
1762 server
= talloc_strdup(frame
, path
+2);
1766 share
= strchr_m(server
,'\\');
1767 if (share
== NULL
) {
1768 printf("Invalid argument\n");
1775 creds
= samba_cmdline_get_creds();
1777 /* Make connection to server */
1779 cli
= connect_one(creds
, server
, share
);
1787 string_replace(filename
, '/', '\\');
1788 if (filename
[0] != '\\') {
1789 filename
= talloc_asprintf(frame
,
1797 status
= cli_resolve_path(frame
,
1804 if (!NT_STATUS_IS_OK(status
)) {
1805 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename
, nt_errstr(status
)));
1809 /* Perform requested action */
1811 if (change_mode
== REQUEST_INHERIT
) {
1812 result
= inherit(targetcli
, targetfile
, owner_username
);
1813 } else if (change_mode
!= REQUEST_NONE
) {
1814 result
= owner_set(targetcli
, change_mode
, targetfile
, owner_username
);
1815 } else if (the_acl
) {
1817 struct cacl_callback_state cbstate
= {
1824 result
= inheritance_cacl_set(targetfile
, &cbstate
);
1826 result
= cacl_set(targetcli
,
1833 result
= cacl_dump(targetcli
, targetfile
, numeric
);