2 Unix SMB/Netbios implementation.
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2000
8 Copyright (C) Jeremy Allison 2000
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 static fstring password;
28 static pstring username;
29 static pstring owner_username;
30 static fstring server;
34 /* numeric is set when the user wants numeric SIDs and ACEs rather
35 than going via LSA calls to resolve them */
38 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD };
39 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
46 /* These values discovered by inspection */
48 static struct perm_value special_values[] = {
58 static struct perm_value standard_values[] = {
59 { "READ", 0x001200a9 },
60 { "CHANGE", 0x001301bf },
61 { "FULL", 0x001f01ff },
65 struct cli_state lsa_cli;
67 struct ntuser_creds creds;
70 /* Open cli connection and policy handle */
72 static BOOL open_policy_hnd(void)
74 creds.pwd.null_pwd = 1;
76 /* Initialise cli LSA connection */
78 if (!lsa_cli.initialised &&
79 !cli_lsa_initialise(&lsa_cli, server, &creds)) {
83 /* Open policy handle */
85 if (!got_policy_hnd) {
86 if (cli_lsa_open_policy(&lsa_cli, True,
87 SEC_RIGHTS_MAXIMUM_ALLOWED, &pol)
88 != NT_STATUS_NOPROBLEMO) {
92 got_policy_hnd = True;
98 /* convert a SID to a string, either numeric or username/group */
99 static void SidToString(fstring str, DOM_SID *sid)
102 uint32 *types = NULL;
105 sid_to_string(str, sid);
109 /* Ask LSA to convert the sid to a name */
111 if (open_policy_hnd() &&
112 cli_lsa_lookup_sids(&lsa_cli, &pol, 1, sid, &names, &types,
113 &num_names) == NT_STATUS_NOPROBLEMO) {
117 fstrcpy(str, names[0]);
125 /* convert a string to a SID, either numeric or username/group */
126 static BOOL StringToSid(DOM_SID *sid, char *str)
128 uint32 *types = NULL;
129 DOM_SID *sids = NULL;
135 if (strncmp(str, "S-", 2) == 0) {
136 result = string_to_sid(sid, str);
140 if (open_policy_hnd() &&
141 cli_lsa_lookup_names(&lsa_cli, &pol, 1, &str, &sids, &types,
142 &num_sids) != NT_STATUS_NOPROBLEMO) {
143 result = string_to_sid(sid, str);
147 sid_copy(sid, &sids[0]);
158 /* print an ACE on a FILE, using either numeric or ascii representation */
159 static void print_ace(FILE *f, SEC_ACE *ace)
161 struct perm_value *v;
166 SidToString(sidstr, &ace->sid);
168 fprintf(f, "%s:", sidstr);
171 fprintf(f, "%d/%d/0x%08x\n",
172 ace->type, ace->flags, ace->info.mask);
178 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
179 fprintf(f, "ALLOWED");
180 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
181 fprintf(f, "DENIED");
183 fprintf(f, "%d", ace->type);
186 /* Not sure what flags can be set in a file ACL */
188 fprintf(f, "/%d/", ace->flags);
190 /* Standard permissions */
192 for (v = standard_values; v->perm; v++) {
193 if (ace->info.mask == v->mask) {
194 fprintf(f, "%s\n", v->perm);
199 /* Special permissions. Print out a hex value if we have
200 leftover bits in the mask. */
202 got_mask = ace->info.mask;
205 for (v = special_values; v->perm; v++) {
206 if ((ace->info.mask & v->mask) == v->mask) {
208 fprintf(f, "%s", v->perm);
210 got_mask &= ~v->mask;
216 fprintf(f, "0x%08x", ace->info.mask);
227 /* parse an ACE in the same format as print_ace() */
228 static BOOL parse_ace(SEC_ACE *ace, char *str)
232 unsigned atype, aflags, amask;
235 struct perm_value *v;
239 if (!p) return False;
243 /* Try to parse numeric form */
245 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
246 StringToSid(&sid, str)) {
250 /* Try to parse text form */
252 if (!StringToSid(&sid, str)) {
256 if (!next_token(&p, tok, "/", sizeof(fstring))) {
260 if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
261 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
262 } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
263 atype = SEC_ACE_TYPE_ACCESS_DENIED;
268 /* Only numeric form accepted for flags at present */
270 if (!(next_token(NULL, tok, "/", sizeof(fstring)) &&
271 sscanf(tok, "%i", &aflags))) {
275 if (!next_token(NULL, tok, "/", sizeof(fstring))) {
279 if (strncmp(tok, "0x", 2) == 0) {
280 if (sscanf(tok, "%i", &amask) != 1) {
286 for (v = standard_values; v->perm; v++) {
287 if (strcmp(tok, v->perm) == 0) {
298 for (v = special_values; v->perm; v++) {
299 if (v->perm[0] == *p) {
305 if (!found) return False;
315 init_sec_ace(ace, &sid, atype, mask, aflags);
319 /* add an ACE to a list of ACEs in a SEC_ACL */
320 static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
325 (*the_acl) = make_sec_acl(3, 1, ace);
329 aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
330 memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
331 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
332 new = make_sec_acl((*the_acl)->revision,1+(*the_acl)->num_aces, aces);
333 free_sec_acl(the_acl);
339 /* parse a ascii version of a security descriptor */
340 static SEC_DESC *sec_desc_parse(char *str)
346 DOM_SID *grp_sid=NULL, *owner_sid=NULL;
350 while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
352 if (strncmp(tok,"REVISION:", 9) == 0) {
353 revision = strtol(tok+9, NULL, 16);
357 if (strncmp(tok,"OWNER:", 6) == 0) {
358 owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
360 !StringToSid(owner_sid, tok+6)) {
361 printf("Failed to parse owner sid\n");
367 if (strncmp(tok,"GROUP:", 6) == 0) {
368 grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
370 !StringToSid(grp_sid, tok+6)) {
371 printf("Failed to parse group sid\n");
377 if (strncmp(tok,"ACL:", 4) == 0) {
379 if (!parse_ace(&ace, tok+4)) {
380 printf("Failed to parse ACL %s\n", tok);
383 if(!add_ace(&dacl, &ace)) {
384 printf("Failed to add ACL %s\n", tok);
390 printf("Failed to parse security descriptor\n");
394 ret = make_sec_desc(revision, owner_sid, grp_sid,
395 NULL, dacl, &sd_size);
399 if (grp_sid) free(grp_sid);
400 if (owner_sid) free(owner_sid);
406 /* print a ascii version of a security descriptor on a FILE handle */
407 static void sec_desc_print(FILE *f, SEC_DESC *sd)
412 printf("REVISION:%d\n", sd->revision);
414 /* Print owner and group sid */
417 SidToString(sidstr, sd->owner_sid);
422 printf("OWNER:%s\n", sidstr);
425 SidToString(sidstr, sd->grp_sid);
430 fprintf(f, "GROUP:%s\n", sidstr);
433 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
434 SEC_ACE *ace = &sd->dacl->ace[i];
442 /*****************************************************
443 dump the acls for a file
444 *******************************************************/
445 static void cacl_dump(struct cli_state *cli, char *filename)
450 if (test_args) return;
452 fnum = cli_nt_create(cli, filename, 0x20000);
454 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
458 sd = cli_query_secdesc(cli, fnum);
461 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
465 sec_desc_print(stdout, sd);
469 cli_close(cli, fnum);
472 /*****************************************************
473 Change the ownership or group ownership of a file. Just
474 because the NT docs say this can't be done :-). JRA.
475 *******************************************************/
477 static void owner_set(struct cli_state *cli, enum chown_mode change_mode, char *filename, char *new_username)
484 fnum = cli_nt_create(cli, filename, READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS);
486 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
490 if (!StringToSid(&sid, new_username))
493 old = cli_query_secdesc(cli, fnum);
495 sd = make_sec_desc(old->revision,
496 (change_mode == REQUEST_CHOWN) ? &sid : old->owner_sid,
497 (change_mode == REQUEST_CHGRP) ? &sid : old->grp_sid,
498 NULL, old->dacl, &sd_size);
500 if (!cli_set_secdesc(cli, fnum, sd)) {
501 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
507 cli_close(cli, fnum);
510 /*****************************************************
511 set the ACLs on a file given an ascii description
512 *******************************************************/
513 static void cacl_set(struct cli_state *cli, char *filename,
514 char *the_acl, enum acl_mode mode)
521 sd = sec_desc_parse(the_acl);
524 if (test_args) return;
526 /* the desired access below is the only one I could find that works with
527 NT4, W2KP and Samba */
528 fnum = cli_nt_create(cli, filename, MAXIMUM_ALLOWED_ACCESS | 0x60000);
530 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
534 old = cli_query_secdesc(cli, fnum);
536 /* the logic here is rather more complex than I would like */
539 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
542 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
543 if (sec_ace_equal(&sd->dacl->ace[i],
544 &old->dacl->ace[j])) {
545 if (j != old->dacl->num_aces-1) {
546 old->dacl->ace[j] = old->dacl->ace[j+1];
548 old->dacl->num_aces--;
549 if (old->dacl->num_aces == 0) {
550 free(old->dacl->ace);
564 SidToString(str, &sd->dacl->ace[i].sid);
565 printf("ACL for SID %s not found\n", str);
571 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
574 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
575 if (sid_equal(&sd->dacl->ace[i].sid,
576 &old->dacl->ace[j].sid)) {
577 old->dacl->ace[j] = sd->dacl->ace[i];
585 SidToString(str, &sd->dacl->ace[i].sid);
586 printf("ACL for SID %s not found\n", str);
593 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
594 add_ace(&old->dacl, &sd->dacl->ace[i]);
608 sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid,
609 NULL, old->dacl, &sd_size);
611 if (!cli_set_secdesc(cli, fnum, sd)) {
612 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
618 cli_close(cli, fnum);
622 /*****************************************************
623 return a connection to a server
624 *******************************************************/
625 struct cli_state *connect_one(char *share)
628 struct nmb_name called, calling;
631 extern struct in_addr ipzero;
632 extern pstring global_myname;
634 fstrcpy(server,share+2);
635 share = strchr(server,'\\');
636 if (!share) return NULL;
644 make_nmb_name(&calling, global_myname, 0x0);
645 make_nmb_name(&called , server, 0x20);
650 /* have to open a new connection */
651 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
652 !cli_connect(c, server_n, &ip)) {
653 DEBUG(0,("Connection to %s failed\n", server_n));
659 if (!cli_session_request(c, &calling, &called)) {
660 DEBUG(0,("session request to %s failed\n", called.name));
663 if (strcmp(called.name, "*SMBSERVER")) {
664 make_nmb_name(&called , "*SMBSERVER", 0x20);
670 DEBUG(4,(" session request ok\n"));
672 if (!cli_negprot(c)) {
673 DEBUG(0,("protocol negotiation failed\n"));
680 char *pass = getpass("Password: ");
682 pstrcpy(password, pass);
686 if (!cli_session_setup(c, username,
687 password, strlen(password),
688 password, strlen(password),
690 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
696 DEBUG(4,(" session setup ok\n"));
698 if (!cli_send_tconX(c, share, "?????",
699 password, strlen(password)+1)) {
700 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
706 DEBUG(4,(" tconx ok\n"));
712 static void usage(void)
715 "Usage: smbcacls //server1/share1 filename -U username [options]\n\
717 \t-D <acls> delete an acl\n\
718 \t-M <acls> modify an acl\n\
719 \t-A <acls> add an acl\n\
720 \t-S <acls> set acls\n\
721 \t-C username change ownership of a file\n\
722 \t-G username change group ownership of a file\n\
723 \t-n don't resolve sids or masks to names\n\
726 The username can be of the form username%%password or\n\
727 workgroup\\username%%password.\n\n\
728 An acl is of the form ACL:<SID>:type/flags/mask\n\
729 You can string acls together with spaces, commas or newlines\n\
733 /****************************************************************************
735 ****************************************************************************/
736 int main(int argc,char *argv[])
746 static pstring servicesf = CONFIGFILE;
747 struct cli_state *cli;
749 char *the_acl = NULL;
750 enum chown_mode change_mode = REQUEST_NONE;
756 if (argc < 4 || argv[1][0] == '-') {
761 setup_logging(argv[0],True);
765 all_string_sub(share,"/","\\",0);
771 charset_initialise();
773 lp_load(servicesf,True,False,False);
774 codepage_initialise(lp_client_code_page());
777 if (getenv("USER")) {
778 pstrcpy(username,getenv("USER"));
783 while ((opt = getopt(argc, argv, "U:nhS:D:A:M:C:G:t")) != EOF) {
786 pstrcpy(username,optarg);
787 p = strchr(username,'%');
790 pstrcpy(password, p+1);
816 pstrcpy(owner_username,optarg);
817 change_mode = REQUEST_CHOWN;
821 pstrcpy(owner_username,optarg);
822 change_mode = REQUEST_CHGRP;
838 printf("Unknown option %c (%d)\n", (char)opt, opt);
852 cli = connect_one(share);
856 if (change_mode != REQUEST_NONE) {
857 owner_set(cli, change_mode, filename, owner_username);
858 } else if (the_acl) {
859 cacl_set(cli, filename, the_acl, mode);
861 cacl_dump(cli, filename);