s3:client: Fix old-style function definition
[Samba.git] / source3 / utils / smbcacls.c
blob71cd93b8bc725ab94fa9c6e857cda1f7da63e5a7
1 /*
2 Unix SMB/CIFS implementation.
3 ACL get/set utility
5 Copyright (C) Andrew Tridgell 2000
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2000
8 Copyright (C) Jelmer Vernooij 2003
9 Copyright (C) Noel Power <noel.power@suse.com> 2013
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "lib/cmdline/cmdline.h"
27 #include "rpc_client/cli_pipe.h"
28 #include "../librpc/gen_ndr/ndr_lsa.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../libcli/security/security.h"
31 #include "libsmb/libsmb.h"
32 #include "libsmb/clirap.h"
33 #include "passdb/machine_sid.h"
34 #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 #include "util_sd.h"
37 static char DIRSEP_CHAR = '\\';
39 static int inheritance = 0;
40 static int test_args;
41 static int sddl;
42 static int query_sec_info = -1;
43 static int set_sec_info = -1;
44 static bool want_mxac;
46 static const char *domain_sid = NULL;
48 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
49 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
50 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
52 struct cacl_callback_state {
53 struct cli_credentials *creds;
54 struct cli_state *cli;
55 struct security_descriptor *aclsd;
56 struct security_acl *acl_to_add;
57 enum acl_mode mode;
58 char *the_acl;
59 bool acl_no_propagate;
60 bool numeric;
63 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
64 struct dom_sid *sid)
66 union lsa_PolicyInformation *info = NULL;
67 struct smbXcli_tcon *orig_tcon = NULL;
68 char *orig_share = NULL;
69 struct rpc_pipe_client *rpc_pipe = NULL;
70 struct policy_handle handle;
71 NTSTATUS status, result;
72 TALLOC_CTX *frame = talloc_stackframe();
74 if (cli_state_has_tcon(cli)) {
75 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
78 status = cli_tree_connect(cli, "IPC$", "?????", NULL);
79 if (!NT_STATUS_IS_OK(status)) {
80 goto done;
83 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
84 if (!NT_STATUS_IS_OK(status)) {
85 goto tdis;
88 status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
89 GENERIC_EXECUTE_ACCESS, &handle);
90 if (!NT_STATUS_IS_OK(status)) {
91 goto tdis;
94 status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
95 frame, &handle,
96 LSA_POLICY_INFO_DOMAIN,
97 &info, &result);
99 if (any_nt_status_not_ok(status, result, &status)) {
100 goto tdis;
103 *sid = *info->domain.sid;
105 tdis:
106 TALLOC_FREE(rpc_pipe);
107 cli_tdis(cli);
108 done:
109 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
110 TALLOC_FREE(frame);
111 return status;
114 static struct dom_sid *get_domain_sid(struct cli_state *cli)
116 NTSTATUS status;
117 struct dom_sid_buf buf;
119 struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
120 if (sid == NULL) {
121 DEBUG(0, ("Out of memory\n"));
122 return NULL;
125 if (domain_sid) {
126 if (!dom_sid_parse(domain_sid, sid)) {
127 DEBUG(0,("failed to parse domain sid\n"));
128 TALLOC_FREE(sid);
130 } else {
131 status = cli_lsa_lookup_domain_sid(cli, sid);
133 if (!NT_STATUS_IS_OK(status)) {
134 DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
135 TALLOC_FREE(sid);
140 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
141 return sid;
144 /* add an ACE to a list of ACEs in a struct security_acl */
145 static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
146 const struct security_ace *ace)
149 struct security_acl *acl = *the_acl;
151 if (acl == NULL) {
152 acl = make_sec_acl(ctx, 3, 0, NULL);
153 if (acl == NULL) {
154 return false;
158 if (acl->num_aces == UINT32_MAX) {
159 return false;
161 ADD_TO_ARRAY(
162 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
163 *the_acl = acl;
164 return True;
167 static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
169 return add_ace_with_ctx(talloc_tos(), the_acl, ace);
172 /* parse a ascii version of a security descriptor */
173 static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
175 const char *p = str;
176 char *tok;
177 struct security_descriptor *ret = NULL;
178 size_t sd_size;
179 struct dom_sid owner_sid = { .num_auths = 0 };
180 bool have_owner = false;
181 struct dom_sid group_sid = { .num_auths = 0 };
182 bool have_group = false;
183 struct security_acl *dacl=NULL;
184 int revision=1;
186 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
187 if (strncmp(tok,"REVISION:", 9) == 0) {
188 revision = strtol(tok+9, NULL, 16);
189 continue;
192 if (strncmp(tok,"OWNER:", 6) == 0) {
193 if (have_owner) {
194 printf("Only specify owner once\n");
195 goto done;
197 if (!StringToSid(cli, &owner_sid, tok+6)) {
198 printf("Failed to parse owner sid\n");
199 goto done;
201 have_owner = true;
202 continue;
205 if (strncmp(tok,"GROUP:", 6) == 0) {
206 if (have_group) {
207 printf("Only specify group once\n");
208 goto done;
210 if (!StringToSid(cli, &group_sid, tok+6)) {
211 printf("Failed to parse group sid\n");
212 goto done;
214 have_group = true;
215 continue;
218 if (strncmp(tok,"ACL:", 4) == 0) {
219 struct security_ace ace;
220 if (!parse_ace(cli, &ace, tok+4)) {
221 goto done;
223 if(!add_ace(&dacl, &ace)) {
224 printf("Failed to add ACL %s\n", tok);
225 goto done;
227 continue;
230 printf("Failed to parse token '%s' in security descriptor,\n", tok);
231 goto done;
234 ret = make_sec_desc(
235 ctx,
236 revision,
237 SEC_DESC_SELF_RELATIVE,
238 have_owner ? &owner_sid : NULL,
239 have_group ? &group_sid : NULL,
240 NULL,
241 dacl,
242 &sd_size);
244 done:
245 return ret;
248 /*****************************************************
249 get fileinfo for filename
250 *******************************************************/
251 static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
253 uint16_t fnum = (uint16_t)-1;
254 NTSTATUS status;
255 struct smb_create_returns cr = {0};
257 /* The desired access below is the only one I could find that works
258 with NT4, W2KP and Samba */
260 status = cli_ntcreate(
261 cli, /* cli */
262 filename, /* fname */
263 0, /* CreatFlags */
264 READ_CONTROL_ACCESS, /* CreatFlags */
265 0, /* FileAttributes */
266 FILE_SHARE_READ|
267 FILE_SHARE_WRITE, /* ShareAccess */
268 FILE_OPEN, /* CreateDisposition */
269 0x0, /* CreateOptions */
270 0x0, /* SecurityFlags */
271 &fnum, /* pfid */
272 &cr); /* cr */
273 if (!NT_STATUS_IS_OK(status)) {
274 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
275 return 0;
278 cli_close(cli, fnum);
279 return cr.file_attributes;
282 /*****************************************************
283 get sec desc for filename
284 *******************************************************/
285 static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
286 struct cli_state *cli,
287 const char *filename)
289 uint16_t fnum = (uint16_t)-1;
290 struct security_descriptor *sd;
291 NTSTATUS status;
292 uint32_t sec_info;
293 uint32_t desired_access = 0;
295 if (query_sec_info == -1) {
296 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
297 } else {
298 sec_info = query_sec_info;
301 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
302 desired_access |= SEC_STD_READ_CONTROL;
304 if (sec_info & SECINFO_SACL) {
305 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
308 if (desired_access == 0) {
309 desired_access |= SEC_STD_READ_CONTROL;
312 status = cli_ntcreate(cli, filename, 0, desired_access,
313 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
314 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
315 if (!NT_STATUS_IS_OK(status)) {
316 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
317 return NULL;
320 status = cli_query_security_descriptor(cli, fnum, sec_info,
321 ctx, &sd);
323 cli_close(cli, fnum);
325 if (!NT_STATUS_IS_OK(status)) {
326 printf("Failed to get security descriptor: %s\n",
327 nt_errstr(status));
328 return NULL;
330 return sd;
333 static struct security_descriptor *get_secdesc(struct cli_state *cli,
334 const char *filename)
336 return get_secdesc_with_ctx(talloc_tos(), cli, filename);
338 /*****************************************************
339 set sec desc for filename
340 *******************************************************/
341 static bool set_secdesc(struct cli_state *cli, const char *filename,
342 struct security_descriptor *sd)
344 uint16_t fnum = (uint16_t)-1;
345 bool result=true;
346 NTSTATUS status;
347 uint32_t desired_access = 0;
348 uint32_t sec_info;
350 if (set_sec_info == -1) {
351 sec_info = 0;
353 if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
354 sec_info |= SECINFO_DACL;
356 if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
357 sec_info |= SECINFO_SACL;
359 if (sd->owner_sid) {
360 sec_info |= SECINFO_OWNER;
362 if (sd->group_sid) {
363 sec_info |= SECINFO_GROUP;
365 } else {
366 sec_info = set_sec_info;
369 /* Make the desired_access more specific. */
370 if (sec_info & SECINFO_DACL) {
371 desired_access |= SEC_STD_WRITE_DAC;
373 if (sec_info & SECINFO_SACL) {
374 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
376 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
377 desired_access |= SEC_STD_WRITE_OWNER;
380 status = cli_ntcreate(cli, filename, 0,
381 desired_access,
382 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
383 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
384 if (!NT_STATUS_IS_OK(status)) {
385 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
386 return false;
389 status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
390 if (!NT_STATUS_IS_OK(status)) {
391 printf("ERROR: security descriptor set failed: %s\n",
392 nt_errstr(status));
393 result=false;
396 cli_close(cli, fnum);
397 return result;
400 /*****************************************************
401 get maximum access for a file
402 *******************************************************/
403 static int cacl_mxac(struct cli_state *cli, const char *filename)
405 NTSTATUS status;
406 uint32_t mxac;
408 status = cli_query_mxac(cli, filename, &mxac);
409 if (!NT_STATUS_IS_OK(status)) {
410 printf("Failed to get mxac: %s\n", nt_errstr(status));
411 return EXIT_FAILED;
414 printf("Maximum access: 0x%x\n", mxac);
416 return EXIT_OK;
420 /*****************************************************
421 dump the acls for a file
422 *******************************************************/
423 static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
425 struct security_descriptor *sd;
426 int ret;
428 if (test_args) {
429 return EXIT_OK;
432 sd = get_secdesc(cli, filename);
433 if (sd == NULL) {
434 return EXIT_FAILED;
437 if (sddl) {
438 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
439 if (str == NULL) {
440 return EXIT_FAILED;
442 printf("%s\n", str);
443 TALLOC_FREE(str);
444 } else {
445 sec_desc_print(cli, stdout, sd, numeric);
448 if (want_mxac) {
449 ret = cacl_mxac(cli, filename);
450 if (ret != EXIT_OK) {
451 return ret;
455 return EXIT_OK;
458 /*****************************************************
459 Change the ownership or group ownership of a file. Just
460 because the NT docs say this can't be done :-). JRA.
461 *******************************************************/
463 static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
464 const char *filename, const char *new_username)
466 struct dom_sid sid;
467 struct security_descriptor *sd;
468 size_t sd_size;
470 if (!StringToSid(cli, &sid, new_username))
471 return EXIT_PARSE_ERROR;
473 sd = make_sec_desc(talloc_tos(),
474 SECURITY_DESCRIPTOR_REVISION_1,
475 SEC_DESC_SELF_RELATIVE,
476 (change_mode == REQUEST_CHOWN) ? &sid : NULL,
477 (change_mode == REQUEST_CHGRP) ? &sid : NULL,
478 NULL, NULL, &sd_size);
480 if (!set_secdesc(cli, filename, sd)) {
481 return EXIT_FAILED;
484 return EXIT_OK;
488 /* The MSDN is contradictory over the ordering of ACE entries in an
489 ACL. However NT4 gives a "The information may have been modified
490 by a computer running Windows NT 5.0" if denied ACEs do not appear
491 before allowed ACEs. At
492 http://technet.microsoft.com/en-us/library/cc781716.aspx the
493 canonical order is specified as "Explicit Deny, Explicit Allow,
494 Inherited ACEs unchanged" */
496 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
498 if (security_ace_equal(ace1, ace2))
499 return 0;
501 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
502 !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
503 return 1;
504 if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
505 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
506 return -1;
507 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
508 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
509 return ace1 - ace2;
511 if (ace1->type != ace2->type)
512 return ace2->type - ace1->type;
514 if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
515 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
517 if (ace1->flags != ace2->flags)
518 return ace1->flags - ace2->flags;
520 if (ace1->access_mask != ace2->access_mask)
521 return ace1->access_mask - ace2->access_mask;
523 if (ace1->size != ace2->size)
524 return ace1->size - ace2->size;
526 return memcmp(ace1, ace2, sizeof(struct security_ace));
529 static void sort_acl(struct security_acl *the_acl)
531 uint32_t i;
532 if (!the_acl) return;
534 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
536 for (i=1;i<the_acl->num_aces;) {
537 if (security_ace_equal(&the_acl->aces[i-1],
538 &the_acl->aces[i])) {
539 ARRAY_DEL_ELEMENT(
540 the_acl->aces, i, the_acl->num_aces);
541 the_acl->num_aces--;
542 } else {
543 i++;
548 /*****************************************************
549 set the ACLs on a file given a security descriptor
550 *******************************************************/
552 static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
553 struct security_descriptor *sd, enum acl_mode mode,
554 bool numeric)
556 struct security_descriptor *old = NULL;
557 uint32_t i, j;
558 size_t sd_size;
559 int result = EXIT_OK;
561 if (!sd) return EXIT_PARSE_ERROR;
562 if (test_args) return EXIT_OK;
564 if (mode != SMB_ACL_SET) {
566 * Do not fetch old ACL when it will be overwritten
567 * completely with a new one.
569 old = get_secdesc(cli, filename);
571 if (!old) {
572 return EXIT_FAILED;
576 /* the logic here is rather more complex than I would like */
577 switch (mode) {
578 case SMB_ACL_DELETE:
579 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
580 bool found = False;
582 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
583 if (security_ace_equal(&sd->dacl->aces[i],
584 &old->dacl->aces[j])) {
585 uint32_t k;
586 for (k=j; k<old->dacl->num_aces-1;k++) {
587 old->dacl->aces[k] = old->dacl->aces[k+1];
589 old->dacl->num_aces--;
590 found = True;
591 break;
595 if (!found) {
596 printf("ACL for ACE:");
597 print_ace(cli, stdout, &sd->dacl->aces[i],
598 numeric);
599 printf(" not found\n");
602 break;
604 case SMB_ACL_MODIFY:
605 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
606 bool found = False;
608 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
609 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
610 &old->dacl->aces[j].trustee)) {
611 old->dacl->aces[j] = sd->dacl->aces[i];
612 found = True;
616 if (!found) {
617 fstring str;
619 SidToString(cli, str,
620 &sd->dacl->aces[i].trustee,
621 numeric);
622 printf("ACL for SID %s not found\n", str);
626 if (sd->owner_sid) {
627 old->owner_sid = sd->owner_sid;
630 if (sd->group_sid) {
631 old->group_sid = sd->group_sid;
634 break;
636 case SMB_ACL_ADD:
637 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
638 add_ace(&old->dacl, &sd->dacl->aces[i]);
640 break;
642 case SMB_ACL_SET:
643 old = sd;
644 break;
647 /* Denied ACE entries must come before allowed ones */
648 sort_acl(old->dacl);
650 /* Create new security descriptor and set it */
652 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
653 But if we're sending an owner, even if it's the same as the one
654 that already exists then W2K3 insists we open with WRITE_OWNER access.
655 I need to check that setting a SD with no owner set works against WNT
656 and W2K. JRA.
659 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
660 old->owner_sid, old->group_sid,
661 NULL, old->dacl, &sd_size);
663 if (!set_secdesc(cli, filename, sd)) {
664 result = EXIT_FAILED;
667 return result;
670 /*****************************************************
671 set the ACLs on a file given an ascii description
672 *******************************************************/
674 static int cacl_set(struct cli_state *cli, const char *filename,
675 char *the_acl, enum acl_mode mode, bool numeric)
677 struct security_descriptor *sd = NULL;
679 if (sddl) {
680 sd = sddl_decode(talloc_tos(), the_acl, get_global_sam_sid());
681 } else {
682 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
685 if (sd == NULL) {
686 return EXIT_PARSE_ERROR;
688 if (test_args) {
689 return EXIT_OK;
691 return cacl_set_from_sd(cli, filename, sd, mode, numeric);
694 /*****************************************************
695 set the inherit on a file
696 *******************************************************/
697 static int inherit(struct cli_state *cli, const char *filename,
698 const char *type)
700 struct security_descriptor *old,*sd;
701 uint32_t oldattr;
702 size_t sd_size;
703 int result = EXIT_OK;
705 old = get_secdesc(cli, filename);
707 if (!old) {
708 return EXIT_FAILED;
711 oldattr = get_fileinfo(cli,filename);
713 if (strcmp(type,"allow")==0) {
714 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
715 SEC_DESC_DACL_PROTECTED) {
716 uint32_t i;
717 char *parentname,*temp;
718 struct security_descriptor *parent;
719 temp = talloc_strdup(talloc_tos(), filename);
721 old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
723 /* look at parent and copy in all its inheritable ACL's. */
724 string_replace(temp, '\\', '/');
725 if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
726 return EXIT_FAILED;
728 string_replace(parentname, '/', '\\');
729 parent = get_secdesc(cli,parentname);
730 if (parent == NULL) {
731 return EXIT_FAILED;
733 for (i=0;i<parent->dacl->num_aces;i++) {
734 struct security_ace *ace=&parent->dacl->aces[i];
735 /* Add inherited flag to all aces */
736 ace->flags=ace->flags|
737 SEC_ACE_FLAG_INHERITED_ACE;
738 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
739 if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
740 SEC_ACE_FLAG_CONTAINER_INHERIT) {
741 add_ace(&old->dacl, ace);
743 } else {
744 if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
745 SEC_ACE_FLAG_OBJECT_INHERIT) {
746 /* clear flags for files */
747 ace->flags=0;
748 add_ace(&old->dacl, ace);
752 } else {
753 printf("Already set to inheritable permissions.\n");
754 return EXIT_FAILED;
756 } else if (strcmp(type,"remove")==0) {
757 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
758 SEC_DESC_DACL_PROTECTED) {
759 old->type=old->type | SEC_DESC_DACL_PROTECTED;
761 /* remove all inherited ACL's. */
762 if (old->dacl) {
763 int i;
764 struct security_acl *temp=old->dacl;
765 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
766 for (i=temp->num_aces-1;i>=0;i--) {
767 struct security_ace *ace=&temp->aces[i];
768 /* Remove all ace with INHERITED flag set */
769 if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
770 SEC_ACE_FLAG_INHERITED_ACE) {
771 add_ace(&old->dacl,ace);
775 } else {
776 printf("Already set to no inheritable permissions.\n");
777 return EXIT_FAILED;
779 } else if (strcmp(type,"copy")==0) {
780 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
781 SEC_DESC_DACL_PROTECTED) {
782 old->type=old->type | SEC_DESC_DACL_PROTECTED;
785 * convert all inherited ACL's to non
786 * inherited ACL's.
788 if (old->dacl) {
789 uint32_t i;
790 for (i=0;i<old->dacl->num_aces;i++) {
791 struct security_ace *ace=&old->dacl->aces[i];
792 /* Remove INHERITED FLAG from all aces */
793 ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
796 } else {
797 printf("Already set to no inheritable permissions.\n");
798 return EXIT_FAILED;
802 /* Denied ACE entries must come before allowed ones */
803 sort_acl(old->dacl);
805 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
806 old->owner_sid, old->group_sid,
807 NULL, old->dacl, &sd_size);
809 if (!set_secdesc(cli, filename, sd)) {
810 result = EXIT_FAILED;
813 return result;
816 /*****************************************************
817 Return a connection to a server.
818 *******************************************************/
819 static struct cli_state *connect_one(struct cli_credentials *creds,
820 const char *server, const char *share)
822 struct cli_state *c = NULL;
823 NTSTATUS nt_status;
824 uint32_t flags = 0;
826 nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
827 NULL, 0,
828 share, "?????",
829 creds,
830 flags);
831 if (!NT_STATUS_IS_OK(nt_status)) {
832 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
833 return NULL;
836 return c;
840 * Process resulting combination of mask & fname ensuring
841 * terminated with wildcard
843 static char *build_dirname(TALLOC_CTX *ctx,
844 const char *mask, char *dir, char *fname)
846 char *mask2 = NULL;
847 char *p = NULL;
849 mask2 = talloc_strdup(ctx, mask);
850 if (!mask2) {
851 return NULL;
853 p = strrchr_m(mask2, DIRSEP_CHAR);
854 if (p) {
855 p[1] = 0;
856 } else {
857 mask2[0] = '\0';
859 mask2 = talloc_asprintf_append(mask2,
860 "%s\\*",
861 fname);
862 return mask2;
866 * Returns the a copy of the ACL flags in ace modified according
867 * to some inheritance rules.
868 * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
869 * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
870 * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
871 * stripped from flags to be propagated to non-container children
872 * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
873 * stripped from flags to be propagated if the NP flag
874 * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
877 static uint8_t get_flags_to_propagate(bool is_container,
878 struct security_ace *ace)
880 uint8_t newflags = ace->flags;
881 /* OBJECT inheritance */
882 bool acl_objinherit = (ace->flags &
883 SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
884 /* CONTAINER inheritance */
885 bool acl_cntrinherit = (ace->flags &
886 SEC_ACE_FLAG_CONTAINER_INHERIT) ==
887 SEC_ACE_FLAG_CONTAINER_INHERIT;
888 /* PROHIBIT inheritance */
889 bool prohibit_inheritance = ((ace->flags &
890 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
891 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
893 /* Assume we are not propagating the ACE */
895 newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
896 /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
897 if (acl_cntrinherit || acl_objinherit) {
899 * object inherit ( alone ) on a container needs
900 * SEC_ACE_FLAG_INHERIT_ONLY
902 if (is_container) {
903 if (acl_objinherit && !acl_cntrinherit) {
904 newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
907 * this is tricky, the only time we would not
908 * propagate the ace for a container is if
909 * prohibit_inheritance is set and object inheritance
910 * alone is set
912 if ((prohibit_inheritance
913 && acl_objinherit
914 && !acl_cntrinherit) == false) {
915 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
917 } else {
919 * don't apply object/container inheritance flags to
920 * non dirs
922 newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
923 | SEC_ACE_FLAG_CONTAINER_INHERIT
924 | SEC_ACE_FLAG_INHERIT_ONLY);
926 * only apply ace to file if object inherit
928 if (acl_objinherit) {
929 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
933 /* if NP is specified strip NP and all OI/CI INHERIT flags */
934 if (prohibit_inheritance) {
935 newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
936 | SEC_ACE_FLAG_CONTAINER_INHERIT
937 | SEC_ACE_FLAG_INHERIT_ONLY
938 | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
941 return newflags;
945 * This function builds a new acl for 'caclfile', first it removes any
946 * existing inheritable ace(s) from the current acl of caclfile, secondly it
947 * applies any inheritable acls of the parent of caclfile ( inheritable acls of
948 * caclfile's parent are passed via acl_to_add member of cbstate )
951 static NTSTATUS propagate_inherited_aces(char *caclfile,
952 struct cacl_callback_state *cbstate)
954 TALLOC_CTX *aclctx = NULL;
955 NTSTATUS status;
956 int result;
957 int fileattr;
958 struct security_descriptor *old = NULL;
959 bool is_container = false;
960 struct security_acl *acl_to_add = cbstate->acl_to_add;
961 struct security_acl *acl_to_remove = NULL;
962 uint32_t i, j;
964 aclctx = talloc_new(NULL);
965 if (aclctx == NULL) {
966 return NT_STATUS_NO_MEMORY;
968 old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
970 if (!old) {
971 status = NT_STATUS_UNSUCCESSFUL;
972 goto out;
975 /* inhibit propagation? */
976 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
977 SEC_DESC_DACL_PROTECTED){
978 status = NT_STATUS_OK;
979 goto out;
982 fileattr = get_fileinfo(cbstate->cli, caclfile);
983 is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
985 /* find acl(s) that are inherited */
986 for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
988 if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
989 if (!add_ace_with_ctx(aclctx, &acl_to_remove,
990 &old->dacl->aces[j])) {
991 status = NT_STATUS_NO_MEMORY;
992 goto out;
997 /* remove any acl(s) that are inherited */
998 if (acl_to_remove) {
999 for (i = 0; i < acl_to_remove->num_aces; i++) {
1000 struct security_ace ace = acl_to_remove->aces[i];
1001 for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1003 if (security_ace_equal(&ace,
1004 &old->dacl->aces[j])) {
1005 uint32_t k;
1006 for (k = j; k < old->dacl->num_aces-1;
1007 k++) {
1008 old->dacl->aces[k] =
1009 old->dacl->aces[k+1];
1011 old->dacl->num_aces--;
1012 break;
1017 /* propagate any inheritable ace to be added */
1018 if (acl_to_add) {
1019 for (i = 0; i < acl_to_add->num_aces; i++) {
1020 struct security_ace ace = acl_to_add->aces[i];
1021 bool is_objectinherit = (ace.flags &
1022 SEC_ACE_FLAG_OBJECT_INHERIT) ==
1023 SEC_ACE_FLAG_OBJECT_INHERIT;
1024 bool is_inherited;
1025 /* don't propagate flags to a file unless OI */
1026 if (!is_objectinherit && !is_container) {
1027 continue;
1030 * adjust flags according to inheritance
1031 * rules
1033 ace.flags = get_flags_to_propagate(is_container, &ace);
1034 is_inherited = (ace.flags &
1035 SEC_ACE_FLAG_INHERITED_ACE) ==
1036 SEC_ACE_FLAG_INHERITED_ACE;
1037 /* don't propagate non inherited flags */
1038 if (!is_inherited) {
1039 continue;
1041 if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1042 status = NT_STATUS_NO_MEMORY;
1043 goto out;
1048 result = cacl_set_from_sd(cbstate->cli, caclfile,
1049 old,
1050 SMB_ACL_SET, cbstate->numeric);
1051 if (result != EXIT_OK) {
1052 status = NT_STATUS_UNSUCCESSFUL;
1053 goto out;
1056 status = NT_STATUS_OK;
1057 out:
1058 TALLOC_FREE(aclctx);
1059 return status;
1063 * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1064 * SEC_ACE_FLAG_CONTAINER_INHERIT
1066 static bool is_inheritable_ace(struct security_ace *ace)
1068 uint8_t flags = ace->flags;
1069 if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1070 | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1071 return true;
1073 return false;
1076 /* This method does some basic sanity checking with respect to automatic
1077 * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1078 * attempts to set inherited permissions directly. Additionally this method
1079 * does some basic initialisation for instance it parses the ACL passed on the
1080 * command line.
1082 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1083 struct cacl_callback_state *cbstate)
1085 NTSTATUS result;
1086 char *the_acl = cbstate->the_acl;
1087 struct cli_state *cli = cbstate->cli;
1088 enum acl_mode mode = cbstate->mode;
1089 struct security_descriptor *sd = NULL;
1090 struct security_descriptor *old = NULL;
1091 uint32_t j;
1092 bool propagate = false;
1094 old = get_secdesc_with_ctx(ctx, cli, filename);
1095 if (old == NULL) {
1096 return NT_STATUS_NO_MEMORY;
1099 /* parse acl passed on the command line */
1100 if (sddl) {
1101 cbstate->aclsd = sddl_decode(ctx, the_acl,
1102 get_global_sam_sid());
1103 } else {
1104 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1107 if (!cbstate->aclsd) {
1108 result = NT_STATUS_UNSUCCESSFUL;
1109 goto out;
1112 sd = cbstate->aclsd;
1114 /* set operation if inheritance is enabled doesn't make sense */
1115 if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1116 SEC_DESC_DACL_PROTECTED)){
1117 d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1118 result = NT_STATUS_UNSUCCESSFUL;
1119 goto out;
1124 * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1125 * flags that are set
1127 for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1128 struct security_ace *ace = &sd->dacl->aces[j];
1129 if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1130 d_printf("Illegal parameter %s\n", the_acl);
1131 result = NT_STATUS_UNSUCCESSFUL;
1132 goto out;
1134 if (!propagate) {
1135 if (is_inheritable_ace(ace)) {
1136 propagate = true;
1141 result = NT_STATUS_OK;
1142 out:
1143 cbstate->acl_no_propagate = !propagate;
1144 return result;
1148 * This method builds inheritable ace(s) from filename (which should be
1149 * a container) that need propagating to children in order to provide
1150 * automatic inheritance. Those inheritable ace(s) are stored in
1151 * acl_to_add member of cbstate for later processing
1152 * (see propagate_inherited_aces)
1154 static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1155 struct cacl_callback_state *cbstate)
1157 NTSTATUS result;
1158 struct cli_state *cli = NULL;
1159 struct security_descriptor *sd = NULL;
1160 struct security_acl *acl_to_add = NULL;
1161 uint32_t j;
1163 cli = cbstate->cli;
1164 sd = get_secdesc_with_ctx(ctx, cli, filename);
1166 if (sd == NULL) {
1167 return NT_STATUS_NO_MEMORY;
1171 * Check if any inheritance related flags are used, if not then
1172 * nothing to do. At the same time populate acls for inheritance
1173 * related ace(s) that need to be added to or deleted from children as
1174 * a result of inheritance propagation.
1177 for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1178 struct security_ace *ace = &sd->dacl->aces[j];
1179 if (is_inheritable_ace(ace)) {
1180 bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1181 if (!added) {
1182 result = NT_STATUS_NO_MEMORY;
1183 goto out;
1187 cbstate->acl_to_add = acl_to_add;
1188 result = NT_STATUS_OK;
1189 out:
1190 return result;
1194 * Callback handler to handle child elements processed by cli_list, we attempt
1195 * to propagate inheritable ace(s) to each child via the function
1196 * propagate_inherited_aces. Children that are themselves directories are passed
1197 * to cli_list again ( to decend the directory structure )
1199 static NTSTATUS cacl_set_cb(struct file_info *f,
1200 const char *mask, void *state)
1202 struct cacl_callback_state *cbstate =
1203 (struct cacl_callback_state *)state;
1204 struct cli_state *cli = NULL;
1205 struct cli_credentials *creds = NULL;
1207 TALLOC_CTX *dirctx = NULL;
1208 NTSTATUS status;
1209 struct cli_state *targetcli = NULL;
1211 char *dir = NULL;
1212 char *dir_end = NULL;
1213 char *mask2 = NULL;
1214 char *targetpath = NULL;
1215 char *caclfile = NULL;
1217 dirctx = talloc_new(NULL);
1218 if (!dirctx) {
1219 status = NT_STATUS_NO_MEMORY;
1220 goto out;
1223 cli = cbstate->cli;
1224 creds = cbstate->creds;
1226 /* Work out the directory. */
1227 dir = talloc_strdup(dirctx, mask);
1228 if (!dir) {
1229 status = NT_STATUS_NO_MEMORY;
1230 goto out;
1233 dir_end = strrchr(dir, DIRSEP_CHAR);
1234 if (dir_end != NULL) {
1235 *dir_end = '\0';
1238 if (!f->name || !f->name[0]) {
1239 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1240 status = NT_STATUS_UNSUCCESSFUL;
1241 goto out;
1244 if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1245 struct cacl_callback_state dir_cbstate;
1246 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1247 | FILE_ATTRIBUTE_SYSTEM
1248 | FILE_ATTRIBUTE_HIDDEN;
1249 dir_end = NULL;
1251 /* ignore special '.' & '..' */
1252 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1253 status = NT_STATUS_OK;
1254 goto out;
1257 mask2 = build_dirname(dirctx, mask, dir, f->name);
1258 if (mask2 == NULL) {
1259 status = NT_STATUS_NO_MEMORY;
1260 goto out;
1263 /* check for dfs */
1264 status = cli_resolve_path(dirctx, "", creds, cli,
1265 mask2, &targetcli, &targetpath);
1266 if (!NT_STATUS_IS_OK(status)) {
1267 goto out;
1271 * prepare path to caclfile, remove any existing wildcard
1272 * chars and convert path separators.
1275 caclfile = talloc_strdup(dirctx, targetpath);
1276 if (!caclfile) {
1277 status = NT_STATUS_NO_MEMORY;
1278 goto out;
1280 dir_end = strrchr(caclfile, '*');
1281 if (dir_end != NULL) {
1282 *dir_end = '\0';
1285 string_replace(caclfile, '/', '\\');
1287 * make directory specific copy of cbstate here
1288 * (for this directory level) to be available as
1289 * the parent cbstate for the children of this directory.
1290 * Note: cbstate is overwritten for the current file being
1291 * processed.
1293 dir_cbstate = *cbstate;
1294 dir_cbstate.cli = targetcli;
1297 * propagate any inherited ace from our parent
1299 status = propagate_inherited_aces(caclfile, &dir_cbstate);
1300 if (!NT_STATUS_IS_OK(status)) {
1301 goto out;
1305 * get inheritable ace(s) for this dir/container
1306 * that will be propagated to its children
1308 status = get_inheritable_aces(dirctx, caclfile,
1309 &dir_cbstate);
1310 if (!NT_STATUS_IS_OK(status)) {
1311 goto out;
1315 * ensure cacl_set_cb gets called for children
1316 * of this directory (targetpath)
1318 status = cli_list(targetcli, targetpath,
1319 attribute, cacl_set_cb,
1320 (void *)&dir_cbstate);
1322 if (!NT_STATUS_IS_OK(status)) {
1323 goto out;
1326 } else {
1328 * build full path to caclfile and replace '/' with '\' so
1329 * other utility functions can deal with it
1332 targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1333 if (!targetpath) {
1334 status = NT_STATUS_NO_MEMORY;
1335 goto out;
1337 string_replace(targetpath, '/', '\\');
1339 /* attempt to propagate any inherited ace to file caclfile */
1340 status = propagate_inherited_aces(targetpath, cbstate);
1342 if (!NT_STATUS_IS_OK(status)) {
1343 goto out;
1346 status = NT_STATUS_OK;
1347 out:
1348 if (!NT_STATUS_IS_OK(status)) {
1349 d_printf("error %s: processing %s\n",
1350 nt_errstr(status),
1351 targetpath);
1353 TALLOC_FREE(dirctx);
1354 return status;
1359 * Wrapper around cl_list to decend the directory tree pointed to by 'filename',
1360 * helper callback function 'cacl_set_cb' handles the child elements processed
1361 * by cli_list.
1363 static int inheritance_cacl_set(char *filename,
1364 struct cacl_callback_state *cbstate)
1366 int result;
1367 NTSTATUS ntstatus;
1368 int fileattr;
1369 char *mask = NULL;
1370 struct cli_state *cli = cbstate->cli;
1371 TALLOC_CTX *ctx = NULL;
1372 bool isdirectory = false;
1373 uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1374 | FILE_ATTRIBUTE_HIDDEN;
1375 ctx = talloc_init("inherit_set");
1376 if (ctx == NULL) {
1377 d_printf("out of memory\n");
1378 result = EXIT_FAILED;
1379 goto out;
1382 /* ensure we have a filename that starts with '\' */
1383 if (!filename || *filename != DIRSEP_CHAR) {
1384 /* illegal or no filename */
1385 result = EXIT_FAILED;
1386 d_printf("illegal or missing name '%s'\n", filename);
1387 goto out;
1391 fileattr = get_fileinfo(cli, filename);
1392 isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1393 == FILE_ATTRIBUTE_DIRECTORY;
1396 * if we've got as far as here then we have already evaluated
1397 * the args.
1399 if (test_args) {
1400 result = EXIT_OK;
1401 goto out;
1404 mask = NULL;
1405 /* make sure we have a trailing '\*' for directory */
1406 if (!isdirectory) {
1407 mask = talloc_strdup(ctx, filename);
1408 } else if (strlen(filename) > 1) {
1410 * if the passed file name doesn't have a trailing '\'
1411 * append it.
1413 char *name_end = strrchr(filename, DIRSEP_CHAR);
1414 if (name_end != filename + strlen(filename) + 1) {
1415 mask = talloc_asprintf(ctx, "%s\\*", filename);
1416 } else {
1417 mask = talloc_strdup(ctx, filename);
1419 } else {
1420 /* filename is a single '\', just append '*' */
1421 mask = talloc_asprintf_append(mask, "%s*", filename);
1424 if (!mask) {
1425 result = EXIT_FAILED;
1426 goto out;
1430 * prepare for automatic propagation of the acl passed on the
1431 * cmdline.
1434 ntstatus = prepare_inheritance_propagation(ctx, filename,
1435 cbstate);
1436 if (!NT_STATUS_IS_OK(ntstatus)) {
1437 d_printf("error: %s processing %s\n",
1438 nt_errstr(ntstatus), filename);
1439 result = EXIT_FAILED;
1440 goto out;
1443 result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1444 cbstate->mode, cbstate->numeric);
1447 * strictly speaking it could be considered an error if a file was
1448 * specificied with '--propagate-inheritance'. However we really want
1449 * to eventually get rid of '--propagate-inheritance' so we will be
1450 * more forgiving here and instead just exit early.
1452 if (!isdirectory || (result != EXIT_OK)) {
1453 goto out;
1456 /* check if there is actually any need to propagate */
1457 if (cbstate->acl_no_propagate) {
1458 goto out;
1460 /* get inheritable attributes this parent container (e.g. filename) */
1461 ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1462 if (NT_STATUS_IS_OK(ntstatus)) {
1463 /* process children */
1464 ntstatus = cli_list(cli, mask, attribute,
1465 cacl_set_cb,
1466 (void *)cbstate);
1469 if (!NT_STATUS_IS_OK(ntstatus)) {
1470 d_printf("error: %s processing %s\n",
1471 nt_errstr(ntstatus), filename);
1472 result = EXIT_FAILED;
1473 goto out;
1476 out:
1477 TALLOC_FREE(ctx);
1478 return result;
1481 /****************************************************************************
1482 main program
1483 ****************************************************************************/
1484 int main(int argc, char *argv[])
1486 const char **argv_const = discard_const_p(const char *, argv);
1487 char *share;
1488 int opt;
1489 enum acl_mode mode = SMB_ACL_SET;
1490 static char *the_acl = NULL;
1491 enum chown_mode change_mode = REQUEST_NONE;
1492 int result;
1493 char *path;
1494 char *filename = NULL;
1495 poptContext pc;
1496 /* numeric is set when the user wants numeric SIDs and ACEs rather
1497 than going via LSA calls to resolve them */
1498 int numeric = 0;
1499 struct cli_state *targetcli = NULL;
1500 struct cli_credentials *creds = NULL;
1501 char *targetfile = NULL;
1502 NTSTATUS status;
1503 bool ok;
1505 struct poptOption long_options[] = {
1506 POPT_AUTOHELP
1508 .longName = "delete",
1509 .shortName = 'D',
1510 .argInfo = POPT_ARG_STRING,
1511 .arg = NULL,
1512 .val = 'D',
1513 .descrip = "Delete an acl",
1514 .argDescrip = "ACL",
1517 .longName = "modify",
1518 .shortName = 'M',
1519 .argInfo = POPT_ARG_STRING,
1520 .arg = NULL,
1521 .val = 'M',
1522 .descrip = "Modify an acl",
1523 .argDescrip = "ACL",
1526 .longName = "add",
1527 .shortName = 'a',
1528 .argInfo = POPT_ARG_STRING,
1529 .arg = NULL,
1530 .val = 'a',
1531 .descrip = "Add an acl",
1532 .argDescrip = "ACL",
1535 .longName = "set",
1536 .shortName = 'S',
1537 .argInfo = POPT_ARG_STRING,
1538 .arg = NULL,
1539 .val = 'S',
1540 .descrip = "Set acls",
1541 .argDescrip = "ACLS",
1544 .longName = "chown",
1545 .shortName = 'C',
1546 .argInfo = POPT_ARG_STRING,
1547 .arg = NULL,
1548 .val = 'C',
1549 .descrip = "Change ownership of a file",
1550 .argDescrip = "USERNAME",
1553 .longName = "chgrp",
1554 .shortName = 'G',
1555 .argInfo = POPT_ARG_STRING,
1556 .arg = NULL,
1557 .val = 'G',
1558 .descrip = "Change group ownership of a file",
1559 .argDescrip = "GROUPNAME",
1562 .longName = "inherit",
1563 .shortName = 'I',
1564 .argInfo = POPT_ARG_STRING,
1565 .arg = NULL,
1566 .val = 'I',
1567 .descrip = "Inherit allow|remove|copy",
1570 .longName = "propagate-inheritance",
1571 .shortName = 0,
1572 .argInfo = POPT_ARG_NONE,
1573 .arg = &inheritance,
1574 .val = 1,
1575 .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
1578 .longName = "numeric",
1579 .shortName = 0,
1580 .argInfo = POPT_ARG_NONE,
1581 .arg = &numeric,
1582 .val = 1,
1583 .descrip = "Don't resolve sids or masks to names",
1586 .longName = "sddl",
1587 .shortName = 0,
1588 .argInfo = POPT_ARG_NONE,
1589 .arg = &sddl,
1590 .val = 1,
1591 .descrip = "Output and input acls in sddl format",
1594 .longName = "query-security-info",
1595 .shortName = 0,
1596 .argInfo = POPT_ARG_INT,
1597 .arg = &query_sec_info,
1598 .val = 1,
1599 .descrip = "The security-info flags for queries"
1602 .longName = "set-security-info",
1603 .shortName = 0,
1604 .argInfo = POPT_ARG_INT,
1605 .arg = &set_sec_info,
1606 .val = 1,
1607 .descrip = "The security-info flags for modifications"
1610 .longName = "test-args",
1611 .shortName = 't',
1612 .argInfo = POPT_ARG_NONE,
1613 .arg = &test_args,
1614 .val = 1,
1615 .descrip = "Test arguments"
1618 .longName = "domain-sid",
1619 .shortName = 0,
1620 .argInfo = POPT_ARG_STRING,
1621 .arg = &domain_sid,
1622 .val = 0,
1623 .descrip = "Domain SID for sddl",
1624 .argDescrip = "SID"},
1626 .longName = "maximum-access",
1627 .shortName = 'x',
1628 .argInfo = POPT_ARG_NONE,
1629 .arg = NULL,
1630 .val = 'x',
1631 .descrip = "Query maximum permissions",
1633 POPT_COMMON_SAMBA
1634 POPT_COMMON_CONNECTION
1635 POPT_COMMON_CREDENTIALS
1636 POPT_LEGACY_S3
1637 POPT_COMMON_VERSION
1638 POPT_TABLEEND
1641 struct cli_state *cli;
1642 TALLOC_CTX *frame = talloc_stackframe();
1643 const char *owner_username = "";
1644 char *server;
1646 smb_init_locale();
1648 ok = samba_cmdline_init(frame,
1649 SAMBA_CMDLINE_CONFIG_CLIENT,
1650 false /* require_smbconf */);
1651 if (!ok) {
1652 DBG_ERR("Failed to init cmdline parser!\n");
1653 TALLOC_FREE(frame);
1654 exit(1);
1656 /* set default debug level to 1 regardless of what smb.conf sets */
1657 lp_set_cmdline("log level", "1");
1659 setlinebuf(stdout);
1661 pc = samba_popt_get_context(getprogname(),
1662 argc,
1663 argv_const,
1664 long_options,
1666 if (pc == NULL) {
1667 DBG_ERR("Failed to setup popt context!\n");
1668 TALLOC_FREE(frame);
1669 exit(1);
1672 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
1673 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
1675 while ((opt = poptGetNextOpt(pc)) != -1) {
1676 switch (opt) {
1677 case 'S':
1678 the_acl = smb_xstrdup(poptGetOptArg(pc));
1679 mode = SMB_ACL_SET;
1680 break;
1682 case 'D':
1683 the_acl = smb_xstrdup(poptGetOptArg(pc));
1684 mode = SMB_ACL_DELETE;
1685 break;
1687 case 'M':
1688 the_acl = smb_xstrdup(poptGetOptArg(pc));
1689 mode = SMB_ACL_MODIFY;
1690 break;
1692 case 'a':
1693 the_acl = smb_xstrdup(poptGetOptArg(pc));
1694 mode = SMB_ACL_ADD;
1695 break;
1697 case 'C':
1698 owner_username = poptGetOptArg(pc);
1699 change_mode = REQUEST_CHOWN;
1700 break;
1702 case 'G':
1703 owner_username = poptGetOptArg(pc);
1704 change_mode = REQUEST_CHGRP;
1705 break;
1707 case 'I':
1708 owner_username = poptGetOptArg(pc);
1709 change_mode = REQUEST_INHERIT;
1710 break;
1711 case 'm':
1712 lp_set_cmdline("client max protocol", poptGetOptArg(pc));
1713 break;
1714 case 'x':
1715 want_mxac = true;
1716 break;
1717 case POPT_ERROR_BADOPT:
1718 fprintf(stderr, "\nInvalid option %s: %s\n\n",
1719 poptBadOption(pc, 0), poptStrerror(opt));
1720 poptPrintUsage(pc, stderr, 0);
1721 exit(1);
1724 if (inheritance && !the_acl) {
1725 poptPrintUsage(pc, stderr, 0);
1726 return -1;
1729 if(!poptPeekArg(pc)) {
1730 poptPrintUsage(pc, stderr, 0);
1731 return -1;
1734 path = talloc_strdup(frame, poptGetArg(pc));
1735 if (!path) {
1736 return -1;
1739 if(!poptPeekArg(pc)) {
1740 poptPrintUsage(pc, stderr, 0);
1741 return -1;
1744 filename = talloc_strdup(frame, poptGetArg(pc));
1745 if (!filename) {
1746 return -1;
1749 poptFreeContext(pc);
1750 samba_cmdline_burn(argc, argv);
1752 string_replace(path,'/','\\');
1754 server = talloc_strdup(frame, path+2);
1755 if (!server) {
1756 return -1;
1758 share = strchr_m(server,'\\');
1759 if (share == NULL) {
1760 printf("Invalid argument\n");
1761 return -1;
1764 *share = 0;
1765 share++;
1767 creds = samba_cmdline_get_creds();
1769 /* Make connection to server */
1770 if (!test_args) {
1771 cli = connect_one(creds, server, share);
1772 if (!cli) {
1773 exit(EXIT_FAILED);
1775 } else {
1776 exit(0);
1779 string_replace(filename, '/', '\\');
1780 if (filename[0] != '\\') {
1781 filename = talloc_asprintf(frame,
1782 "\\%s",
1783 filename);
1784 if (!filename) {
1785 return -1;
1789 status = cli_resolve_path(frame,
1791 creds,
1792 cli,
1793 filename,
1794 &targetcli,
1795 &targetfile);
1796 if (!NT_STATUS_IS_OK(status)) {
1797 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
1798 return -1;
1801 /* Perform requested action */
1803 if (change_mode == REQUEST_INHERIT) {
1804 result = inherit(targetcli, targetfile, owner_username);
1805 } else if (change_mode != REQUEST_NONE) {
1806 result = owner_set(targetcli, change_mode, targetfile, owner_username);
1807 } else if (the_acl) {
1808 if (inheritance) {
1809 struct cacl_callback_state cbstate = {
1810 .creds = creds,
1811 .cli = targetcli,
1812 .mode = mode,
1813 .the_acl = the_acl,
1814 .numeric = numeric,
1816 result = inheritance_cacl_set(targetfile, &cbstate);
1817 } else {
1818 result = cacl_set(targetcli,
1819 targetfile,
1820 the_acl,
1821 mode,
1822 numeric);
1824 } else {
1825 result = cacl_dump(targetcli, targetfile, numeric);
1828 TALLOC_FREE(frame);
1830 return result;