2 Unix SMB/Netbios implementation.
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 static fstring password;
27 static fstring username;
28 static fstring server;
32 /* numeric is set when the user wants numeric SIDs and ACEs rather
33 than going via LSA calls to resolve them */
36 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD};
43 /* These values discovered by inspection */
45 static struct perm_value special_values[] = {
55 static struct perm_value standard_values[] = {
56 { "READ", 0x001200a9 },
57 { "CHANGE", 0x001301bf },
58 { "FULL", 0x001f01ff },
62 struct cli_state lsa_cli;
64 struct ntuser_creds creds;
67 /* Open cli connection and policy handle */
69 static BOOL open_policy_hnd(void)
71 creds.pwd.null_pwd = 1;
73 /* Initialise cli LSA connection */
75 if (!lsa_cli.initialised &&
76 !cli_lsa_initialise(&lsa_cli, server, &creds)) {
80 /* Open policy handle */
82 if (!got_policy_hnd) {
83 if (cli_lsa_open_policy(&lsa_cli, True,
84 SEC_RIGHTS_MAXIMUM_ALLOWED, &pol)
85 != NT_STATUS_NOPROBLEMO) {
89 got_policy_hnd = True;
95 /* convert a SID to a string, either numeric or username/group */
96 static void SidToString(fstring str, DOM_SID *sid)
102 sid_to_string(str, sid);
106 /* Ask LSA to convert the sid to a name */
108 if (open_policy_hnd() &&
109 cli_lsa_lookup_sids(&lsa_cli, &pol, 1, sid, &names, &types,
110 &num_names) == NT_STATUS_NOPROBLEMO) {
114 fstrcpy(str, names[0]);
122 /* convert a string to a SID, either numeric or username/group */
123 static BOOL StringToSid(DOM_SID *sid, char *str)
125 uint32 *types = NULL;
126 DOM_SID *sids = NULL;
132 if (strncmp(str, "S-", 2) == 0) {
133 result = string_to_sid(sid, str);
137 if (open_policy_hnd() &&
138 cli_lsa_lookup_names(&lsa_cli, &pol, 1, &str, &sids, &types,
139 &num_sids) != NT_STATUS_NOPROBLEMO) {
140 result = string_to_sid(sid, str);
144 sid_copy(sid, &sids[0]);
155 /* print an ACE on a FILE, using either numeric or ascii representation */
156 static void print_ace(FILE *f, SEC_ACE *ace)
158 struct perm_value *v;
163 SidToString(sidstr, &ace->sid);
165 fprintf(f, "%s:", sidstr);
168 fprintf(f, "%d/%d/0x%08x\n",
169 ace->type, ace->flags, ace->info.mask);
175 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
176 fprintf(f, "ALLOWED");
177 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
178 fprintf(f, "DENIED");
180 fprintf(f, "%d", ace->type);
183 /* Not sure what flags can be set in a file ACL */
185 fprintf(f, "/%d/", ace->flags);
187 /* Standard permissions */
189 for (v = standard_values; v->perm; v++) {
190 if (ace->info.mask == v->mask) {
191 fprintf(f, "%s\n", v->perm);
196 /* Special permissions. Print out a hex value if we have
197 leftover bits in the mask. */
199 got_mask = ace->info.mask;
202 for (v = special_values; v->perm; v++) {
203 if ((ace->info.mask & v->mask) == v->mask) {
205 fprintf(f, "%s", v->perm);
207 got_mask &= ~v->mask;
213 fprintf(f, "0x%08x", ace->info.mask);
224 /* parse an ACE in the same format as print_ace() */
225 static BOOL parse_ace(SEC_ACE *ace, char *str)
229 unsigned atype, aflags, amask;
232 struct perm_value *v;
236 if (!p) return False;
240 /* Try to parse numeric form */
242 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
243 StringToSid(&sid, str)) {
247 /* Try to parse text form */
249 if (!StringToSid(&sid, str)) {
253 if (!next_token(&p, tok, "/", sizeof(fstring))) {
257 if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
258 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
259 } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
260 atype = SEC_ACE_TYPE_ACCESS_DENIED;
265 /* Only numeric form accepted for flags at present */
267 if (!(next_token(NULL, tok, "/", sizeof(fstring)) &&
268 sscanf(tok, "%i", &aflags))) {
272 if (!next_token(NULL, tok, "/", sizeof(fstring))) {
276 if (strncmp(tok, "0x", 2) == 0) {
277 if (sscanf(tok, "%i", &amask) != 1) {
283 for (v = standard_values; v->perm; v++) {
284 if (strcmp(tok, v->perm) == 0) {
295 for (v = special_values; v->perm; v++) {
296 if (v->perm[0] == *p) {
302 if (!found) return False;
312 init_sec_ace(ace, &sid, atype, mask, aflags);
316 /* add an ACE to a list of ACEs in a SEC_ACL */
317 static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
322 (*the_acl) = make_sec_acl(3, 1, ace);
326 aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
327 memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
328 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
329 new = make_sec_acl((*the_acl)->revision,1+(*the_acl)->num_aces, aces);
330 free_sec_acl(the_acl);
336 /* parse a ascii version of a security descriptor */
337 static SEC_DESC *sec_desc_parse(char *str)
343 DOM_SID *grp_sid=NULL, *owner_sid=NULL;
347 while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
349 if (strncmp(tok,"REVISION:", 9) == 0) {
350 revision = strtol(tok+9, NULL, 16);
354 if (strncmp(tok,"OWNER:", 6) == 0) {
355 owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
357 !StringToSid(owner_sid, tok+6)) {
358 printf("Failed to parse owner sid\n");
364 if (strncmp(tok,"GROUP:", 6) == 0) {
365 grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
367 !StringToSid(grp_sid, tok+6)) {
368 printf("Failed to parse group sid\n");
374 if (strncmp(tok,"ACL:", 4) == 0) {
376 if (!parse_ace(&ace, tok+4)) {
377 printf("Failed to parse ACL %s\n", tok);
380 if(!add_ace(&dacl, &ace)) {
381 printf("Failed to add ACL %s\n", tok);
387 printf("Failed to parse security descriptor\n");
391 ret = make_sec_desc(revision, owner_sid, grp_sid,
392 NULL, dacl, &sd_size);
396 if (grp_sid) free(grp_sid);
397 if (owner_sid) free(owner_sid);
403 /* print a ascii version of a security descriptor on a FILE handle */
404 static void sec_desc_print(FILE *f, SEC_DESC *sd)
409 printf("REVISION:%d\n", sd->revision);
411 /* Print owner and group sid */
414 SidToString(sidstr, sd->owner_sid);
419 printf("OWNER:%s\n", sidstr);
422 SidToString(sidstr, sd->grp_sid);
427 fprintf(f, "GROUP:%s\n", sidstr);
430 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
431 SEC_ACE *ace = &sd->dacl->ace[i];
439 /*****************************************************
440 dump the acls for a file
441 *******************************************************/
442 static void cacl_dump(struct cli_state *cli, char *filename)
447 if (test_args) return;
449 fnum = cli_nt_create(cli, filename, 0x20000);
451 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
455 sd = cli_query_secdesc(cli, fnum);
458 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
462 sec_desc_print(stdout, sd);
466 cli_close(cli, fnum);
469 /*****************************************************
470 set the ACLs on a file given an ascii description
471 *******************************************************/
472 static void cacl_set(struct cli_state *cli, char *filename,
473 char *the_acl, enum acl_mode mode)
480 sd = sec_desc_parse(the_acl);
483 if (test_args) return;
485 /* the desired access below is the only one I could find that works with
486 NT4, W2KP and Samba */
487 fnum = cli_nt_create(cli, filename, MAXIMUM_ALLOWED_ACCESS | 0x60000);
489 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
493 old = cli_query_secdesc(cli, fnum);
495 /* the logic here is rather more complex than I would like */
498 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
501 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
502 if (sec_ace_equal(&sd->dacl->ace[i],
503 &old->dacl->ace[j])) {
504 if (j != old->dacl->num_aces-1) {
505 old->dacl->ace[j] = old->dacl->ace[j+1];
507 old->dacl->num_aces--;
508 if (old->dacl->num_aces == 0) {
509 free(old->dacl->ace);
523 SidToString(str, &sd->dacl->ace[i].sid);
524 printf("ACL for SID %s not found\n", str);
530 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
533 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
534 if (sid_equal(&sd->dacl->ace[i].sid,
535 &old->dacl->ace[j].sid)) {
536 old->dacl->ace[j] = sd->dacl->ace[i];
544 SidToString(str, &sd->dacl->ace[i].sid);
545 printf("ACL for SID %s not found\n", str);
552 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
553 add_ace(&old->dacl, &sd->dacl->ace[i]);
567 sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid,
568 NULL, old->dacl, &sd_size);
570 if (!cli_set_secdesc(cli, fnum, sd)) {
571 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
577 cli_close(cli, fnum);
581 /*****************************************************
582 return a connection to a server
583 *******************************************************/
584 struct cli_state *connect_one(char *share)
587 struct nmb_name called, calling;
590 extern struct in_addr ipzero;
591 extern pstring global_myname;
593 fstrcpy(server,share+2);
594 share = strchr(server,'\\');
595 if (!share) return NULL;
603 make_nmb_name(&calling, global_myname, 0x0);
604 make_nmb_name(&called , server, 0x20);
609 /* have to open a new connection */
610 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
611 !cli_connect(c, server_n, &ip)) {
612 DEBUG(0,("Connection to %s failed\n", server_n));
618 if (!cli_session_request(c, &calling, &called)) {
619 DEBUG(0,("session request to %s failed\n", called.name));
622 if (strcmp(called.name, "*SMBSERVER")) {
623 make_nmb_name(&called , "*SMBSERVER", 0x20);
629 DEBUG(4,(" session request ok\n"));
631 if (!cli_negprot(c)) {
632 DEBUG(0,("protocol negotiation failed\n"));
639 char *pass = getpass("Password: ");
641 pstrcpy(password, pass);
645 if (!cli_session_setup(c, username,
646 password, strlen(password),
647 password, strlen(password),
649 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
655 DEBUG(4,(" session setup ok\n"));
657 if (!cli_send_tconX(c, share, "?????",
658 password, strlen(password)+1)) {
659 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
665 DEBUG(4,(" tconx ok\n"));
671 static void usage(void)
674 "Usage: smbcacls //server1/share1 filename -U username [options]\n\
676 \t-D <acls> delete an acl\n\
677 \t-M <acls> modify an acl\n\
678 \t-A <acls> add an acl\n\
679 \t-S <acls> set acls\n\
680 \t-n don't resolve sids or masks to names\n\
683 The username can be of the form username%%password or\n\
684 workgroup\\username%%password.\n\n\
685 An acl is of the form ACL:<SID>:type/flags/mask\n\
686 You can string acls together with spaces, commas or newlines\n\
690 /****************************************************************************
692 ****************************************************************************/
693 int main(int argc,char *argv[])
703 static pstring servicesf = CONFIGFILE;
704 struct cli_state *cli;
706 char *the_acl = NULL;
712 if (argc < 4 || argv[1][0] == '-') {
717 setup_logging(argv[0],True);
721 all_string_sub(share,"/","\\",0);
727 charset_initialise();
729 lp_load(servicesf,True,False,False);
730 codepage_initialise(lp_client_code_page());
733 if (getenv("USER")) {
734 pstrcpy(username,getenv("USER"));
739 while ((opt = getopt(argc, argv, "U:nhS:D:A:M:t")) != EOF) {
742 pstrcpy(username,optarg);
743 p = strchr(username,'%');
746 pstrcpy(password, p+1);
784 printf("Unknown option %c (%d)\n", (char)opt, opt);
798 cli = connect_one(share);
803 cacl_set(cli, filename, the_acl, mode);
805 cacl_dump(cli, filename);