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;
29 /* numeric is set when the user wants numeric SIDs and ACEs rather
30 than going via LSA calls to resolve them */
33 enum acl_mode {ACL_SET, ACL_DELETE, ACL_MODIFY, ACL_ADD};
40 /* These values discovered by inspection */
42 static struct perm_value special_values[] = {
52 static struct perm_value standard_values[] = {
53 { "READ", 0x001200a9 },
54 { "CHANGE", 0x001301bf },
55 { "FULL", 0x001f01ff },
59 /* convert a SID to a string, either numeric or username/group */
60 static void SidToString(fstring str, DOM_SID *sid)
63 sid_to_string(str, sid);
66 /* Need to add LSA lookups */
68 sid_to_string(str, sid);
72 /* convert a string to a SID, either numeric or username/group */
73 static BOOL StringToSid(DOM_SID *sid, fstring str)
75 if (strncmp(str,"S-", 2) == 0) {
76 return string_to_sid(sid, str);
79 /* Need to add LSA lookups */
81 return string_to_sid(sid, str);
86 /* print an ACE on a FILE, using either numeric or ascii representation */
87 static void print_ace(FILE *f, SEC_ACE *ace)
92 SidToString(sidstr, &ace->sid);
94 fprintf(f, "%s:", sidstr);
97 fprintf(f, "%d/%d/0x%08x\n",
98 ace->type, ace->flags, ace->info.mask);
104 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
105 fprintf(f, "ALLOWED");
106 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
107 fprintf(f, "DENIED");
109 fprintf(f, "%d", ace->type);
112 /* Not sure what flags can be set in a file ACL */
114 fprintf(f, "/%d/", ace->flags);
116 /* Standard permissions */
118 for (v = standard_values; v->perm; v++) {
119 if (ace->info.mask == v->mask) {
120 fprintf(f, "%s\n", v->perm);
125 /* Special permissions */
127 for (v = special_values; v->perm; v++) {
128 if ((ace->info.mask & v->mask) == v->mask) {
129 fprintf(f, "%s", v->perm);
137 /* parse an ACE in the same format as print_ace() */
138 static BOOL parse_ace(SEC_ACE *ace, char *str)
141 unsigned atype, aflags, amask;
146 if (!p) return False;
148 if (sscanf(p+1, "%x/%x/%08x",
149 &atype, &aflags, &amask) != 3 ||
150 !StringToSid(&sid, str)) {
154 init_sec_ace(ace, &sid, atype, mask, aflags);
158 /* add an ACE to a list of ACEs in a SEC_ACL */
159 static BOOL add_ace(SEC_ACL **acl, SEC_ACE *ace)
164 (*acl) = make_sec_acl(3, 1, ace);
168 aces = calloc(1+(*acl)->num_aces,sizeof(SEC_ACE));
169 memcpy(aces, (*acl)->ace, (*acl)->num_aces * sizeof(SEC_ACE));
170 memcpy(aces+(*acl)->num_aces, ace, sizeof(SEC_ACE));
171 new = make_sec_acl((*acl)->revision,1+(*acl)->num_aces, aces);
178 /* parse a ascii version of a security descriptor */
179 static SEC_DESC *sec_desc_parse(char *str)
185 DOM_SID *grp_sid=NULL, *owner_sid=NULL;
190 while (next_token(&p, tok, " \t,\r\n", sizeof(tok))) {
192 if (strncmp(tok,"REVISION:", 9) == 0) {
193 revision = strtol(tok+9, NULL, 16);
196 if (strncmp(tok,"TYPE:", 5) == 0) {
197 type = strtol(tok+5, NULL, 16);
200 if (strncmp(tok,"OWNER:", 6) == 0) {
201 owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
203 !StringToSid(owner_sid, tok+6)) {
204 printf("Failed to parse owner sid\n");
209 if (strncmp(tok,"GROUP:", 6) == 0) {
210 grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
212 !StringToSid(grp_sid, tok+6)) {
213 printf("Failed to parse group sid\n");
218 if (strncmp(tok,"ACL:", 4) == 0) {
220 if (!parse_ace(&ace, tok+4) ||
221 !add_ace(&dacl, &ace)) {
222 printf("Failed to parse ACL\n");
228 ret = make_sec_desc(revision, owner_sid, grp_sid,
229 NULL, dacl, &sd_size);
232 if (grp_sid) free(grp_sid);
233 if (owner_sid) free(owner_sid);
239 /* print a ascii version of a security descriptor on a FILE handle */
240 static void sec_desc_print(FILE *f, SEC_DESC *sd)
245 printf("REVISION:%d\nTYPE:0x%x\n", sd->revision, sd->type);
247 /* Print owner and group sid */
250 SidToString(sidstr, sd->owner_sid);
255 printf("OWNER:%s\n", sidstr);
258 SidToString(sidstr, sd->grp_sid);
263 fprintf(f, "GROUP:%s\n", sidstr);
266 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
267 SEC_ACE *ace = &sd->dacl->ace[i];
275 /*****************************************************
276 dump the acls for a file
277 *******************************************************/
278 static void cacl_dump(struct cli_state *cli, char *filename)
283 fnum = cli_nt_create(cli, filename);
285 printf("Failed to open %s\n", filename);
289 sd = cli_query_secdesc(cli, fnum);
292 printf("ERROR: secdesc query failed\n");
296 sec_desc_print(stdout, sd);
300 cli_close(cli, fnum);
303 /*****************************************************
304 set the ACLs on a file given an ascii description
305 *******************************************************/
306 static void cacl_set(struct cli_state *cli, char *filename,
307 char *acl, enum acl_mode mode)
314 sd = sec_desc_parse(acl);
316 printf("Failed to parse security descriptor\n");
320 fnum = cli_nt_create(cli, filename);
322 printf("Failed to open %s\n", filename);
326 old = cli_query_secdesc(cli, fnum);
328 /* the logic here is rather more complex than I would like */
331 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
332 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
333 if (sec_ace_equal(&sd->dacl->ace[i],
334 &old->dacl->ace[j])) {
335 if (j != old->dacl->num_aces-1) {
336 old->dacl->ace[j] = old->dacl->ace[j+1];
338 old->dacl->num_aces--;
339 if (old->dacl->num_aces == 0) {
340 free(old->dacl->ace);
353 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
356 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
357 if (sid_equal(&sd->dacl->ace[i].sid,
358 &old->dacl->ace[j].sid)) {
359 old->dacl->ace[j] = sd->dacl->ace[i];
367 SidToString(str, &sd->dacl->ace[i].sid);
368 printf("ACL for SID %s not found\n", str);
375 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
376 add_ace(&old->dacl, &sd->dacl->ace[i]);
391 sd = make_sec_desc(old->revision, old->owner_sid, old->grp_sid,
392 NULL, old->dacl, &sd_size);
394 if (!cli_set_secdesc(cli, fnum, sd)) {
395 printf("ERROR: secdesc set failed\n");
402 cli_close(cli, fnum);
406 /*****************************************************
407 return a connection to a server
408 *******************************************************/
409 struct cli_state *connect_one(char *share)
412 struct nmb_name called, calling;
416 extern struct in_addr ipzero;
417 extern pstring global_myname;
419 fstrcpy(server,share+2);
420 share = strchr(server,'\\');
421 if (!share) return NULL;
429 make_nmb_name(&calling, global_myname, 0x0);
430 make_nmb_name(&called , server, 0x20);
435 /* have to open a new connection */
436 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, 139) == 0) ||
437 !cli_connect(c, server_n, &ip)) {
438 DEBUG(0,("Connection to %s failed\n", server_n));
442 if (!cli_session_request(c, &calling, &called)) {
443 DEBUG(0,("session request to %s failed\n", called.name));
445 if (strcmp(called.name, "*SMBSERVER")) {
446 make_nmb_name(&called , "*SMBSERVER", 0x20);
452 DEBUG(4,(" session request ok\n"));
454 if (!cli_negprot(c)) {
455 DEBUG(0,("protocol negotiation failed\n"));
461 char *pass = getpass("Password: ");
463 pstrcpy(password, pass);
467 if (!cli_session_setup(c, username,
468 password, strlen(password),
469 password, strlen(password),
471 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
475 DEBUG(4,(" session setup ok\n"));
477 if (!cli_send_tconX(c, share, "?????",
478 password, strlen(password)+1)) {
479 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
484 DEBUG(4,(" tconx ok\n"));
490 static void usage(void)
493 "Usage: smbcacls //server1/share1 filename [options]\n\n\
495 \t-D <acls> delete an acl\n\
496 \t-M <acls> modify an acl\n\
497 \t-A <acls> add an acl\n\
498 \t-S <acls> set acls\n\
499 \t-U username set the network username\n\
500 \t-n don't resolve sids or masks to names\n\
503 An acl is of the form ACL:<SID>:type/flags/mask\n\
504 You can string acls together with spaces, commas or newlines\n\
508 /****************************************************************************
510 ****************************************************************************/
511 int main(int argc,char *argv[])
521 static pstring servicesf = CONFIGFILE;
522 struct cli_state *cli;
530 if (argc < 4 || argv[1][0] == '-') {
535 setup_logging(argv[0],True);
539 all_string_sub(share,"/","\\",0);
545 charset_initialise();
547 lp_load(servicesf,True,False,False);
548 codepage_initialise(lp_client_code_page());
551 if (getenv("USER")) {
552 pstrcpy(username,getenv("USER"));
557 while ((opt = getopt(argc, argv, "U:nhS:D:A:M:")) != EOF) {
560 pstrcpy(username,optarg);
561 p = strchr(username,'%');
564 pstrcpy(password, p+1);
598 printf("Unknown option %c (%d)\n", (char)opt, opt);
606 cli = connect_one(share);
610 cacl_set(cli, filename, acl, mode);
612 cacl_dump(cli, filename);