1/* 2 Unix SMB/CIFS implementation. 3 ACL get/set utility 4 5 Copyright (C) Andrew Tridgell 2000 6 Copyright (C) Tim Potter 2000 7 Copyright (C) Jeremy Allison 2000 8 Copyright (C) Jelmer Vernooij 2003 9 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. 14 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. 19 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/>. 22*/ 23 24#include "includes.h" 25 26extern bool AllowDebugChange; 27 28static int test_args; 29 30#define CREATE_ACCESS_READ READ_CONTROL_ACCESS 31 32/* numeric is set when the user wants numeric SIDs and ACEs rather 33 than going via LSA calls to resolve them */ 34static int numeric; 35 36enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD }; 37enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP}; 38enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; 39 40struct perm_value { 41 const char *perm; 42 uint32 mask; 43}; 44 45/* These values discovered by inspection */ 46 47static const struct perm_value special_values[] = { 48 { "R", 0x00120089 }, 49 { "W", 0x00120116 }, 50 { "X", 0x001200a0 }, 51 { "D", 0x00010000 }, 52 { "P", 0x00040000 }, 53 { "O", 0x00080000 }, 54 { NULL, 0 }, 55}; 56 57static const struct perm_value standard_values[] = { 58 { "READ", 0x001200a9 }, 59 { "CHANGE", 0x001301bf }, 60 { "FULL", 0x001f01ff }, 61 { NULL, 0 }, 62}; 63 64/* Open cli connection and policy handle */ 65 66static NTSTATUS cli_lsa_lookup_sid(struct cli_state *cli, 67 const DOM_SID *sid, 68 TALLOC_CTX *mem_ctx, 69 enum lsa_SidType *type, 70 char **domain, char **name) 71{ 72 uint16 orig_cnum = cli->cnum; 73 struct rpc_pipe_client *p = NULL; 74 struct policy_handle handle; 75 NTSTATUS status; 76 TALLOC_CTX *frame = talloc_stackframe(); 77 enum lsa_SidType *types; 78 char **domains; 79 char **names; 80 81 status = cli_tcon_andx(cli, "IPC$", "?????", "", 0); 82 if (!NT_STATUS_IS_OK(status)) { 83 return status; 84 } 85 86 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id, 87 &p); 88 if (!NT_STATUS_IS_OK(status)) { 89 goto fail; 90 } 91 92 status = rpccli_lsa_open_policy(p, talloc_tos(), True, 93 GENERIC_EXECUTE_ACCESS, &handle); 94 if (!NT_STATUS_IS_OK(status)) { 95 goto fail; 96 } 97 98 status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid, 99 &domains, &names, &types); 100 if (!NT_STATUS_IS_OK(status)) { 101 goto fail; 102 } 103 104 *type = types[0]; 105 *domain = talloc_move(mem_ctx, &domains[0]); 106 *name = talloc_move(mem_ctx, &names[0]); 107 108 status = NT_STATUS_OK; 109 fail: 110 TALLOC_FREE(p); 111 cli_tdis(cli); 112 cli->cnum = orig_cnum; 113 TALLOC_FREE(frame); 114 return status; 115} 116 117static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli, 118 const char *name, 119 enum lsa_SidType *type, 120 DOM_SID *sid) 121{ 122 uint16 orig_cnum = cli->cnum; 123 struct rpc_pipe_client *p; 124 struct policy_handle handle; 125 NTSTATUS status; 126 TALLOC_CTX *frame = talloc_stackframe(); 127 DOM_SID *sids; 128 enum lsa_SidType *types; 129 130 status = cli_tcon_andx(cli, "IPC$", "?????", "", 0); 131 if (!NT_STATUS_IS_OK(status)) { 132 return status; 133 } 134 135 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id, 136 &p); 137 if (!NT_STATUS_IS_OK(status)) { 138 goto fail; 139 } 140 141 status = rpccli_lsa_open_policy(p, talloc_tos(), True, 142 GENERIC_EXECUTE_ACCESS, &handle); 143 if (!NT_STATUS_IS_OK(status)) { 144 goto fail; 145 } 146 147 status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name, 148 NULL, 1, &sids, &types); 149 if (!NT_STATUS_IS_OK(status)) { 150 goto fail; 151 } 152 153 *type = types[0]; 154 *sid = sids[0]; 155 156 status = NT_STATUS_OK; 157 fail: 158 TALLOC_FREE(p); 159 cli_tdis(cli); 160 cli->cnum = orig_cnum; 161 TALLOC_FREE(frame); 162 return status; 163} 164 165/* convert a SID to a string, either numeric or username/group */ 166static void SidToString(struct cli_state *cli, fstring str, const DOM_SID *sid) 167{ 168 char *domain = NULL; 169 char *name = NULL; 170 enum lsa_SidType type; 171 NTSTATUS status; 172 173 sid_to_fstring(str, sid); 174 175 if (numeric) { 176 return; 177 } 178 179 status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type, 180 &domain, &name); 181 182 if (!NT_STATUS_IS_OK(status)) { 183 return; 184 } 185 186 if (*domain) { 187 slprintf(str, sizeof(fstring) - 1, "%s%s%s", 188 domain, lp_winbind_separator(), name); 189 } else { 190 fstrcpy(str, name); 191 } 192} 193 194/* convert a string to a SID, either numeric or username/group */ 195static bool StringToSid(struct cli_state *cli, DOM_SID *sid, const char *str) 196{ 197 enum lsa_SidType type; 198 199 if (strncmp(str, "S-", 2) == 0) { 200 return string_to_sid(sid, str); 201 } 202 203 return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid)); 204} 205 206static void print_ace_flags(FILE *f, uint8_t flags) 207{ 208 char *str = talloc_strdup(NULL, ""); 209 210 if (!str) { 211 goto out; 212 } 213 214 if (flags & SEC_ACE_FLAG_OBJECT_INHERIT) { 215 str = talloc_asprintf(str, "%s%s", 216 str, "OI|"); 217 if (!str) { 218 goto out; 219 } 220 } 221 if (flags & SEC_ACE_FLAG_CONTAINER_INHERIT) { 222 str = talloc_asprintf(str, "%s%s", 223 str, "CI|"); 224 if (!str) { 225 goto out; 226 } 227 } 228 if (flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { 229 str = talloc_asprintf(str, "%s%s", 230 str, "NP|"); 231 if (!str) { 232 goto out; 233 } 234 } 235 if (flags & SEC_ACE_FLAG_INHERIT_ONLY) { 236 str = talloc_asprintf(str, "%s%s", 237 str, "IO|"); 238 if (!str) { 239 goto out; 240 } 241 } 242 if (flags & SEC_ACE_FLAG_INHERITED_ACE) { 243 str = talloc_asprintf(str, "%s%s", 244 str, "I|"); 245 if (!str) { 246 goto out; 247 } 248 } 249 /* Ignore define SEC_ACE_FLAG_SUCCESSFUL_ACCESS ( 0x40 ) 250 and SEC_ACE_FLAG_FAILED_ACCESS ( 0x80 ) as they're 251 audit ace flags. */ 252 253 if (str[strlen(str)-1] == '|') { 254 str[strlen(str)-1] = '\0'; 255 fprintf(f, "/%s/", str); 256 } else { 257 fprintf(f, "/0x%x/", flags); 258 } 259 TALLOC_FREE(str); 260 return; 261 262 out: 263 fprintf(f, "/0x%x/", flags); 264} 265 266/* print an ACE on a FILE, using either numeric or ascii representation */ 267static void print_ace(struct cli_state *cli, FILE *f, SEC_ACE *ace) 268{ 269 const struct perm_value *v; 270 fstring sidstr; 271 int do_print = 0; 272 uint32 got_mask; 273 274 SidToString(cli, sidstr, &ace->trustee); 275 276 fprintf(f, "%s:", sidstr); 277 278 if (numeric) { 279 fprintf(f, "%d/0x%x/0x%08x", 280 ace->type, ace->flags, ace->access_mask); 281 return; 282 } 283 284 /* Ace type */ 285 286 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) { 287 fprintf(f, "ALLOWED"); 288 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) { 289 fprintf(f, "DENIED"); 290 } else { 291 fprintf(f, "%d", ace->type); 292 } 293 294 print_ace_flags(f, ace->flags); 295 296 /* Standard permissions */ 297 298 for (v = standard_values; v->perm; v++) { 299 if (ace->access_mask == v->mask) { 300 fprintf(f, "%s", v->perm); 301 return; 302 } 303 } 304 305 /* Special permissions. Print out a hex value if we have 306 leftover bits in the mask. */ 307 308 got_mask = ace->access_mask; 309 310 again: 311 for (v = special_values; v->perm; v++) { 312 if ((ace->access_mask & v->mask) == v->mask) { 313 if (do_print) { 314 fprintf(f, "%s", v->perm); 315 } 316 got_mask &= ~v->mask; 317 } 318 } 319 320 if (!do_print) { 321 if (got_mask != 0) { 322 fprintf(f, "0x%08x", ace->access_mask); 323 } else { 324 do_print = 1; 325 goto again; 326 } 327 } 328} 329 330static bool parse_ace_flags(const char *str, unsigned int *pflags) 331{ 332 const char *p = str; 333 *pflags = 0; 334 335 while (*p) { 336 if (strnequal(p, "OI", 2)) { 337 *pflags |= SEC_ACE_FLAG_OBJECT_INHERIT; 338 p += 2; 339 } else if (strnequal(p, "CI", 2)) { 340 *pflags |= SEC_ACE_FLAG_CONTAINER_INHERIT; 341 p += 2; 342 } else if (strnequal(p, "NP", 2)) { 343 *pflags |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT; 344 p += 2; 345 } else if (strnequal(p, "IO", 2)) { 346 *pflags |= SEC_ACE_FLAG_INHERIT_ONLY; 347 p += 2; 348 } else if (*p == 'I') { 349 *pflags |= SEC_ACE_FLAG_INHERITED_ACE; 350 p += 1; 351 } else if (*p) { 352 return false; 353 } 354 355 if (*p != '|' && *p != '\0') { 356 return false; 357 } 358 } 359 return true; 360} 361 362/* parse an ACE in the same format as print_ace() */ 363static bool parse_ace(struct cli_state *cli, SEC_ACE *ace, 364 const char *orig_str) 365{ 366 char *p; 367 const char *cp; 368 char *tok; 369 unsigned int atype = 0; 370 unsigned int aflags = 0; 371 unsigned int amask = 0; 372 DOM_SID sid; 373 uint32_t mask; 374 const struct perm_value *v; 375 char *str = SMB_STRDUP(orig_str); 376 TALLOC_CTX *frame = talloc_stackframe(); 377 378 if (!str) { 379 TALLOC_FREE(frame); 380 return False; 381 } 382 383 ZERO_STRUCTP(ace); 384 p = strchr_m(str,':'); 385 if (!p) { 386 printf("ACE '%s': missing ':'.\n", orig_str); 387 SAFE_FREE(str); 388 TALLOC_FREE(frame); 389 return False; 390 } 391 *p = '\0'; 392 p++; 393 /* Try to parse numeric form */ 394 395 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 && 396 StringToSid(cli, &sid, str)) { 397 goto done; 398 } 399 400 /* Try to parse text form */ 401 402 if (!StringToSid(cli, &sid, str)) { 403 printf("ACE '%s': failed to convert '%s' to SID\n", 404 orig_str, str); 405 SAFE_FREE(str); 406 TALLOC_FREE(frame); 407 return False; 408 } 409 410 cp = p; 411 if (!next_token_talloc(frame, &cp, &tok, "/")) { 412 printf("ACE '%s': failed to find '/' character.\n", 413 orig_str); 414 SAFE_FREE(str); 415 TALLOC_FREE(frame); 416 return False; 417 } 418 419 if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { 420 atype = SEC_ACE_TYPE_ACCESS_ALLOWED; 421 } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) { 422 atype = SEC_ACE_TYPE_ACCESS_DENIED; 423 } else { 424 printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n", 425 orig_str, tok); 426 SAFE_FREE(str); 427 TALLOC_FREE(frame); 428 return False; 429 } 430 431 /* Only numeric form accepted for flags at present */ 432 433 if (!next_token_talloc(frame, &cp, &tok, "/")) { 434 printf("ACE '%s': bad flags entry at '%s'\n", 435 orig_str, tok); 436 SAFE_FREE(str); 437 TALLOC_FREE(frame); 438 return False; 439 } 440 441 if (tok[0] < '0' || tok[0] > '9') { 442 if (!parse_ace_flags(tok, &aflags)) { 443 printf("ACE '%s': bad named flags entry at '%s'\n", 444 orig_str, tok); 445 SAFE_FREE(str); 446 TALLOC_FREE(frame); 447 return False; 448 } 449 } else if (strnequal(tok, "0x", 2)) { 450 if (!sscanf(tok, "%x", &aflags)) { 451 printf("ACE '%s': bad hex flags entry at '%s'\n", 452 orig_str, tok); 453 SAFE_FREE(str); 454 TALLOC_FREE(frame); 455 return False; 456 } 457 } else { 458 if (!sscanf(tok, "%i", &aflags)) { 459 printf("ACE '%s': bad integer flags entry at '%s'\n", 460 orig_str, tok); 461 SAFE_FREE(str); 462 TALLOC_FREE(frame); 463 return False; 464 } 465 } 466 467 if (!next_token_talloc(frame, &cp, &tok, "/")) { 468 printf("ACE '%s': missing / at '%s'\n", 469 orig_str, tok); 470 SAFE_FREE(str); 471 TALLOC_FREE(frame); 472 return False; 473 } 474 475 if (strncmp(tok, "0x", 2) == 0) { 476 if (sscanf(tok, "%i", &amask) != 1) { 477 printf("ACE '%s': bad hex number at '%s'\n", 478 orig_str, tok); 479 SAFE_FREE(str); 480 TALLOC_FREE(frame); 481 return False; 482 } 483 goto done; 484 } 485 486 for (v = standard_values; v->perm; v++) { 487 if (strcmp(tok, v->perm) == 0) { 488 amask = v->mask; 489 goto done; 490 } 491 } 492 493 p = tok; 494 495 while(*p) { 496 bool found = False; 497 498 for (v = special_values; v->perm; v++) { 499 if (v->perm[0] == *p) { 500 amask |= v->mask; 501 found = True; 502 } 503 } 504 505 if (!found) { 506 printf("ACE '%s': bad permission value at '%s'\n", 507 orig_str, p); 508 SAFE_FREE(str); 509 TALLOC_FREE(frame); 510 return False; 511 } 512 p++; 513 } 514 515 if (*p) { 516 TALLOC_FREE(frame); 517 SAFE_FREE(str); 518 return False; 519 } 520 521 done: 522 mask = amask; 523 init_sec_ace(ace, &sid, atype, mask, aflags); 524 TALLOC_FREE(frame); 525 SAFE_FREE(str); 526 return True; 527} 528 529/* add an ACE to a list of ACEs in a SEC_ACL */ 530static bool add_ace(SEC_ACL **the_acl, SEC_ACE *ace) 531{ 532 SEC_ACL *new_ace; 533 SEC_ACE *aces; 534 if (! *the_acl) { 535 return (((*the_acl) = make_sec_acl(talloc_tos(), 3, 1, ace)) 536 != NULL); 537 } 538 539 if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) { 540 return False; 541 } 542 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE)); 543 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE)); 544 new_ace = make_sec_acl(talloc_tos(),(*the_acl)->revision,1+(*the_acl)->num_aces, aces); 545 SAFE_FREE(aces); 546 (*the_acl) = new_ace; 547 return True; 548} 549 550/* parse a ascii version of a security descriptor */ 551static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str) 552{ 553 const char *p = str; 554 char *tok; 555 SEC_DESC *ret = NULL; 556 size_t sd_size; 557 DOM_SID *grp_sid=NULL, *owner_sid=NULL; 558 SEC_ACL *dacl=NULL; 559 int revision=1; 560 561 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) { 562 if (strncmp(tok,"REVISION:", 9) == 0) { 563 revision = strtol(tok+9, NULL, 16); 564 continue; 565 } 566 567 if (strncmp(tok,"OWNER:", 6) == 0) { 568 if (owner_sid) { 569 printf("Only specify owner once\n"); 570 goto done; 571 } 572 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); 573 if (!owner_sid || 574 !StringToSid(cli, owner_sid, tok+6)) { 575 printf("Failed to parse owner sid\n"); 576 goto done; 577 } 578 continue; 579 } 580 581 if (strncmp(tok,"GROUP:", 6) == 0) { 582 if (grp_sid) { 583 printf("Only specify group once\n"); 584 goto done; 585 } 586 grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); 587 if (!grp_sid || 588 !StringToSid(cli, grp_sid, tok+6)) { 589 printf("Failed to parse group sid\n"); 590 goto done; 591 } 592 continue; 593 } 594 595 if (strncmp(tok,"ACL:", 4) == 0) { 596 SEC_ACE ace; 597 if (!parse_ace(cli, &ace, tok+4)) { 598 goto done; 599 } 600 if(!add_ace(&dacl, &ace)) { 601 printf("Failed to add ACL %s\n", tok); 602 goto done; 603 } 604 continue; 605 } 606 607 printf("Failed to parse token '%s' in security descriptor,\n", tok); 608 goto done; 609 } 610 611 ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid, 612 NULL, dacl, &sd_size); 613 614 done: 615 SAFE_FREE(grp_sid); 616 SAFE_FREE(owner_sid); 617 618 return ret; 619} 620 621 622/* print a ascii version of a security descriptor on a FILE handle */ 623static void sec_desc_print(struct cli_state *cli, FILE *f, SEC_DESC *sd) 624{ 625 fstring sidstr; 626 uint32 i; 627 628 fprintf(f, "REVISION:%d\n", sd->revision); 629 fprintf(f, "CONTROL:0x%x\n", sd->type); 630 631 /* Print owner and group sid */ 632 633 if (sd->owner_sid) { 634 SidToString(cli, sidstr, sd->owner_sid); 635 } else { 636 fstrcpy(sidstr, ""); 637 } 638 639 fprintf(f, "OWNER:%s\n", sidstr); 640 641 if (sd->group_sid) { 642 SidToString(cli, sidstr, sd->group_sid); 643 } else { 644 fstrcpy(sidstr, ""); 645 } 646 647 fprintf(f, "GROUP:%s\n", sidstr); 648 649 /* Print aces */ 650 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { 651 SEC_ACE *ace = &sd->dacl->aces[i]; 652 fprintf(f, "ACL:"); 653 print_ace(cli, f, ace); 654 fprintf(f, "\n"); 655 } 656 657} 658 659/***************************************************** 660dump the acls for a file 661*******************************************************/ 662static int cacl_dump(struct cli_state *cli, char *filename) 663{ 664 int result = EXIT_FAILED; 665 uint16_t fnum = (uint16_t)-1; 666 SEC_DESC *sd; 667 668 if (test_args) 669 return EXIT_OK; 670 671 if (!NT_STATUS_IS_OK(cli_ntcreate(cli, filename, 0, CREATE_ACCESS_READ, 0, 672 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum))) { 673 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 674 goto done; 675 } 676 677 sd = cli_query_secdesc(cli, fnum, talloc_tos()); 678 679 if (!sd) { 680 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli)); 681 goto done; 682 } 683 684 sec_desc_print(cli, stdout, sd); 685 686 result = EXIT_OK; 687 688done: 689 if (fnum != (uint16_t)-1) 690 cli_close(cli, fnum); 691 692 return result; 693} 694 695/***************************************************** 696Change the ownership or group ownership of a file. Just 697because the NT docs say this can't be done :-). JRA. 698*******************************************************/ 699 700static int owner_set(struct cli_state *cli, enum chown_mode change_mode, 701 const char *filename, const char *new_username) 702{ 703 uint16_t fnum; 704 DOM_SID sid; 705 SEC_DESC *sd, *old; 706 size_t sd_size; 707 708 if (!NT_STATUS_IS_OK(cli_ntcreate(cli, filename, 0, CREATE_ACCESS_READ, 0, 709 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum))) { 710 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 711 return EXIT_FAILED; 712 } 713 714 if (!StringToSid(cli, &sid, new_username)) 715 return EXIT_PARSE_ERROR; 716 717 old = cli_query_secdesc(cli, fnum, talloc_tos()); 718 719 cli_close(cli, fnum); 720 721 if (!old) { 722 printf("owner_set: Failed to query old descriptor\n"); 723 return EXIT_FAILED; 724 } 725 726 sd = make_sec_desc(talloc_tos(),old->revision, old->type, 727 (change_mode == REQUEST_CHOWN) ? &sid : NULL, 728 (change_mode == REQUEST_CHGRP) ? &sid : NULL, 729 NULL, NULL, &sd_size); 730 731 if (!NT_STATUS_IS_OK(cli_ntcreate(cli, filename, 0, WRITE_OWNER_ACCESS, 0, 732 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum))) { 733 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 734 return EXIT_FAILED; 735 } 736 737 if (!cli_set_secdesc(cli, fnum, sd)) { 738 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); 739 cli_close(cli, fnum); 740 return EXIT_FAILED; 741 } 742 743 cli_close(cli, fnum); 744 745 return EXIT_OK; 746} 747 748 749/* The MSDN is contradictory over the ordering of ACE entries in an 750 ACL. However NT4 gives a "The information may have been modified 751 by a computer running Windows NT 5.0" if denied ACEs do not appear 752 before allowed ACEs. At 753 http://technet.microsoft.com/en-us/library/cc781716.aspx the 754 canonical order is specified as "Explicit Deny, Explicit Allow, 755 Inherited ACEs unchanged" */ 756 757static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2) 758{ 759 if (sec_ace_equal(ace1, ace2)) 760 return 0; 761 762 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && 763 !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) 764 return 1; 765 if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && 766 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) 767 return -1; 768 if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && 769 (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) 770 return ace1 - ace2; 771 772 if (ace1->type != ace2->type) 773 return ace2->type - ace1->type; 774 775 if (sid_compare(&ace1->trustee, &ace2->trustee)) 776 return sid_compare(&ace1->trustee, &ace2->trustee); 777 778 if (ace1->flags != ace2->flags) 779 return ace1->flags - ace2->flags; 780 781 if (ace1->access_mask != ace2->access_mask) 782 return ace1->access_mask - ace2->access_mask; 783 784 if (ace1->size != ace2->size) 785 return ace1->size - ace2->size; 786 787 return memcmp(ace1, ace2, sizeof(SEC_ACE)); 788} 789 790static void sort_acl(SEC_ACL *the_acl) 791{ 792 uint32 i; 793 if (!the_acl) return; 794 795 qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare); 796 797 for (i=1;i<the_acl->num_aces;) { 798 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) { 799 int j; 800 for (j=i; j<the_acl->num_aces-1; j++) { 801 the_acl->aces[j] = the_acl->aces[j+1]; 802 } 803 the_acl->num_aces--; 804 } else { 805 i++; 806 } 807 } 808} 809 810/***************************************************** 811set the ACLs on a file given an ascii description 812*******************************************************/ 813 814static int cacl_set(struct cli_state *cli, char *filename, 815 char *the_acl, enum acl_mode mode) 816{ 817 uint16_t fnum; 818 SEC_DESC *sd, *old; 819 uint32 i, j; 820 size_t sd_size; 821 int result = EXIT_OK; 822 823 sd = sec_desc_parse(talloc_tos(), cli, the_acl); 824 825 if (!sd) return EXIT_PARSE_ERROR; 826 if (test_args) return EXIT_OK; 827 828 /* The desired access below is the only one I could find that works 829 with NT4, W2KP and Samba */ 830 831 if (!NT_STATUS_IS_OK(cli_ntcreate(cli, filename, 0, CREATE_ACCESS_READ, 0, 832 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum))) { 833 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); 834 return EXIT_FAILED; 835 } 836 837 old = cli_query_secdesc(cli, fnum, talloc_tos()); 838 839 if (!old) { 840 printf("calc_set: Failed to query old descriptor\n"); 841 return EXIT_FAILED; 842 } 843 844 cli_close(cli, fnum); 845 846 /* the logic here is rather more complex than I would like */ 847 switch (mode) { 848 case SMB_ACL_DELETE: 849 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 850 bool found = False; 851 852 for (j=0;old->dacl && j<old->dacl->num_aces;j++) { 853 if (sec_ace_equal(&sd->dacl->aces[i], 854 &old->dacl->aces[j])) { 855 uint32 k; 856 for (k=j; k<old->dacl->num_aces-1;k++) { 857 old->dacl->aces[k] = old->dacl->aces[k+1]; 858 } 859 old->dacl->num_aces--; 860 found = True; 861 break; 862 } 863 } 864 865 if (!found) { 866 printf("ACL for ACE:"); 867 print_ace(cli, stdout, &sd->dacl->aces[i]); 868 printf(" not found\n"); 869 } 870 } 871 break; 872 873 case SMB_ACL_MODIFY: 874 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 875 bool found = False; 876 877 for (j=0;old->dacl && j<old->dacl->num_aces;j++) { 878 if (sid_equal(&sd->dacl->aces[i].trustee, 879 &old->dacl->aces[j].trustee)) { 880 old->dacl->aces[j] = sd->dacl->aces[i]; 881 found = True; 882 } 883 } 884 885 if (!found) { 886 fstring str; 887 888 SidToString(cli, str, 889 &sd->dacl->aces[i].trustee); 890 printf("ACL for SID %s not found\n", str); 891 } 892 } 893 894 if (sd->owner_sid) { 895 old->owner_sid = sd->owner_sid; 896 } 897 898 if (sd->group_sid) { 899 old->group_sid = sd->group_sid; 900 } 901 902 break; 903 904 case SMB_ACL_ADD: 905 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 906 add_ace(&old->dacl, &sd->dacl->aces[i]); 907 } 908 break; 909 910 case SMB_ACL_SET: 911 old = sd; 912 break; 913 } 914 915 /* Denied ACE entries must come before allowed ones */ 916 sort_acl(old->dacl); 917 918 /* Create new security descriptor and set it */ 919 920 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER. 921 But if we're sending an owner, even if it's the same as the one 922 that already exists then W2K3 insists we open with WRITE_OWNER access. 923 I need to check that setting a SD with no owner set works against WNT 924 and W2K. JRA. 925 */ 926 927 sd = make_sec_desc(talloc_tos(),old->revision, old->type, 928 old->owner_sid, old->group_sid, 929 NULL, old->dacl, &sd_size); 930 931 if (!NT_STATUS_IS_OK(cli_ntcreate(cli, filename, 0, WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS, 0, 932 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum))) { 933 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); 934 return EXIT_FAILED; 935 } 936 937 if (!cli_set_secdesc(cli, fnum, sd)) { 938 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); 939 result = EXIT_FAILED; 940 } 941 942 /* Clean up */ 943 944 cli_close(cli, fnum); 945 946 return result; 947} 948 949 950/***************************************************** 951 Return a connection to a server. 952*******************************************************/ 953static struct cli_state *connect_one(struct user_auth_info *auth_info, 954 const char *server, const char *share) 955{ 956 struct cli_state *c = NULL; 957 struct sockaddr_storage ss; 958 NTSTATUS nt_status; 959 uint32_t flags = 0; 960 961 zero_sockaddr(&ss); 962 963 if (get_cmdline_auth_info_use_kerberos(auth_info)) { 964 flags |= CLI_FULL_CONNECTION_USE_KERBEROS | 965 CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS; 966 } 967 968 if (get_cmdline_auth_info_use_machine_account(auth_info) && 969 !set_cmdline_auth_info_machine_account_creds(auth_info)) { 970 return NULL; 971 } 972 973 set_cmdline_auth_info_getpass(auth_info); 974 975 nt_status = cli_full_connection(&c, global_myname(), server, 976 &ss, 0, 977 share, "?????", 978 get_cmdline_auth_info_username(auth_info), 979 lp_workgroup(), 980 get_cmdline_auth_info_password(auth_info), 981 flags, 982 get_cmdline_auth_info_signing_state(auth_info), 983 NULL); 984 if (!NT_STATUS_IS_OK(nt_status)) { 985 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); 986 return NULL; 987 } 988 989 if (get_cmdline_auth_info_smb_encrypt(auth_info)) { 990 nt_status = cli_cm_force_encryption(c, 991 get_cmdline_auth_info_username(auth_info), 992 get_cmdline_auth_info_password(auth_info), 993 lp_workgroup(), 994 share); 995 if (!NT_STATUS_IS_OK(nt_status)) { 996 cli_shutdown(c); 997 c = NULL; 998 } 999 } 1000 1001 return c; 1002} 1003 1004/**************************************************************************** 1005 main program 1006****************************************************************************/ 1007 int main(int argc, const char *argv[]) 1008{ 1009 char *share; 1010 int opt; 1011 enum acl_mode mode = SMB_ACL_SET; 1012 static char *the_acl = NULL; 1013 enum chown_mode change_mode = REQUEST_NONE; 1014 int result; 1015 char *path; 1016 char *filename = NULL; 1017 poptContext pc; 1018 struct poptOption long_options[] = { 1019 POPT_AUTOHELP 1020 { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" }, 1021 { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" }, 1022 { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" }, 1023 { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" }, 1024 { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" }, 1025 { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" }, 1026 { "numeric", 0, POPT_ARG_NONE, &numeric, 1, "Don't resolve sids or masks to names" }, 1027 { "test-args", 't', POPT_ARG_NONE, &test_args, 1, "Test arguments"}, 1028 POPT_COMMON_SAMBA 1029 POPT_COMMON_CONNECTION 1030 POPT_COMMON_CREDENTIALS 1031 POPT_TABLEEND 1032 }; 1033 1034 struct cli_state *cli; 1035 TALLOC_CTX *frame = talloc_stackframe(); 1036 const char *owner_username = ""; 1037 char *server; 1038 struct user_auth_info *auth_info; 1039 1040 load_case_tables(); 1041 1042 1043 /* set default debug level to 1 regardless of what smb.conf sets */ 1044 setup_logging( "smbcacls", True ); 1045 DEBUGLEVEL_CLASS[DBGC_ALL] = 1; 1046 dbf = x_stderr; 1047 x_setbuf( x_stderr, NULL ); 1048 AllowDebugChange = false; 1049 1050 setlinebuf(stdout); 1051 1052 lp_load(get_dyn_CONFIGFILE(),True,False,False,True); 1053 load_interfaces(); 1054 1055 auth_info = user_auth_info_init(frame); 1056 if (auth_info == NULL) { 1057 exit(1); 1058 } 1059 popt_common_set_auth_info(auth_info); 1060 1061 pc = poptGetContext("smbcacls", argc, argv, long_options, 0); 1062 1063 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: " 1064 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'"); 1065 1066 while ((opt = poptGetNextOpt(pc)) != -1) { 1067 switch (opt) { 1068 case 'S': 1069 the_acl = smb_xstrdup(poptGetOptArg(pc)); 1070 mode = SMB_ACL_SET; 1071 break; 1072 1073 case 'D': 1074 the_acl = smb_xstrdup(poptGetOptArg(pc)); 1075 mode = SMB_ACL_DELETE; 1076 break; 1077 1078 case 'M': 1079 the_acl = smb_xstrdup(poptGetOptArg(pc)); 1080 mode = SMB_ACL_MODIFY; 1081 break; 1082 1083 case 'a': 1084 the_acl = smb_xstrdup(poptGetOptArg(pc)); 1085 mode = SMB_ACL_ADD; 1086 break; 1087 1088 case 'C': 1089 owner_username = poptGetOptArg(pc); 1090 change_mode = REQUEST_CHOWN; 1091 break; 1092 1093 case 'G': 1094 owner_username = poptGetOptArg(pc); 1095 change_mode = REQUEST_CHGRP; 1096 break; 1097 } 1098 } 1099 1100 /* Make connection to server */ 1101 if(!poptPeekArg(pc)) { 1102 poptPrintUsage(pc, stderr, 0); 1103 return -1; 1104 } 1105 1106 path = talloc_strdup(frame, poptGetArg(pc)); 1107 if (!path) { 1108 return -1; 1109 } 1110 1111 if(!poptPeekArg(pc)) { 1112 poptPrintUsage(pc, stderr, 0); 1113 return -1; 1114 } 1115 1116 filename = talloc_strdup(frame, poptGetArg(pc)); 1117 if (!filename) { 1118 return -1; 1119 } 1120 1121 string_replace(path,'/','\\'); 1122 1123 server = talloc_strdup(frame, path+2); 1124 if (!server) { 1125 return -1; 1126 } 1127 share = strchr_m(server,'\\'); 1128 if (!share) { 1129 printf("Invalid argument: %s\n", share); 1130 return -1; 1131 } 1132 1133 *share = 0; 1134 share++; 1135 1136 if (!test_args) { 1137 cli = connect_one(auth_info, server, share); 1138 if (!cli) { 1139 exit(EXIT_FAILED); 1140 } 1141 } else { 1142 exit(0); 1143 } 1144 1145 string_replace(filename, '/', '\\'); 1146 if (filename[0] != '\\') { 1147 filename = talloc_asprintf(frame, 1148 "\\%s", 1149 filename); 1150 if (!filename) { 1151 return -1; 1152 } 1153 } 1154 1155 /* Perform requested action */ 1156 1157 if (change_mode != REQUEST_NONE) { 1158 result = owner_set(cli, change_mode, filename, owner_username); 1159 } else if (the_acl) { 1160 result = cacl_set(cli, filename, the_acl, mode); 1161 } else { 1162 result = cacl_dump(cli, filename); 1163 } 1164 1165 TALLOC_FREE(frame); 1166 1167 return result; 1168} 1169