2 Unix SMB/Netbios implementation.
6 Copyright (C) Andrew Tridgell 2000
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 static fstring password;
26 static fstring username;
27 static fstring server;
31 /* numeric is set when the user wants numeric SIDs and ACEs rather
32 than going via LSA calls to resolve them */
35 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD};
42 /* These values discovered by inspection */
44 static struct perm_value special_values[] = {
54 static struct perm_value standard_values[] = {
55 { "READ", 0x001200a9 },
56 { "CHANGE", 0x001301bf },
57 { "FULL", 0x001f01ff },
61 /* convert a SID to a string, either numeric or username/group */
62 static void SidToString(fstring str, DOM_SID *sid)
66 struct ntuser_creds creds;
75 creds.pwd.null_pwd = 1;
77 if (numeric || !cli_lsa_initialise(&cli, server, &creds) ||
78 cli_lsa_open_policy(&cli, True, SEC_RIGHTS_MAXIMUM_ALLOWED,
79 &pol) != NT_STATUS_NOPROBLEMO ||
80 cli_lsa_lookup_sids(&cli, &pol, 1, sid, &names, &types,
81 &num_names) != NT_STATUS_NOPROBLEMO) {
82 sid_to_string(str, sid);
86 fstrcpy(str, names[0]);
93 if (cli.initialised) {
94 cli_lsa_close(&cli, &pol);
95 cli_lsa_shutdown(&cli);
99 /* convert a string to a SID, either numeric or username/group */
100 static BOOL StringToSid(DOM_SID *sid, fstring str)
103 struct cli_state cli;
104 struct ntuser_creds creds;
112 if (strncmp(str, "S-", 2) == 0) {
113 return string_to_sid(sid, str);
120 creds.pwd.null_pwd = 1;
122 if (!cli_lsa_initialise(&cli, server, &creds) ||
123 cli_lsa_open_policy(&cli, True, SEC_RIGHTS_MAXIMUM_ALLOWED,
124 &pol) != NT_STATUS_NOPROBLEMO ||
125 cli_lsa_lookup_names(&cli, &pol, 1, &str, &sids, &types,
126 &num_sids) != NT_STATUS_NOPROBLEMO) {
127 result = string_to_sid(sid, str);
131 sid_copy(sid, &sids[0]);
137 if (cli.initialised) {
138 cli_lsa_close(&cli, &pol);
139 cli_lsa_shutdown(&cli);
146 /* print an ACE on a FILE, using either numeric or ascii representation */
147 static void print_ace(FILE *f, SEC_ACE *ace)
149 struct perm_value *v;
154 SidToString(sidstr, &ace->sid);
156 fprintf(f, "%s:", sidstr);
159 fprintf(f, "%d/%d/0x%08x\n",
160 ace->type, ace->flags, ace->info.mask);
166 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
167 fprintf(f, "ALLOWED");
168 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
169 fprintf(f, "DENIED");
171 fprintf(f, "%d", ace->type);
174 /* Not sure what flags can be set in a file ACL */
176 fprintf(f, "/%d/", ace->flags);
178 /* Standard permissions */
180 for (v = standard_values; v->perm; v++) {
181 if (ace->info.mask == v->mask) {
182 fprintf(f, "%s\n", v->perm);
187 /* Special permissions. Print out a hex value if we have
188 leftover bits in the mask. */
190 got_mask = ace->info.mask;
193 for (v = special_values; v->perm; v++) {
194 if ((ace->info.mask & v->mask) == v->mask) {
196 fprintf(f, "%s", v->perm);
198 got_mask &= ~v->mask;
204 fprintf(f, "0x%08x", ace->info.mask);
215 /* parse an ACE in the same format as print_ace() */
216 static BOOL parse_ace(SEC_ACE *ace, char *str)
220 unsigned atype, aflags, amask;
223 struct perm_value *v;
227 if (!p) return False;
231 /* Try to parse numeric form */
233 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
234 StringToSid(&sid, str)) {
238 /* Try to parse text form */
240 if (!StringToSid(&sid, str)) {
244 if (!next_token(&p, tok, "/", sizeof(fstring))) {
248 if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
249 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
250 } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
251 atype = SEC_ACE_TYPE_ACCESS_DENIED;
256 /* Only numeric form accepted for flags at present */
258 if (!(next_token(NULL, tok, "/", sizeof(fstring)) &&
259 sscanf(tok, "%i", &aflags))) {
263 if (!next_token(NULL, tok, "/", sizeof(fstring))) {
267 if (strncmp(tok, "0x", 2) == 0) {
268 if (sscanf(tok, "%i", &amask) != 1) {
274 for (v = standard_values; v->perm; v++) {
275 if (strcmp(tok, v->perm) == 0) {
286 for (v = special_values; v->perm; v++) {
287 if (v->perm[0] == *p) {
293 if (!found) return False;
303 init_sec_ace(ace, &sid, atype, mask, aflags);
307 /* add an ACE to a list of ACEs in a SEC_ACL */
308 static BOOL add_ace(SEC_ACL **acl, SEC_ACE *ace)
313 (*acl) = make_sec_acl(3, 1, ace);
317 aces = calloc(1+(*acl)->num_aces,sizeof(SEC_ACE));
318 memcpy(aces, (*acl)->ace, (*acl)->num_aces * sizeof(SEC_ACE));
319 memcpy(aces+(*acl)->num_aces, ace, sizeof(SEC_ACE));
320 new = make_sec_acl((*acl)->revision,1+(*acl)->num_aces, aces);
327 /* parse a ascii version of a security descriptor */
328 static SEC_DESC *sec_desc_parse(char *str)
334 DOM_SID *grp_sid=NULL, *owner_sid=NULL;
338 while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
340 if (strncmp(tok,"REVISION:", 9) == 0) {
341 revision = strtol(tok+9, NULL, 16);
345 if (strncmp(tok,"OWNER:", 6) == 0) {
346 owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
348 !StringToSid(owner_sid, tok+6)) {
349 printf("Failed to parse owner sid\n");
355 if (strncmp(tok,"GROUP:", 6) == 0) {
356 grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
358 !StringToSid(grp_sid, tok+6)) {
359 printf("Failed to parse group sid\n");
365 if (strncmp(tok,"ACL:", 4) == 0) {
367 if (!parse_ace(&ace, tok+4) ||
368 !add_ace(&dacl, &ace)) {
369 printf("Failed to parse ACL\n");
375 printf("Failed to parse security descriptor\n");
379 ret = make_sec_desc(revision, owner_sid, grp_sid,
380 NULL, dacl, &sd_size);
384 if (grp_sid) free(grp_sid);
385 if (owner_sid) free(owner_sid);
391 /* print a ascii version of a security descriptor on a FILE handle */
392 static void sec_desc_print(FILE *f, SEC_DESC *sd)
397 printf("REVISION:%d\n", sd->revision);
399 /* Print owner and group sid */
402 SidToString(sidstr, sd->owner_sid);
407 printf("OWNER:%s\n", sidstr);
410 SidToString(sidstr, sd->grp_sid);
415 fprintf(f, "GROUP:%s\n", sidstr);
418 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
419 SEC_ACE *ace = &sd->dacl->ace[i];
427 /*****************************************************
428 dump the acls for a file
429 *******************************************************/
430 static void cacl_dump(struct cli_state *cli, char *filename)
435 if (test_args) return;
437 fnum = cli_nt_create(cli, filename, 0x20000);
439 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
443 sd = cli_query_secdesc(cli, fnum);
446 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
450 sec_desc_print(stdout, sd);
454 cli_close(cli, fnum);
457 /*****************************************************
458 set the ACLs on a file given an ascii description
459 *******************************************************/
460 static void cacl_set(struct cli_state *cli, char *filename,
461 char *acl, enum acl_mode mode)
468 sd = sec_desc_parse(acl);
471 if (test_args) return;
473 /* the desired access below is the only one I could find that works with
474 NT4, W2KP and Samba */
475 fnum = cli_nt_create(cli, filename, MAXIMUM_ALLOWED_ACCESS | 0x60000);
477 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
481 old = cli_query_secdesc(cli, fnum);
483 /* the logic here is rather more complex than I would like */
486 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
489 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
490 if (sec_ace_equal(&sd->dacl->ace[i],
491 &old->dacl->ace[j])) {
492 if (j != old->dacl->num_aces-1) {
493 old->dacl->ace[j] = old->dacl->ace[j+1];
495 old->dacl->num_aces--;
496 if (old->dacl->num_aces == 0) {
497 free(old->dacl->ace);
511 SidToString(str, &sd->dacl->ace[i].sid);
512 printf("ACL for SID %s not found\n", str);
518 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
521 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
522 if (sid_equal(&sd->dacl->ace[i].sid,
523 &old->dacl->ace[j].sid)) {
524 old->dacl->ace[j] = sd->dacl->ace[i];
532 SidToString(str, &sd->dacl->ace[i].sid);
533 printf("ACL for SID %s not found\n", str);
540 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
541 add_ace(&old->dacl, &sd->dacl->ace[i]);
555 sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid,
556 NULL, old->dacl, &sd_size);
558 if (!cli_set_secdesc(cli, fnum, sd)) {
559 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
566 cli_close(cli, fnum);
570 /*****************************************************
571 return a connection to a server
572 *******************************************************/
573 struct cli_state *connect_one(char *share)
576 struct nmb_name called, calling;
579 extern struct in_addr ipzero;
580 extern pstring global_myname;
582 fstrcpy(server,share+2);
583 share = strchr(server,'\\');
584 if (!share) return NULL;
592 make_nmb_name(&calling, global_myname, 0x0);
593 make_nmb_name(&called , server, 0x20);
598 /* have to open a new connection */
599 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
600 !cli_connect(c, server_n, &ip)) {
601 DEBUG(0,("Connection to %s failed\n", server_n));
605 if (!cli_session_request(c, &calling, &called)) {
606 DEBUG(0,("session request to %s failed\n", called.name));
608 if (strcmp(called.name, "*SMBSERVER")) {
609 make_nmb_name(&called , "*SMBSERVER", 0x20);
615 DEBUG(4,(" session request ok\n"));
617 if (!cli_negprot(c)) {
618 DEBUG(0,("protocol negotiation failed\n"));
624 char *pass = getpass("Password: ");
626 pstrcpy(password, pass);
630 if (!cli_session_setup(c, username,
631 password, strlen(password),
632 password, strlen(password),
634 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
638 DEBUG(4,(" session setup ok\n"));
640 if (!cli_send_tconX(c, share, "?????",
641 password, strlen(password)+1)) {
642 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
647 DEBUG(4,(" tconx ok\n"));
653 static void usage(void)
656 "Usage: smbcacls //server1/share1 filename -U username [options]\n\
658 \t-D <acls> delete an acl\n\
659 \t-M <acls> modify an acl\n\
660 \t-A <acls> add an acl\n\
661 \t-S <acls> set acls\n\
662 \t-n don't resolve sids or masks to names\n\
665 The username can be of the form username%%password or\n\
666 workgroup\\username%%password.\n\n\
667 An acl is of the form ACL:<SID>:type/flags/mask\n\
668 You can string acls together with spaces, commas or newlines\n\
672 /****************************************************************************
674 ****************************************************************************/
675 int main(int argc,char *argv[])
685 static pstring servicesf = CONFIGFILE;
686 struct cli_state *cli;
694 if (argc < 4 || argv[1][0] == '-') {
699 setup_logging(argv[0],True);
703 all_string_sub(share,"/","\\",0);
709 charset_initialise();
711 lp_load(servicesf,True,False,False);
712 codepage_initialise(lp_client_code_page());
715 if (getenv("USER")) {
716 pstrcpy(username,getenv("USER"));
721 while ((opt = getopt(argc, argv, "U:nhS:D:A:M:t")) != EOF) {
724 pstrcpy(username,optarg);
725 p = strchr(username,'%');
728 pstrcpy(password, p+1);
766 printf("Unknown option %c (%d)\n", (char)opt, opt);
780 cli = connect_one(share);
785 cacl_set(cli, filename, acl, mode);
787 cacl_dump(cli, filename);