ldb: Release ldb 1.6.3
[Samba.git] / source3 / utils / smbcacls.c
blobb61d11df860f783a58125cc3196b3c1a55f2f413
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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "popt_common_cmdline.h"
26 #include "rpc_client/cli_pipe.h"
27 #include "../librpc/gen_ndr/ndr_lsa.h"
28 #include "rpc_client/cli_lsarpc.h"
29 #include "../libcli/security/security.h"
30 #include "libsmb/libsmb.h"
31 #include "libsmb/clirap.h"
32 #include "passdb/machine_sid.h"
33 #include "../librpc/gen_ndr/ndr_lsa_c.h"
34 #include "util_sd.h"
36 static int test_args;
38 #define CREATE_ACCESS_READ READ_CONTROL_ACCESS
40 static int sddl;
41 static int query_sec_info = -1;
42 static int set_sec_info = -1;
43 static bool want_mxac;
45 static const char *domain_sid = NULL;
47 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
48 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
49 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
51 static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
52 struct dom_sid *sid)
54 union lsa_PolicyInformation *info = NULL;
55 struct smbXcli_tcon *orig_tcon = NULL;
56 struct rpc_pipe_client *rpc_pipe = NULL;
57 struct policy_handle handle;
58 NTSTATUS status, result;
59 TALLOC_CTX *frame = talloc_stackframe();
61 if (cli_state_has_tcon(cli)) {
62 orig_tcon = cli_state_save_tcon(cli);
63 if (orig_tcon == NULL) {
64 status = NT_STATUS_NO_MEMORY;
65 goto done;
69 status = cli_tree_connect(cli, "IPC$", "?????", NULL);
70 if (!NT_STATUS_IS_OK(status)) {
71 goto done;
74 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
75 if (!NT_STATUS_IS_OK(status)) {
76 goto tdis;
79 status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
80 GENERIC_EXECUTE_ACCESS, &handle);
81 if (!NT_STATUS_IS_OK(status)) {
82 goto tdis;
85 status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
86 frame, &handle,
87 LSA_POLICY_INFO_DOMAIN,
88 &info, &result);
90 if (any_nt_status_not_ok(status, result, &status)) {
91 goto tdis;
94 *sid = *info->domain.sid;
96 tdis:
97 TALLOC_FREE(rpc_pipe);
98 cli_tdis(cli);
99 done:
100 cli_state_restore_tcon(cli, orig_tcon);
101 TALLOC_FREE(frame);
102 return status;
105 static struct dom_sid *get_domain_sid(struct cli_state *cli)
107 NTSTATUS status;
108 struct dom_sid_buf buf;
110 struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
111 if (sid == NULL) {
112 DEBUG(0, ("Out of memory\n"));
113 return NULL;
116 if (domain_sid) {
117 if (!dom_sid_parse(domain_sid, sid)) {
118 DEBUG(0,("failed to parse domain sid\n"));
119 TALLOC_FREE(sid);
121 } else {
122 status = cli_lsa_lookup_domain_sid(cli, sid);
124 if (!NT_STATUS_IS_OK(status)) {
125 DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
126 TALLOC_FREE(sid);
131 DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
132 return sid;
135 /* add an ACE to a list of ACEs in a struct security_acl */
136 static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
138 struct security_acl *new_ace;
139 struct security_ace *aces;
140 if (! *the_acl) {
141 return (((*the_acl) = make_sec_acl(talloc_tos(), 3, 1, ace))
142 != NULL);
145 if (!(aces = SMB_CALLOC_ARRAY(struct security_ace, 1+(*the_acl)->num_aces))) {
146 return False;
148 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(struct
149 security_ace));
150 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(struct security_ace));
151 new_ace = make_sec_acl(talloc_tos(),(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
152 SAFE_FREE(aces);
153 (*the_acl) = new_ace;
154 return True;
157 /* parse a ascii version of a security descriptor */
158 static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
160 const char *p = str;
161 char *tok;
162 struct security_descriptor *ret = NULL;
163 size_t sd_size;
164 struct dom_sid *grp_sid=NULL, *owner_sid=NULL;
165 struct security_acl *dacl=NULL;
166 int revision=1;
168 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
169 if (strncmp(tok,"REVISION:", 9) == 0) {
170 revision = strtol(tok+9, NULL, 16);
171 continue;
174 if (strncmp(tok,"OWNER:", 6) == 0) {
175 if (owner_sid) {
176 printf("Only specify owner once\n");
177 goto done;
179 owner_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
180 if (!owner_sid ||
181 !StringToSid(cli, owner_sid, tok+6)) {
182 printf("Failed to parse owner sid\n");
183 goto done;
185 continue;
188 if (strncmp(tok,"GROUP:", 6) == 0) {
189 if (grp_sid) {
190 printf("Only specify group once\n");
191 goto done;
193 grp_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
194 if (!grp_sid ||
195 !StringToSid(cli, grp_sid, tok+6)) {
196 printf("Failed to parse group sid\n");
197 goto done;
199 continue;
202 if (strncmp(tok,"ACL:", 4) == 0) {
203 struct security_ace ace;
204 if (!parse_ace(cli, &ace, tok+4)) {
205 goto done;
207 if(!add_ace(&dacl, &ace)) {
208 printf("Failed to add ACL %s\n", tok);
209 goto done;
211 continue;
214 printf("Failed to parse token '%s' in security descriptor,\n", tok);
215 goto done;
218 ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid,
219 NULL, dacl, &sd_size);
221 done:
222 SAFE_FREE(grp_sid);
223 SAFE_FREE(owner_sid);
225 return ret;
228 /*****************************************************
229 get fileinfo for filename
230 *******************************************************/
231 static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
233 uint16_t fnum = (uint16_t)-1;
234 NTSTATUS status;
235 struct smb_create_returns cr = {0};
237 /* The desired access below is the only one I could find that works
238 with NT4, W2KP and Samba */
240 status = cli_ntcreate(cli, filename, 0, CREATE_ACCESS_READ,
241 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
242 FILE_OPEN, 0x0, 0x0, &fnum, &cr);
243 if (!NT_STATUS_IS_OK(status)) {
244 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
245 return 0;
248 cli_close(cli, fnum);
249 return cr.file_attributes;
252 /*****************************************************
253 get sec desc for filename
254 *******************************************************/
255 static struct security_descriptor *get_secdesc(struct cli_state *cli, const char *filename)
257 uint16_t fnum = (uint16_t)-1;
258 struct security_descriptor *sd;
259 NTSTATUS status;
260 uint32_t sec_info;
261 uint32_t desired_access = 0;
263 if (query_sec_info == -1) {
264 sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
265 } else {
266 sec_info = query_sec_info;
269 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
270 desired_access |= SEC_STD_READ_CONTROL;
272 if (sec_info & SECINFO_SACL) {
273 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
276 if (desired_access == 0) {
277 desired_access |= SEC_STD_READ_CONTROL;
280 status = cli_ntcreate(cli, filename, 0, desired_access,
281 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
282 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
283 if (!NT_STATUS_IS_OK(status)) {
284 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
285 return NULL;
288 status = cli_query_security_descriptor(cli, fnum, sec_info,
289 talloc_tos(), &sd);
291 cli_close(cli, fnum);
293 if (!NT_STATUS_IS_OK(status)) {
294 printf("Failed to get security descriptor: %s\n",
295 nt_errstr(status));
296 return NULL;
298 return sd;
301 /*****************************************************
302 set sec desc for filename
303 *******************************************************/
304 static bool set_secdesc(struct cli_state *cli, const char *filename,
305 struct security_descriptor *sd)
307 uint16_t fnum = (uint16_t)-1;
308 bool result=true;
309 NTSTATUS status;
310 uint32_t desired_access = 0;
311 uint32_t sec_info;
313 if (set_sec_info == -1) {
314 sec_info = 0;
316 if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
317 sec_info |= SECINFO_DACL;
319 if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
320 sec_info |= SECINFO_SACL;
322 if (sd->owner_sid) {
323 sec_info |= SECINFO_OWNER;
325 if (sd->group_sid) {
326 sec_info |= SECINFO_GROUP;
328 } else {
329 sec_info = set_sec_info;
332 /* Make the desired_access more specific. */
333 if (sec_info & SECINFO_DACL) {
334 desired_access |= SEC_STD_WRITE_DAC;
336 if (sec_info & SECINFO_SACL) {
337 desired_access |= SEC_FLAG_SYSTEM_SECURITY;
339 if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
340 desired_access |= SEC_STD_WRITE_OWNER;
343 status = cli_ntcreate(cli, filename, 0,
344 desired_access,
345 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
346 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
347 if (!NT_STATUS_IS_OK(status)) {
348 printf("Failed to open %s: %s\n", filename, nt_errstr(status));
349 return false;
352 status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
353 if (!NT_STATUS_IS_OK(status)) {
354 printf("ERROR: security descriptor set failed: %s\n",
355 nt_errstr(status));
356 result=false;
359 cli_close(cli, fnum);
360 return result;
363 /*****************************************************
364 get maximum access for a file
365 *******************************************************/
366 static int cacl_mxac(struct cli_state *cli, const char *filename)
368 NTSTATUS status;
369 uint32_t mxac;
371 status = cli_query_mxac(cli, filename, &mxac);
372 if (!NT_STATUS_IS_OK(status)) {
373 printf("Failed to get mxac: %s\n", nt_errstr(status));
374 return EXIT_FAILED;
377 printf("Maximum access: 0x%x\n", mxac);
379 return EXIT_OK;
383 /*****************************************************
384 dump the acls for a file
385 *******************************************************/
386 static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
388 struct security_descriptor *sd;
389 int ret;
391 if (test_args) {
392 return EXIT_OK;
395 sd = get_secdesc(cli, filename);
396 if (sd == NULL) {
397 return EXIT_FAILED;
400 if (sddl) {
401 char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
402 if (str == NULL) {
403 return EXIT_FAILED;
405 printf("%s\n", str);
406 TALLOC_FREE(str);
407 } else {
408 sec_desc_print(cli, stdout, sd, numeric);
411 if (want_mxac) {
412 ret = cacl_mxac(cli, filename);
413 if (ret != EXIT_OK) {
414 return ret;
418 return EXIT_OK;
421 /*****************************************************
422 Change the ownership or group ownership of a file. Just
423 because the NT docs say this can't be done :-). JRA.
424 *******************************************************/
426 static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
427 const char *filename, const char *new_username)
429 struct dom_sid sid;
430 struct security_descriptor *sd;
431 size_t sd_size;
433 if (!StringToSid(cli, &sid, new_username))
434 return EXIT_PARSE_ERROR;
436 sd = make_sec_desc(talloc_tos(),
437 SECURITY_DESCRIPTOR_REVISION_1,
438 SEC_DESC_SELF_RELATIVE,
439 (change_mode == REQUEST_CHOWN) ? &sid : NULL,
440 (change_mode == REQUEST_CHGRP) ? &sid : NULL,
441 NULL, NULL, &sd_size);
443 if (!set_secdesc(cli, filename, sd)) {
444 return EXIT_FAILED;
447 return EXIT_OK;
451 /* The MSDN is contradictory over the ordering of ACE entries in an
452 ACL. However NT4 gives a "The information may have been modified
453 by a computer running Windows NT 5.0" if denied ACEs do not appear
454 before allowed ACEs. At
455 http://technet.microsoft.com/en-us/library/cc781716.aspx the
456 canonical order is specified as "Explicit Deny, Explicit Allow,
457 Inherited ACEs unchanged" */
459 static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
461 if (security_ace_equal(ace1, ace2))
462 return 0;
464 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
465 !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
466 return 1;
467 if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
468 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
469 return -1;
470 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
471 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
472 return ace1 - ace2;
474 if (ace1->type != ace2->type)
475 return ace2->type - ace1->type;
477 if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
478 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
480 if (ace1->flags != ace2->flags)
481 return ace1->flags - ace2->flags;
483 if (ace1->access_mask != ace2->access_mask)
484 return ace1->access_mask - ace2->access_mask;
486 if (ace1->size != ace2->size)
487 return ace1->size - ace2->size;
489 return memcmp(ace1, ace2, sizeof(struct security_ace));
492 static void sort_acl(struct security_acl *the_acl)
494 uint32_t i;
495 if (!the_acl) return;
497 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
499 for (i=1;i<the_acl->num_aces;) {
500 if (security_ace_equal(&the_acl->aces[i-1],
501 &the_acl->aces[i])) {
502 int j;
503 for (j=i; j<the_acl->num_aces-1; j++) {
504 the_acl->aces[j] = the_acl->aces[j+1];
506 the_acl->num_aces--;
507 } else {
508 i++;
513 /*****************************************************
514 set the ACLs on a file given an ascii description
515 *******************************************************/
517 static int cacl_set(struct cli_state *cli, const char *filename,
518 char *the_acl, enum acl_mode mode, bool numeric)
520 struct security_descriptor *sd, *old;
521 uint32_t i, j;
522 size_t sd_size;
523 int result = EXIT_OK;
525 if (sddl) {
526 sd = sddl_decode(talloc_tos(), the_acl, get_domain_sid(cli));
527 } else {
528 sd = sec_desc_parse(talloc_tos(), cli, the_acl);
531 if (!sd) return EXIT_PARSE_ERROR;
532 if (test_args) return EXIT_OK;
534 if (mode != SMB_ACL_SET) {
536 * Do not fetch old ACL when it will be overwritten
537 * completely with a new one.
539 old = get_secdesc(cli, filename);
541 if (!old) {
542 return EXIT_FAILED;
546 /* the logic here is rather more complex than I would like */
547 switch (mode) {
548 case SMB_ACL_DELETE:
549 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
550 bool found = False;
552 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
553 if (security_ace_equal(&sd->dacl->aces[i],
554 &old->dacl->aces[j])) {
555 uint32_t k;
556 for (k=j; k<old->dacl->num_aces-1;k++) {
557 old->dacl->aces[k] = old->dacl->aces[k+1];
559 old->dacl->num_aces--;
560 found = True;
561 break;
565 if (!found) {
566 printf("ACL for ACE:");
567 print_ace(cli, stdout, &sd->dacl->aces[i],
568 numeric);
569 printf(" not found\n");
572 break;
574 case SMB_ACL_MODIFY:
575 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
576 bool found = False;
578 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
579 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
580 &old->dacl->aces[j].trustee)) {
581 old->dacl->aces[j] = sd->dacl->aces[i];
582 found = True;
586 if (!found) {
587 fstring str;
589 SidToString(cli, str,
590 &sd->dacl->aces[i].trustee,
591 numeric);
592 printf("ACL for SID %s not found\n", str);
596 if (sd->owner_sid) {
597 old->owner_sid = sd->owner_sid;
600 if (sd->group_sid) {
601 old->group_sid = sd->group_sid;
604 break;
606 case SMB_ACL_ADD:
607 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
608 add_ace(&old->dacl, &sd->dacl->aces[i]);
610 break;
612 case SMB_ACL_SET:
613 old = sd;
614 break;
617 /* Denied ACE entries must come before allowed ones */
618 sort_acl(old->dacl);
620 /* Create new security descriptor and set it */
622 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
623 But if we're sending an owner, even if it's the same as the one
624 that already exists then W2K3 insists we open with WRITE_OWNER access.
625 I need to check that setting a SD with no owner set works against WNT
626 and W2K. JRA.
629 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
630 old->owner_sid, old->group_sid,
631 NULL, old->dacl, &sd_size);
633 if (!set_secdesc(cli, filename, sd)) {
634 result = EXIT_FAILED;
637 return result;
640 /*****************************************************
641 set the inherit on a file
642 *******************************************************/
643 static int inherit(struct cli_state *cli, const char *filename,
644 const char *type)
646 struct security_descriptor *old,*sd;
647 uint32_t oldattr;
648 size_t sd_size;
649 int result = EXIT_OK;
651 old = get_secdesc(cli, filename);
653 if (!old) {
654 return EXIT_FAILED;
657 oldattr = get_fileinfo(cli,filename);
659 if (strcmp(type,"allow")==0) {
660 if ((old->type & SEC_DESC_DACL_PROTECTED) ==
661 SEC_DESC_DACL_PROTECTED) {
662 int i;
663 char *parentname,*temp;
664 struct security_descriptor *parent;
665 temp = talloc_strdup(talloc_tos(), filename);
667 old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
669 /* look at parent and copy in all its inheritable ACL's. */
670 string_replace(temp, '\\', '/');
671 if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
672 return EXIT_FAILED;
674 string_replace(parentname, '/', '\\');
675 parent = get_secdesc(cli,parentname);
676 if (parent == NULL) {
677 return EXIT_FAILED;
679 for (i=0;i<parent->dacl->num_aces;i++) {
680 struct security_ace *ace=&parent->dacl->aces[i];
681 /* Add inherited flag to all aces */
682 ace->flags=ace->flags|
683 SEC_ACE_FLAG_INHERITED_ACE;
684 if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
685 if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
686 SEC_ACE_FLAG_CONTAINER_INHERIT) {
687 add_ace(&old->dacl, ace);
689 } else {
690 if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
691 SEC_ACE_FLAG_OBJECT_INHERIT) {
692 /* clear flags for files */
693 ace->flags=0;
694 add_ace(&old->dacl, ace);
698 } else {
699 printf("Already set to inheritable permissions.\n");
700 return EXIT_FAILED;
702 } else if (strcmp(type,"remove")==0) {
703 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
704 SEC_DESC_DACL_PROTECTED) {
705 old->type=old->type | SEC_DESC_DACL_PROTECTED;
707 /* remove all inherited ACL's. */
708 if (old->dacl) {
709 int i;
710 struct security_acl *temp=old->dacl;
711 old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
712 for (i=temp->num_aces-1;i>=0;i--) {
713 struct security_ace *ace=&temp->aces[i];
714 /* Remove all ace with INHERITED flag set */
715 if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
716 SEC_ACE_FLAG_INHERITED_ACE) {
717 add_ace(&old->dacl,ace);
721 } else {
722 printf("Already set to no inheritable permissions.\n");
723 return EXIT_FAILED;
725 } else if (strcmp(type,"copy")==0) {
726 if ((old->type & SEC_DESC_DACL_PROTECTED) !=
727 SEC_DESC_DACL_PROTECTED) {
728 old->type=old->type | SEC_DESC_DACL_PROTECTED;
730 /* convert all inherited ACL's to non inherated ACL's. */
731 if (old->dacl) {
732 int i;
733 for (i=0;i<old->dacl->num_aces;i++) {
734 struct security_ace *ace=&old->dacl->aces[i];
735 /* Remove INHERITED FLAG from all aces */
736 ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
739 } else {
740 printf("Already set to no inheritable permissions.\n");
741 return EXIT_FAILED;
745 /* Denied ACE entries must come before allowed ones */
746 sort_acl(old->dacl);
748 sd = make_sec_desc(talloc_tos(),old->revision, old->type,
749 old->owner_sid, old->group_sid,
750 NULL, old->dacl, &sd_size);
752 if (!set_secdesc(cli, filename, sd)) {
753 result = EXIT_FAILED;
756 return result;
759 /*****************************************************
760 Return a connection to a server.
761 *******************************************************/
762 static struct cli_state *connect_one(const struct user_auth_info *auth_info,
763 const char *server, const char *share)
765 struct cli_state *c = NULL;
766 NTSTATUS nt_status;
767 uint32_t flags = 0;
769 if (get_cmdline_auth_info_use_kerberos(auth_info)) {
770 flags |= CLI_FULL_CONNECTION_USE_KERBEROS |
771 CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
774 nt_status = cli_full_connection(&c, lp_netbios_name(), server,
775 NULL, 0,
776 share, "?????",
777 get_cmdline_auth_info_username(auth_info),
778 lp_workgroup(),
779 get_cmdline_auth_info_password(auth_info),
780 flags,
781 get_cmdline_auth_info_signing_state(auth_info));
782 if (!NT_STATUS_IS_OK(nt_status)) {
783 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
784 return NULL;
787 if (get_cmdline_auth_info_smb_encrypt(auth_info)) {
788 nt_status = cli_cm_force_encryption(c,
789 get_cmdline_auth_info_username(auth_info),
790 get_cmdline_auth_info_password(auth_info),
791 lp_workgroup(),
792 share);
793 if (!NT_STATUS_IS_OK(nt_status)) {
794 cli_shutdown(c);
795 c = NULL;
799 return c;
802 /****************************************************************************
803 main program
804 ****************************************************************************/
805 int main(int argc, char *argv[])
807 const char **argv_const = discard_const_p(const char *, argv);
808 char *share;
809 int opt;
810 enum acl_mode mode = SMB_ACL_SET;
811 static char *the_acl = NULL;
812 enum chown_mode change_mode = REQUEST_NONE;
813 int result;
814 char *path;
815 char *filename = NULL;
816 poptContext pc;
817 /* numeric is set when the user wants numeric SIDs and ACEs rather
818 than going via LSA calls to resolve them */
819 int numeric = 0;
821 struct poptOption long_options[] = {
822 POPT_AUTOHELP
824 .longName = "delete",
825 .shortName = 'D',
826 .argInfo = POPT_ARG_STRING,
827 .arg = NULL,
828 .val = 'D',
829 .descrip = "Delete an acl",
830 .argDescrip = "ACL",
833 .longName = "modify",
834 .shortName = 'M',
835 .argInfo = POPT_ARG_STRING,
836 .arg = NULL,
837 .val = 'M',
838 .descrip = "Modify an acl",
839 .argDescrip = "ACL",
842 .longName = "add",
843 .shortName = 'a',
844 .argInfo = POPT_ARG_STRING,
845 .arg = NULL,
846 .val = 'a',
847 .descrip = "Add an acl",
848 .argDescrip = "ACL",
851 .longName = "set",
852 .shortName = 'S',
853 .argInfo = POPT_ARG_STRING,
854 .arg = NULL,
855 .val = 'S',
856 .descrip = "Set acls",
857 .argDescrip = "ACLS",
860 .longName = "chown",
861 .shortName = 'C',
862 .argInfo = POPT_ARG_STRING,
863 .arg = NULL,
864 .val = 'C',
865 .descrip = "Change ownership of a file",
866 .argDescrip = "USERNAME",
869 .longName = "chgrp",
870 .shortName = 'G',
871 .argInfo = POPT_ARG_STRING,
872 .arg = NULL,
873 .val = 'G',
874 .descrip = "Change group ownership of a file",
875 .argDescrip = "GROUPNAME",
878 .longName = "inherit",
879 .shortName = 'I',
880 .argInfo = POPT_ARG_STRING,
881 .arg = NULL,
882 .val = 'I',
883 .descrip = "Inherit allow|remove|copy",
886 .longName = "numeric",
887 .shortName = 0,
888 .argInfo = POPT_ARG_NONE,
889 .arg = &numeric,
890 .val = 1,
891 .descrip = "Don't resolve sids or masks to names",
894 .longName = "sddl",
895 .shortName = 0,
896 .argInfo = POPT_ARG_NONE,
897 .arg = &sddl,
898 .val = 1,
899 .descrip = "Output and input acls in sddl format",
902 .longName = "query-security-info",
903 .shortName = 0,
904 .argInfo = POPT_ARG_INT,
905 .arg = &query_sec_info,
906 .val = 1,
907 .descrip = "The security-info flags for queries"
910 .longName = "set-security-info",
911 .shortName = 0,
912 .argInfo = POPT_ARG_INT,
913 .arg = &set_sec_info,
914 .val = 1,
915 .descrip = "The security-info flags for modifications"
918 .longName = "test-args",
919 .shortName = 't',
920 .argInfo = POPT_ARG_NONE,
921 .arg = &test_args,
922 .val = 1,
923 .descrip = "Test arguments"
926 .longName = "domain-sid",
927 .shortName = 0,
928 .argInfo = POPT_ARG_STRING,
929 .arg = &domain_sid,
930 .val = 0,
931 .descrip = "Domain SID for sddl",
932 .argDescrip = "SID"},
934 .longName = "max-protocol",
935 .shortName = 'm',
936 .argInfo = POPT_ARG_STRING,
937 .arg = NULL,
938 .val = 'm',
939 .descrip = "Set the max protocol level",
940 .argDescrip = "LEVEL",
943 .longName = "maximum-access",
944 .shortName = 'x',
945 .argInfo = POPT_ARG_NONE,
946 .arg = NULL,
947 .val = 'x',
948 .descrip = "Query maximum persmissions",
950 POPT_COMMON_SAMBA
951 POPT_COMMON_CONNECTION
952 POPT_COMMON_CREDENTIALS
953 POPT_TABLEEND
956 struct cli_state *cli;
957 TALLOC_CTX *frame = talloc_stackframe();
958 const char *owner_username = "";
959 char *server;
961 smb_init_locale();
963 /* set default debug level to 1 regardless of what smb.conf sets */
964 setup_logging( "smbcacls", DEBUG_STDERR);
965 lp_set_cmdline("log level", "1");
967 setlinebuf(stdout);
969 popt_common_credentials_set_ignore_missing_conf();
971 pc = poptGetContext("smbcacls", argc, argv_const, long_options, 0);
973 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
974 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
976 while ((opt = poptGetNextOpt(pc)) != -1) {
977 switch (opt) {
978 case 'S':
979 the_acl = smb_xstrdup(poptGetOptArg(pc));
980 mode = SMB_ACL_SET;
981 break;
983 case 'D':
984 the_acl = smb_xstrdup(poptGetOptArg(pc));
985 mode = SMB_ACL_DELETE;
986 break;
988 case 'M':
989 the_acl = smb_xstrdup(poptGetOptArg(pc));
990 mode = SMB_ACL_MODIFY;
991 break;
993 case 'a':
994 the_acl = smb_xstrdup(poptGetOptArg(pc));
995 mode = SMB_ACL_ADD;
996 break;
998 case 'C':
999 owner_username = poptGetOptArg(pc);
1000 change_mode = REQUEST_CHOWN;
1001 break;
1003 case 'G':
1004 owner_username = poptGetOptArg(pc);
1005 change_mode = REQUEST_CHGRP;
1006 break;
1008 case 'I':
1009 owner_username = poptGetOptArg(pc);
1010 change_mode = REQUEST_INHERIT;
1011 break;
1012 case 'm':
1013 lp_set_cmdline("client max protocol", poptGetOptArg(pc));
1014 break;
1015 case 'x':
1016 want_mxac = true;
1017 break;
1021 /* Make connection to server */
1022 if(!poptPeekArg(pc)) {
1023 poptPrintUsage(pc, stderr, 0);
1024 return -1;
1027 path = talloc_strdup(frame, poptGetArg(pc));
1028 if (!path) {
1029 return -1;
1032 if(!poptPeekArg(pc)) {
1033 poptPrintUsage(pc, stderr, 0);
1034 return -1;
1037 filename = talloc_strdup(frame, poptGetArg(pc));
1038 if (!filename) {
1039 return -1;
1042 poptFreeContext(pc);
1043 popt_burn_cmdline_password(argc, argv);
1044 popt_common_credentials_post();
1046 string_replace(path,'/','\\');
1048 server = talloc_strdup(frame, path+2);
1049 if (!server) {
1050 return -1;
1052 share = strchr_m(server,'\\');
1053 if (!share) {
1054 printf("Invalid argument: %s\n", share);
1055 return -1;
1058 *share = 0;
1059 share++;
1061 if (!test_args) {
1062 cli = connect_one(popt_get_cmdline_auth_info(), server, share);
1063 if (!cli) {
1064 exit(EXIT_FAILED);
1066 } else {
1067 popt_free_cmdline_auth_info();
1068 exit(0);
1071 string_replace(filename, '/', '\\');
1072 if (filename[0] != '\\') {
1073 filename = talloc_asprintf(frame,
1074 "\\%s",
1075 filename);
1076 if (!filename) {
1077 return -1;
1081 /* Perform requested action */
1083 if (change_mode == REQUEST_INHERIT) {
1084 result = inherit(cli, filename, owner_username);
1085 } else if (change_mode != REQUEST_NONE) {
1086 result = owner_set(cli, change_mode, filename, owner_username);
1087 } else if (the_acl) {
1088 result = cacl_set(cli, filename, the_acl, mode, numeric);
1089 } else {
1090 result = cacl_dump(cli, filename, numeric);
1093 popt_free_cmdline_auth_info();
1094 TALLOC_FREE(frame);
1096 return result;