s4:kdc: Make use of smb_krb5_principal_is_tgs()
[Samba.git] / source3 / utils / smbcacls.c
blob03866d5626742ba4d62bd84705485599fbf96d31
1 /*
2 Unix SMB/CIFS implementation.
3 ACL get/set utility
5 Copyright (C) Andrew Tridgell 2000
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2000
8 Copyright (C) Jelmer Vernooij 2003
9 Copyright (C) Noel Power <noel.power@suse.com> 2013
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "lib/cmdline/cmdline.h"
27 #include "rpc_client/cli_pipe.h"
28 #include "../librpc/gen_ndr/ndr_lsa.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../libcli/security/security.h"
31 #include "libsmb/libsmb.h"
32 #include "libsmb/clirap.h"
33 #include "passdb/machine_sid.h"
34 #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 #include "util_sd.h"
36 #include "lib/param/param.h"
38 static char DIRSEP_CHAR = '\\';
40 static int inheritance = 0;
41 static int test_args;
42 static int sddl;
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;
58 enum acl_mode mode;
59 char *the_acl;
60 bool acl_no_propagate;
61 bool numeric;
64 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
65 struct dom_sid *sid)
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)) {
81 goto done;
84 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
85 if (!NT_STATUS_IS_OK(status)) {
86 goto tdis;
89 status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
90 GENERIC_EXECUTE_ACCESS, &handle);
91 if (!NT_STATUS_IS_OK(status)) {
92 goto tdis;
95 status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
96 frame, &handle,
97 LSA_POLICY_INFO_DOMAIN,
98 &info, &result);
100 if (any_nt_status_not_ok(status, result, &status)) {
101 goto tdis;
104 *sid = *info->domain.sid;
106 tdis:
107 TALLOC_FREE(rpc_pipe);
108 cli_tdis(cli);
109 done:
110 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
111 TALLOC_FREE(frame);
112 return status;
115 static struct dom_sid *get_domain_sid(struct cli_state *cli)
117 NTSTATUS status;
118 struct dom_sid_buf buf;
120 struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
121 if (sid == NULL) {
122 DEBUG(0, ("Out of memory\n"));
123 return NULL;
126 if (domain_sid) {
127 if (!dom_sid_parse(domain_sid, sid)) {
128 DEBUG(0,("failed to parse domain sid\n"));
129 TALLOC_FREE(sid);
131 } else {
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)));
136 TALLOC_FREE(sid);
141 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
142 return sid;
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;
152 if (acl == NULL) {
153 acl = make_sec_acl(ctx, 3, 0, NULL);
154 if (acl == NULL) {
155 return false;
159 if (acl->num_aces == UINT32_MAX) {
160 return false;
162 ADD_TO_ARRAY(
163 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
164 *the_acl = acl;
165 return True;
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)
176 const char *p = str;
177 char *tok;
178 struct security_descriptor *ret = NULL;
179 size_t sd_size;
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;
185 int revision=1;
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);
190 continue;
193 if (strncmp(tok,"OWNER:", 6) == 0) {
194 if (have_owner) {
195 printf("Only specify owner once\n");
196 goto done;
198 if (!StringToSid(cli, &owner_sid, tok+6)) {
199 printf("Failed to parse owner sid\n");
200 goto done;
202 have_owner = true;
203 continue;
206 if (strncmp(tok,"GROUP:", 6) == 0) {
207 if (have_group) {
208 printf("Only specify group once\n");
209 goto done;
211 if (!StringToSid(cli, &group_sid, tok+6)) {
212 printf("Failed to parse group sid\n");
213 goto done;
215 have_group = true;
216 continue;
219 if (strncmp(tok,"ACL:", 4) == 0) {
220 struct security_ace ace;
221 if (!parse_ace(cli, &ace, tok+4)) {
222 goto done;
224 if(!add_ace(&dacl, &ace)) {
225 printf("Failed to add ACL %s\n", tok);
226 goto done;
228 continue;
231 printf("Failed to parse token '%s' in security descriptor,\n", tok);
232 goto done;
235 ret = make_sec_desc(
236 ctx,
237 revision,
238 SEC_DESC_SELF_RELATIVE,
239 have_owner ? &owner_sid : NULL,
240 have_group ? &group_sid : NULL,
241 NULL,
242 dacl,
243 &sd_size);
245 done:
246 return ret;
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;
255 NTSTATUS status;
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(
262 cli, /* cli */
263 filename, /* fname */
264 0, /* CreatFlags */
265 READ_CONTROL_ACCESS, /* CreatFlags */
266 0, /* FileAttributes */
267 FILE_SHARE_READ|
268 FILE_SHARE_WRITE, /* ShareAccess */
269 FILE_OPEN, /* CreateDisposition */
270 0x0, /* CreateOptions */
271 0x0, /* SecurityFlags */
272 &fnum, /* pfid */
273 &cr); /* cr */
274 if (!NT_STATUS_IS_OK(status)) {
275 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
276 return 0;
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;
292 NTSTATUS status;
293 uint32_t sec_info;
294 uint32_t desired_access = 0;
296 if (query_sec_info == -1) {
297 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
298 } else {
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));
318 return NULL;
321 status = cli_query_security_descriptor(cli, fnum, sec_info,
322 ctx, &sd);
324 cli_close(cli, fnum);
326 if (!NT_STATUS_IS_OK(status)) {
327 printf("Failed to get security descriptor: %s\n",
328 nt_errstr(status));
329 return NULL;
331 return sd;
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;
346 bool result=true;
347 NTSTATUS status;
348 uint32_t desired_access = 0;
349 uint32_t sec_info;
351 if (set_sec_info == -1) {
352 sec_info = 0;
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;
360 if (sd->owner_sid) {
361 sec_info |= SECINFO_OWNER;
363 if (sd->group_sid) {
364 sec_info |= SECINFO_GROUP;
366 } else {
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,
382 desired_access,
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));
387 return false;
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",
393 nt_errstr(status));
394 result=false;
397 cli_close(cli, fnum);
398 return result;
401 /*****************************************************
402 get maximum access for a file
403 *******************************************************/
404 static int cacl_mxac(struct cli_state *cli, const char *filename)
406 NTSTATUS status;
407 uint32_t mxac;
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));
412 return EXIT_FAILED;
415 printf("Maximum access: 0x%x\n", mxac);
417 return EXIT_OK;
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;
427 int ret;
429 if (test_args) {
430 return EXIT_OK;
433 sd = get_secdesc(cli, filename);
434 if (sd == NULL) {
435 return EXIT_FAILED;
438 if (sddl) {
439 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
440 if (str == NULL) {
441 return EXIT_FAILED;
443 printf("%s\n", str);
444 TALLOC_FREE(str);
445 } else {
446 sec_desc_print(cli, stdout, sd, numeric);
449 if (want_mxac) {
450 ret = cacl_mxac(cli, filename);
451 if (ret != EXIT_OK) {
452 return ret;
456 return 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)
467 struct dom_sid sid;
468 struct security_descriptor *sd;
469 size_t sd_size;
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)) {
482 return EXIT_FAILED;
485 return EXIT_OK;
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))
500 return 0;
502 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
503 !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
504 return 1;
505 if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
506 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
507 return -1;
508 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
509 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
510 return ace1 - ace2;
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)
532 uint32_t i;
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])) {
540 ARRAY_DEL_ELEMENT(
541 the_acl->aces, i, the_acl->num_aces);
542 the_acl->num_aces--;
543 } else {
544 i++;
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,
555 bool numeric)
557 struct security_descriptor *old = NULL;
558 uint32_t i, j;
559 size_t sd_size;
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);
572 if (!old) {
573 return EXIT_FAILED;
577 /* the logic here is rather more complex than I would like */
578 switch (mode) {
579 case SMB_ACL_DELETE:
580 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
581 bool found = False;
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])) {
586 uint32_t k;
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--;
591 found = True;
592 break;
596 if (!found) {
597 printf("ACL for ACE:");
598 print_ace(cli, stdout, &sd->dacl->aces[i],
599 numeric);
600 printf(" not found\n");
603 break;
605 case SMB_ACL_MODIFY:
606 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
607 bool found = False;
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];
613 found = True;
617 if (!found) {
618 fstring str;
620 SidToString(cli, str,
621 &sd->dacl->aces[i].trustee,
622 numeric);
623 printf("ACL for SID %s not found\n", str);
627 if (sd->owner_sid) {
628 old->owner_sid = sd->owner_sid;
631 if (sd->group_sid) {
632 old->group_sid = sd->group_sid;
635 break;
637 case SMB_ACL_ADD:
638 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
639 add_ace(&old->dacl, &sd->dacl->aces[i]);
641 break;
643 case SMB_ACL_SET:
644 old = sd;
645 break;
648 /* Denied ACE entries must come before allowed ones */
649 sort_acl(old->dacl);
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
657 and W2K. JRA.
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;
668 return result;
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;
680 if (sddl) {
681 sd = sddl_decode(talloc_tos(), the_acl, get_global_sam_sid());
682 } else {
683 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
686 if (sd == NULL) {
687 return EXIT_PARSE_ERROR;
689 if (test_args) {
690 return EXIT_OK;
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,
699 const char *type)
701 struct security_descriptor *old,*sd;
702 uint32_t oldattr;
703 size_t sd_size;
704 int result = EXIT_OK;
706 old = get_secdesc(cli, filename);
708 if (!old) {
709 return EXIT_FAILED;
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) {
717 uint32_t i;
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)) {
727 return EXIT_FAILED;
729 string_replace(parentname, '/', '\\');
730 parent = get_secdesc(cli,parentname);
731 if (parent == NULL) {
732 return EXIT_FAILED;
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);
744 } else {
745 if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
746 SEC_ACE_FLAG_OBJECT_INHERIT) {
747 /* clear flags for files */
748 ace->flags=0;
749 add_ace(&old->dacl, ace);
753 } else {
754 printf("Already set to inheritable permissions.\n");
755 return EXIT_FAILED;
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. */
763 if (old->dacl) {
764 int i;
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);
776 } else {
777 printf("Already set to no inheritable permissions.\n");
778 return EXIT_FAILED;
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
787 * inherited ACL's.
789 if (old->dacl) {
790 uint32_t i;
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);
797 } else {
798 printf("Already set to no inheritable permissions.\n");
799 return EXIT_FAILED;
803 /* Denied ACE entries must come before allowed ones */
804 sort_acl(old->dacl);
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;
814 return result;
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;
824 NTSTATUS nt_status;
825 uint32_t flags = 0;
827 nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
828 NULL, 0,
829 share, "?????",
830 creds,
831 flags);
832 if (!NT_STATUS_IS_OK(nt_status)) {
833 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
834 return NULL;
837 return c;
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)
847 char *mask2 = NULL;
848 char *p = NULL;
850 mask2 = talloc_strdup(ctx, mask);
851 if (!mask2) {
852 return NULL;
854 p = strrchr_m(mask2, DIRSEP_CHAR);
855 if (p) {
856 p[1] = 0;
857 } else {
858 mask2[0] = '\0';
860 mask2 = talloc_asprintf_append(mask2,
861 "%s\\*",
862 fname);
863 return 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
903 if (is_container) {
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
911 * alone is set
913 if ((prohibit_inheritance
914 && acl_objinherit
915 && !acl_cntrinherit) == false) {
916 newflags |= SEC_ACE_FLAG_INHERITED_ACE;
918 } else {
920 * don't apply object/container inheritance flags to
921 * non dirs
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);
942 return newflags;
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;
956 NTSTATUS status;
957 int result;
958 int fileattr;
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;
963 uint32_t i, j;
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);
971 if (!old) {
972 status = NT_STATUS_UNSUCCESSFUL;
973 goto out;
976 /* inhibit propagation? */
977 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
978 SEC_DESC_DACL_PROTECTED){
979 status = NT_STATUS_OK;
980 goto out;
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;
993 goto out;
998 /* remove any acl(s) that are inherited */
999 if (acl_to_remove) {
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])) {
1006 uint32_t k;
1007 for (k = j; k < old->dacl->num_aces-1;
1008 k++) {
1009 old->dacl->aces[k] =
1010 old->dacl->aces[k+1];
1012 old->dacl->num_aces--;
1013 break;
1018 /* propagate any inheritable ace to be added */
1019 if (acl_to_add) {
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;
1025 bool is_inherited;
1026 /* don't propagate flags to a file unless OI */
1027 if (!is_objectinherit && !is_container) {
1028 continue;
1031 * adjust flags according to inheritance
1032 * rules
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) {
1040 continue;
1042 if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1043 status = NT_STATUS_NO_MEMORY;
1044 goto out;
1049 result = cacl_set_from_sd(cbstate->cli, caclfile,
1050 old,
1051 SMB_ACL_SET, cbstate->numeric);
1052 if (result != EXIT_OK) {
1053 status = NT_STATUS_UNSUCCESSFUL;
1054 goto out;
1057 status = NT_STATUS_OK;
1058 out:
1059 TALLOC_FREE(aclctx);
1060 return status;
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)) {
1072 return true;
1074 return false;
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
1081 * command line.
1083 static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1084 struct cacl_callback_state *cbstate)
1086 NTSTATUS result;
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;
1092 uint32_t j;
1093 bool propagate = false;
1095 old = get_secdesc_with_ctx(ctx, cli, filename);
1096 if (old == NULL) {
1097 return NT_STATUS_NO_MEMORY;
1100 /* parse acl passed on the command line */
1101 if (sddl) {
1102 cbstate->aclsd = sddl_decode(ctx, the_acl,
1103 get_global_sam_sid());
1104 } else {
1105 cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1108 if (!cbstate->aclsd) {
1109 result = NT_STATUS_UNSUCCESSFUL;
1110 goto out;
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;
1120 goto out;
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;
1133 goto out;
1135 if (!propagate) {
1136 if (is_inheritable_ace(ace)) {
1137 propagate = true;
1142 result = NT_STATUS_OK;
1143 out:
1144 cbstate->acl_no_propagate = !propagate;
1145 return result;
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)
1158 NTSTATUS result;
1159 struct cli_state *cli = NULL;
1160 struct security_descriptor *sd = NULL;
1161 struct security_acl *acl_to_add = NULL;
1162 uint32_t j;
1164 cli = cbstate->cli;
1165 sd = get_secdesc_with_ctx(ctx, cli, filename);
1167 if (sd == NULL) {
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);
1182 if (!added) {
1183 result = NT_STATUS_NO_MEMORY;
1184 goto out;
1188 cbstate->acl_to_add = acl_to_add;
1189 result = NT_STATUS_OK;
1190 out:
1191 return result;
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;
1209 NTSTATUS status;
1210 struct cli_state *targetcli = NULL;
1212 char *dir = NULL;
1213 char *dir_end = NULL;
1214 char *mask2 = NULL;
1215 char *targetpath = NULL;
1216 char *caclfile = NULL;
1218 dirctx = talloc_new(NULL);
1219 if (!dirctx) {
1220 status = NT_STATUS_NO_MEMORY;
1221 goto out;
1224 cli = cbstate->cli;
1225 creds = cbstate->creds;
1227 /* Work out the directory. */
1228 dir = talloc_strdup(dirctx, mask);
1229 if (!dir) {
1230 status = NT_STATUS_NO_MEMORY;
1231 goto out;
1234 dir_end = strrchr(dir, DIRSEP_CHAR);
1235 if (dir_end != NULL) {
1236 *dir_end = '\0';
1239 if (!f->name || !f->name[0]) {
1240 d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1241 status = NT_STATUS_UNSUCCESSFUL;
1242 goto out;
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;
1250 dir_end = NULL;
1252 /* ignore special '.' & '..' */
1253 if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1254 status = NT_STATUS_OK;
1255 goto out;
1258 mask2 = build_dirname(dirctx, mask, dir, f->name);
1259 if (mask2 == NULL) {
1260 status = NT_STATUS_NO_MEMORY;
1261 goto out;
1264 /* check for dfs */
1265 status = cli_resolve_path(dirctx, "", creds, cli,
1266 mask2, &targetcli, &targetpath);
1267 if (!NT_STATUS_IS_OK(status)) {
1268 goto out;
1272 * prepare path to caclfile, remove any existing wildcard
1273 * chars and convert path separators.
1276 caclfile = talloc_strdup(dirctx, targetpath);
1277 if (!caclfile) {
1278 status = NT_STATUS_NO_MEMORY;
1279 goto out;
1281 dir_end = strrchr(caclfile, '*');
1282 if (dir_end != NULL) {
1283 *dir_end = '\0';
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
1292 * processed.
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)) {
1302 goto out;
1306 * get inheritable ace(s) for this dir/container
1307 * that will be propagated to its children
1309 status = get_inheritable_aces(dirctx, caclfile,
1310 &dir_cbstate);
1311 if (!NT_STATUS_IS_OK(status)) {
1312 goto out;
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)) {
1324 goto out;
1327 } else {
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);
1334 if (!targetpath) {
1335 status = NT_STATUS_NO_MEMORY;
1336 goto out;
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)) {
1344 goto out;
1347 status = NT_STATUS_OK;
1348 out:
1349 if (!NT_STATUS_IS_OK(status)) {
1350 d_printf("error %s: processing %s\n",
1351 nt_errstr(status),
1352 targetpath);
1354 TALLOC_FREE(dirctx);
1355 return status;
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
1362 * by cli_list.
1364 static int inheritance_cacl_set(char *filename,
1365 struct cacl_callback_state *cbstate)
1367 int result;
1368 NTSTATUS ntstatus;
1369 int fileattr;
1370 char *mask = NULL;
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");
1377 if (ctx == NULL) {
1378 d_printf("out of memory\n");
1379 result = EXIT_FAILED;
1380 goto out;
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);
1388 goto out;
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
1398 * the args.
1400 if (test_args) {
1401 result = EXIT_OK;
1402 goto out;
1405 mask = NULL;
1406 /* make sure we have a trailing '\*' for directory */
1407 if (!isdirectory) {
1408 mask = talloc_strdup(ctx, filename);
1409 } else if (strlen(filename) > 1) {
1411 * if the passed file name doesn't have a trailing '\'
1412 * append it.
1414 char *name_end = strrchr(filename, DIRSEP_CHAR);
1415 if (name_end != filename + strlen(filename) + 1) {
1416 mask = talloc_asprintf(ctx, "%s\\*", filename);
1417 } else {
1418 mask = talloc_strdup(ctx, filename);
1420 } else {
1421 /* filename is a single '\', just append '*' */
1422 mask = talloc_asprintf_append(mask, "%s*", filename);
1425 if (!mask) {
1426 result = EXIT_FAILED;
1427 goto out;
1431 * prepare for automatic propagation of the acl passed on the
1432 * cmdline.
1435 ntstatus = prepare_inheritance_propagation(ctx, filename,
1436 cbstate);
1437 if (!NT_STATUS_IS_OK(ntstatus)) {
1438 d_printf("error: %s processing %s\n",
1439 nt_errstr(ntstatus), filename);
1440 result = EXIT_FAILED;
1441 goto out;
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)) {
1454 goto out;
1457 /* check if there is actually any need to propagate */
1458 if (cbstate->acl_no_propagate) {
1459 goto out;
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,
1466 cacl_set_cb,
1467 (void *)cbstate);
1470 if (!NT_STATUS_IS_OK(ntstatus)) {
1471 d_printf("error: %s processing %s\n",
1472 nt_errstr(ntstatus), filename);
1473 result = EXIT_FAILED;
1474 goto out;
1477 out:
1478 TALLOC_FREE(ctx);
1479 return result;
1482 /****************************************************************************
1483 main program
1484 ****************************************************************************/
1485 int main(int argc, char *argv[])
1487 const char **argv_const = discard_const_p(const char *, argv);
1488 char *share;
1489 int opt;
1490 enum acl_mode mode = SMB_ACL_SET;
1491 static char *the_acl = NULL;
1492 enum chown_mode change_mode = REQUEST_NONE;
1493 int result;
1494 char *path;
1495 char *filename = NULL;
1496 poptContext pc;
1497 /* numeric is set when the user wants numeric SIDs and ACEs rather
1498 than going via LSA calls to resolve them */
1499 int numeric = 0;
1500 struct cli_state *targetcli = NULL;
1501 struct cli_credentials *creds = NULL;
1502 char *targetfile = NULL;
1503 NTSTATUS status;
1504 bool ok;
1505 struct loadparm_context *lp_ctx = NULL;
1507 struct poptOption long_options[] = {
1508 POPT_AUTOHELP
1510 .longName = "delete",
1511 .shortName = 'D',
1512 .argInfo = POPT_ARG_STRING,
1513 .arg = NULL,
1514 .val = 'D',
1515 .descrip = "Delete an acl",
1516 .argDescrip = "ACL",
1519 .longName = "modify",
1520 .shortName = 'M',
1521 .argInfo = POPT_ARG_STRING,
1522 .arg = NULL,
1523 .val = 'M',
1524 .descrip = "Modify an acl",
1525 .argDescrip = "ACL",
1528 .longName = "add",
1529 .shortName = 'a',
1530 .argInfo = POPT_ARG_STRING,
1531 .arg = NULL,
1532 .val = 'a',
1533 .descrip = "Add an acl",
1534 .argDescrip = "ACL",
1537 .longName = "set",
1538 .shortName = 'S',
1539 .argInfo = POPT_ARG_STRING,
1540 .arg = NULL,
1541 .val = 'S',
1542 .descrip = "Set acls",
1543 .argDescrip = "ACLS",
1546 .longName = "chown",
1547 .shortName = 'C',
1548 .argInfo = POPT_ARG_STRING,
1549 .arg = NULL,
1550 .val = 'C',
1551 .descrip = "Change ownership of a file",
1552 .argDescrip = "USERNAME",
1555 .longName = "chgrp",
1556 .shortName = 'G',
1557 .argInfo = POPT_ARG_STRING,
1558 .arg = NULL,
1559 .val = 'G',
1560 .descrip = "Change group ownership of a file",
1561 .argDescrip = "GROUPNAME",
1564 .longName = "inherit",
1565 .shortName = 'I',
1566 .argInfo = POPT_ARG_STRING,
1567 .arg = NULL,
1568 .val = 'I',
1569 .descrip = "Inherit allow|remove|copy",
1572 .longName = "propagate-inheritance",
1573 .shortName = 0,
1574 .argInfo = POPT_ARG_NONE,
1575 .arg = &inheritance,
1576 .val = 1,
1577 .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
1580 .longName = "numeric",
1581 .shortName = 0,
1582 .argInfo = POPT_ARG_NONE,
1583 .arg = &numeric,
1584 .val = 1,
1585 .descrip = "Don't resolve sids or masks to names",
1588 .longName = "sddl",
1589 .shortName = 0,
1590 .argInfo = POPT_ARG_NONE,
1591 .arg = &sddl,
1592 .val = 1,
1593 .descrip = "Output and input acls in sddl format",
1596 .longName = "query-security-info",
1597 .shortName = 0,
1598 .argInfo = POPT_ARG_INT,
1599 .arg = &query_sec_info,
1600 .val = 1,
1601 .descrip = "The security-info flags for queries"
1604 .longName = "set-security-info",
1605 .shortName = 0,
1606 .argInfo = POPT_ARG_INT,
1607 .arg = &set_sec_info,
1608 .val = 1,
1609 .descrip = "The security-info flags for modifications"
1612 .longName = "test-args",
1613 .shortName = 't',
1614 .argInfo = POPT_ARG_NONE,
1615 .arg = &test_args,
1616 .val = 1,
1617 .descrip = "Test arguments"
1620 .longName = "domain-sid",
1621 .shortName = 0,
1622 .argInfo = POPT_ARG_STRING,
1623 .arg = &domain_sid,
1624 .val = 0,
1625 .descrip = "Domain SID for sddl",
1626 .argDescrip = "SID"},
1628 .longName = "maximum-access",
1629 .shortName = 'x',
1630 .argInfo = POPT_ARG_NONE,
1631 .arg = NULL,
1632 .val = 'x',
1633 .descrip = "Query maximum permissions",
1635 POPT_COMMON_SAMBA
1636 POPT_COMMON_CONNECTION
1637 POPT_COMMON_CREDENTIALS
1638 POPT_LEGACY_S3
1639 POPT_COMMON_VERSION
1640 POPT_TABLEEND
1643 struct cli_state *cli;
1644 TALLOC_CTX *frame = talloc_stackframe();
1645 const char *owner_username = "";
1646 char *server;
1648 smb_init_locale();
1650 ok = samba_cmdline_init(frame,
1651 SAMBA_CMDLINE_CONFIG_CLIENT,
1652 false /* require_smbconf */);
1653 if (!ok) {
1654 DBG_ERR("Failed to init cmdline parser!\n");
1655 TALLOC_FREE(frame);
1656 exit(1);
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");
1662 setlinebuf(stdout);
1664 pc = samba_popt_get_context(getprogname(),
1665 argc,
1666 argv_const,
1667 long_options,
1669 if (pc == NULL) {
1670 DBG_ERR("Failed to setup popt context!\n");
1671 TALLOC_FREE(frame);
1672 exit(1);
1675 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
1676 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
1678 while ((opt = poptGetNextOpt(pc)) != -1) {
1679 switch (opt) {
1680 case 'S':
1681 the_acl = smb_xstrdup(poptGetOptArg(pc));
1682 mode = SMB_ACL_SET;
1683 break;
1685 case 'D':
1686 the_acl = smb_xstrdup(poptGetOptArg(pc));
1687 mode = SMB_ACL_DELETE;
1688 break;
1690 case 'M':
1691 the_acl = smb_xstrdup(poptGetOptArg(pc));
1692 mode = SMB_ACL_MODIFY;
1693 break;
1695 case 'a':
1696 the_acl = smb_xstrdup(poptGetOptArg(pc));
1697 mode = SMB_ACL_ADD;
1698 break;
1700 case 'C':
1701 owner_username = poptGetOptArg(pc);
1702 change_mode = REQUEST_CHOWN;
1703 break;
1705 case 'G':
1706 owner_username = poptGetOptArg(pc);
1707 change_mode = REQUEST_CHGRP;
1708 break;
1710 case 'I':
1711 owner_username = poptGetOptArg(pc);
1712 change_mode = REQUEST_INHERIT;
1713 break;
1714 case 'm':
1715 lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
1716 break;
1717 case 'x':
1718 want_mxac = true;
1719 break;
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);
1724 exit(1);
1727 if (inheritance && !the_acl) {
1728 poptPrintUsage(pc, stderr, 0);
1729 return -1;
1732 if(!poptPeekArg(pc)) {
1733 poptPrintUsage(pc, stderr, 0);
1734 return -1;
1737 path = talloc_strdup(frame, poptGetArg(pc));
1738 if (!path) {
1739 return -1;
1742 if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
1743 printf("Invalid argument: %s\n", path);
1744 return -1;
1747 if(!poptPeekArg(pc)) {
1748 poptPrintUsage(pc, stderr, 0);
1749 return -1;
1752 filename = talloc_strdup(frame, poptGetArg(pc));
1753 if (!filename) {
1754 return -1;
1757 poptFreeContext(pc);
1758 samba_cmdline_burn(argc, argv);
1760 string_replace(path,'/','\\');
1762 server = talloc_strdup(frame, path+2);
1763 if (!server) {
1764 return -1;
1766 share = strchr_m(server,'\\');
1767 if (share == NULL) {
1768 printf("Invalid argument\n");
1769 return -1;
1772 *share = 0;
1773 share++;
1775 creds = samba_cmdline_get_creds();
1777 /* Make connection to server */
1778 if (!test_args) {
1779 cli = connect_one(creds, server, share);
1780 if (!cli) {
1781 exit(EXIT_FAILED);
1783 } else {
1784 exit(0);
1787 string_replace(filename, '/', '\\');
1788 if (filename[0] != '\\') {
1789 filename = talloc_asprintf(frame,
1790 "\\%s",
1791 filename);
1792 if (!filename) {
1793 return -1;
1797 status = cli_resolve_path(frame,
1799 creds,
1800 cli,
1801 filename,
1802 &targetcli,
1803 &targetfile);
1804 if (!NT_STATUS_IS_OK(status)) {
1805 DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
1806 return -1;
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) {
1816 if (inheritance) {
1817 struct cacl_callback_state cbstate = {
1818 .creds = creds,
1819 .cli = targetcli,
1820 .mode = mode,
1821 .the_acl = the_acl,
1822 .numeric = numeric,
1824 result = inheritance_cacl_set(targetfile, &cbstate);
1825 } else {
1826 result = cacl_set(targetcli,
1827 targetfile,
1828 the_acl,
1829 mode,
1830 numeric);
1832 } else {
1833 result = cacl_dump(targetcli, targetfile, numeric);
1836 gfree_all();
1837 TALLOC_FREE(frame);
1839 return result;