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 2 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, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23*/ 24 25#include "includes.h" 26 27static pstring owner_username; 28static fstring server; 29static int test_args = False; 30static TALLOC_CTX *ctx; 31 32#define CREATE_ACCESS_READ READ_CONTROL_ACCESS 33 34/* numeric is set when the user wants numeric SIDs and ACEs rather 35 than going via LSA calls to resolve them */ 36static BOOL numeric = False; 37 38enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD }; 39enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP}; 40enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; 41 42struct perm_value { 43 const char *perm; 44 uint32 mask; 45}; 46 47/* These values discovered by inspection */ 48 49static const struct perm_value special_values[] = { 50 { "R", 0x00120089 }, 51 { "W", 0x00120116 }, 52 { "X", 0x001200a0 }, 53 { "D", 0x00010000 }, 54 { "P", 0x00040000 }, 55 { "O", 0x00080000 }, 56 { NULL, 0 }, 57}; 58 59static const struct perm_value standard_values[] = { 60 { "READ", 0x001200a9 }, 61 { "CHANGE", 0x001301bf }, 62 { "FULL", 0x001f01ff }, 63 { NULL, 0 }, 64}; 65 66static struct cli_state *global_hack_cli; 67static struct rpc_pipe_client *global_pipe_hnd; 68static POLICY_HND pol; 69static BOOL got_policy_hnd; 70 71static struct cli_state *connect_one(const char *share); 72 73/* Open cli connection and policy handle */ 74 75static BOOL cacls_open_policy_hnd(void) 76{ 77 /* Initialise cli LSA connection */ 78 79 if (!global_hack_cli) { 80 NTSTATUS ret; 81 global_hack_cli = connect_one("IPC$"); 82 global_pipe_hnd = cli_rpc_pipe_open_noauth(global_hack_cli, PI_LSARPC, &ret); 83 if (!global_pipe_hnd) { 84 return False; 85 } 86 } 87 88 /* Open policy handle */ 89 90 if (!got_policy_hnd) { 91 92 /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED, 93 but NT sends 0x2000000 so we might as well do it too. */ 94 95 if (!NT_STATUS_IS_OK(rpccli_lsa_open_policy(global_pipe_hnd, global_hack_cli->mem_ctx, True, 96 GENERIC_EXECUTE_ACCESS, &pol))) { 97 return False; 98 } 99 100 got_policy_hnd = True; 101 } 102 103 return True; 104} 105 106/* convert a SID to a string, either numeric or username/group */ 107static void SidToString(fstring str, DOM_SID *sid) 108{ 109 char **domains = NULL; 110 char **names = NULL; 111 enum lsa_SidType *types = NULL; 112 113 sid_to_string(str, sid); 114 115 if (numeric) return; 116 117 /* Ask LSA to convert the sid to a name */ 118 119 if (!cacls_open_policy_hnd() || 120 !NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(global_pipe_hnd, global_hack_cli->mem_ctx, 121 &pol, 1, sid, &domains, 122 &names, &types)) || 123 !domains || !domains[0] || !names || !names[0]) { 124 return; 125 } 126 127 /* Converted OK */ 128 129 slprintf(str, sizeof(fstring) - 1, "%s%s%s", 130 domains[0], lp_winbind_separator(), 131 names[0]); 132 133} 134 135/* convert a string to a SID, either numeric or username/group */ 136static BOOL StringToSid(DOM_SID *sid, const char *str) 137{ 138 enum lsa_SidType *types = NULL; 139 DOM_SID *sids = NULL; 140 BOOL result = True; 141 142 if (strncmp(str, "S-", 2) == 0) { 143 return string_to_sid(sid, str); 144 } 145 146 if (!cacls_open_policy_hnd() || 147 !NT_STATUS_IS_OK(rpccli_lsa_lookup_names(global_pipe_hnd, global_hack_cli->mem_ctx, 148 &pol, 1, &str, NULL, &sids, 149 &types))) { 150 result = False; 151 goto done; 152 } 153 154 sid_copy(sid, &sids[0]); 155 done: 156 157 return result; 158} 159 160 161/* print an ACE on a FILE, using either numeric or ascii representation */ 162static void print_ace(FILE *f, SEC_ACE *ace) 163{ 164 const struct perm_value *v; 165 fstring sidstr; 166 int do_print = 0; 167 uint32 got_mask; 168 169 SidToString(sidstr, &ace->trustee); 170 171 fprintf(f, "%s:", sidstr); 172 173 if (numeric) { 174 fprintf(f, "%d/%d/0x%08x", 175 ace->type, ace->flags, ace->access_mask); 176 return; 177 } 178 179 /* Ace type */ 180 181 if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) { 182 fprintf(f, "ALLOWED"); 183 } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) { 184 fprintf(f, "DENIED"); 185 } else { 186 fprintf(f, "%d", ace->type); 187 } 188 189 /* Not sure what flags can be set in a file ACL */ 190 191 fprintf(f, "/%d/", ace->flags); 192 193 /* Standard permissions */ 194 195 for (v = standard_values; v->perm; v++) { 196 if (ace->access_mask == v->mask) { 197 fprintf(f, "%s", v->perm); 198 return; 199 } 200 } 201 202 /* Special permissions. Print out a hex value if we have 203 leftover bits in the mask. */ 204 205 got_mask = ace->access_mask; 206 207 again: 208 for (v = special_values; v->perm; v++) { 209 if ((ace->access_mask & v->mask) == v->mask) { 210 if (do_print) { 211 fprintf(f, "%s", v->perm); 212 } 213 got_mask &= ~v->mask; 214 } 215 } 216 217 if (!do_print) { 218 if (got_mask != 0) { 219 fprintf(f, "0x%08x", ace->access_mask); 220 } else { 221 do_print = 1; 222 goto again; 223 } 224 } 225} 226 227 228/* parse an ACE in the same format as print_ace() */ 229static BOOL parse_ace(SEC_ACE *ace, const char *orig_str) 230{ 231 char *p; 232 const char *cp; 233 fstring tok; 234 unsigned int atype = 0; 235 unsigned int aflags = 0; 236 unsigned int amask = 0; 237 DOM_SID sid; 238 SEC_ACCESS mask; 239 const struct perm_value *v; 240 char *str = SMB_STRDUP(orig_str); 241 242 if (!str) { 243 return False; 244 } 245 246 ZERO_STRUCTP(ace); 247 p = strchr_m(str,':'); 248 if (!p) { 249 printf("ACE '%s': missing ':'.\n", orig_str); 250 SAFE_FREE(str); 251 return False; 252 } 253 *p = '\0'; 254 p++; 255 /* Try to parse numeric form */ 256 257 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 && 258 StringToSid(&sid, str)) { 259 goto done; 260 } 261 262 /* Try to parse text form */ 263 264 if (!StringToSid(&sid, str)) { 265 printf("ACE '%s': failed to convert '%s' to SID\n", 266 orig_str, str); 267 SAFE_FREE(str); 268 return False; 269 } 270 271 cp = p; 272 if (!next_token(&cp, tok, "/", sizeof(fstring))) { 273 printf("ACE '%s': failed to find '/' character.\n", 274 orig_str); 275 SAFE_FREE(str); 276 return False; 277 } 278 279 if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { 280 atype = SEC_ACE_TYPE_ACCESS_ALLOWED; 281 } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) { 282 atype = SEC_ACE_TYPE_ACCESS_DENIED; 283 } else { 284 printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n", 285 orig_str, tok); 286 SAFE_FREE(str); 287 return False; 288 } 289 290 /* Only numeric form accepted for flags at present */ 291 292 if (!(next_token(&cp, tok, "/", sizeof(fstring)) && 293 sscanf(tok, "%i", &aflags))) { 294 printf("ACE '%s': bad integer flags entry at '%s'\n", 295 orig_str, tok); 296 SAFE_FREE(str); 297 return False; 298 } 299 300 if (!next_token(&cp, tok, "/", sizeof(fstring))) { 301 printf("ACE '%s': missing / at '%s'\n", 302 orig_str, tok); 303 SAFE_FREE(str); 304 return False; 305 } 306 307 if (strncmp(tok, "0x", 2) == 0) { 308 if (sscanf(tok, "%i", &amask) != 1) { 309 printf("ACE '%s': bad hex number at '%s'\n", 310 orig_str, tok); 311 SAFE_FREE(str); 312 return False; 313 } 314 goto done; 315 } 316 317 for (v = standard_values; v->perm; v++) { 318 if (strcmp(tok, v->perm) == 0) { 319 amask = v->mask; 320 goto done; 321 } 322 } 323 324 p = tok; 325 326 while(*p) { 327 BOOL found = False; 328 329 for (v = special_values; v->perm; v++) { 330 if (v->perm[0] == *p) { 331 amask |= v->mask; 332 found = True; 333 } 334 } 335 336 if (!found) { 337 printf("ACE '%s': bad permission value at '%s'\n", 338 orig_str, p); 339 SAFE_FREE(str); 340 return False; 341 } 342 p++; 343 } 344 345 if (*p) { 346 SAFE_FREE(str); 347 return False; 348 } 349 350 done: 351 mask = amask; 352 init_sec_ace(ace, &sid, atype, mask, aflags); 353 SAFE_FREE(str); 354 return True; 355} 356 357/* add an ACE to a list of ACEs in a SEC_ACL */ 358static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace) 359{ 360 SEC_ACL *new_ace; 361 SEC_ACE *aces; 362 if (! *the_acl) { 363 return (((*the_acl) = make_sec_acl(ctx, 3, 1, ace)) != NULL); 364 } 365 366 if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) { 367 return False; 368 } 369 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE)); 370 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE)); 371 new_ace = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces); 372 SAFE_FREE(aces); 373 (*the_acl) = new_ace; 374 return True; 375} 376 377/* parse a ascii version of a security descriptor */ 378static SEC_DESC *sec_desc_parse(char *str) 379{ 380 const char *p = str; 381 fstring tok; 382 SEC_DESC *ret = NULL; 383 size_t sd_size; 384 DOM_SID *group_sid=NULL, *owner_sid=NULL; 385 SEC_ACL *dacl=NULL; 386 int revision=1; 387 388 while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) { 389 390 if (strncmp(tok,"REVISION:", 9) == 0) { 391 revision = strtol(tok+9, NULL, 16); 392 continue; 393 } 394 395 if (strncmp(tok,"OWNER:", 6) == 0) { 396 if (owner_sid) { 397 printf("Only specify owner once\n"); 398 goto done; 399 } 400 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); 401 if (!owner_sid || 402 !StringToSid(owner_sid, tok+6)) { 403 printf("Failed to parse owner sid\n"); 404 goto done; 405 } 406 continue; 407 } 408 409 if (strncmp(tok,"GROUP:", 6) == 0) { 410 if (group_sid) { 411 printf("Only specify group once\n"); 412 goto done; 413 } 414 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); 415 if (!group_sid || 416 !StringToSid(group_sid, tok+6)) { 417 printf("Failed to parse group sid\n"); 418 goto done; 419 } 420 continue; 421 } 422 423 if (strncmp(tok,"ACL:", 4) == 0) { 424 SEC_ACE ace; 425 if (!parse_ace(&ace, tok+4)) { 426 goto done; 427 } 428 if(!add_ace(&dacl, &ace)) { 429 printf("Failed to add ACL %s\n", tok); 430 goto done; 431 } 432 continue; 433 } 434 435 printf("Failed to parse token '%s' in security descriptor,\n", tok); 436 goto done; 437 } 438 439 ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, group_sid, 440 NULL, dacl, &sd_size); 441 442 done: 443 SAFE_FREE(group_sid); 444 SAFE_FREE(owner_sid); 445 446 return ret; 447} 448 449 450/* print a ascii version of a security descriptor on a FILE handle */ 451static void sec_desc_print(FILE *f, SEC_DESC *sd) 452{ 453 fstring sidstr; 454 uint32 i; 455 456 fprintf(f, "REVISION:%d\n", sd->revision); 457 458 /* Print owner and group sid */ 459 460 if (sd->owner_sid) { 461 SidToString(sidstr, sd->owner_sid); 462 } else { 463 fstrcpy(sidstr, ""); 464 } 465 466 fprintf(f, "OWNER:%s\n", sidstr); 467 468 if (sd->group_sid) { 469 SidToString(sidstr, sd->group_sid); 470 } else { 471 fstrcpy(sidstr, ""); 472 } 473 474 fprintf(f, "GROUP:%s\n", sidstr); 475 476 /* Print aces */ 477 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { 478 SEC_ACE *ace = &sd->dacl->aces[i]; 479 fprintf(f, "ACL:"); 480 print_ace(f, ace); 481 fprintf(f, "\n"); 482 } 483 484} 485 486/***************************************************** 487dump the acls for a file 488*******************************************************/ 489static int cacl_dump(struct cli_state *cli, char *filename) 490{ 491 int result = EXIT_FAILED; 492 int fnum = -1; 493 SEC_DESC *sd; 494 495 if (test_args) 496 return EXIT_OK; 497 498 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); 499 500 if (fnum == -1) { 501 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 502 goto done; 503 } 504 505 sd = cli_query_secdesc(cli, fnum, ctx); 506 507 if (!sd) { 508 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli)); 509 goto done; 510 } 511 512 sec_desc_print(stdout, sd); 513 514 result = EXIT_OK; 515 516done: 517 if (fnum != -1) 518 cli_close(cli, fnum); 519 520 return result; 521} 522 523/***************************************************** 524Change the ownership or group ownership of a file. Just 525because the NT docs say this can't be done :-). JRA. 526*******************************************************/ 527 528static int owner_set(struct cli_state *cli, enum chown_mode change_mode, 529 char *filename, char *new_username) 530{ 531 int fnum; 532 DOM_SID sid; 533 SEC_DESC *sd, *old; 534 size_t sd_size; 535 536 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); 537 538 if (fnum == -1) { 539 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 540 return EXIT_FAILED; 541 } 542 543 if (!StringToSid(&sid, new_username)) 544 return EXIT_PARSE_ERROR; 545 546 old = cli_query_secdesc(cli, fnum, ctx); 547 548 cli_close(cli, fnum); 549 550 if (!old) { 551 printf("owner_set: Failed to query old descriptor\n"); 552 return EXIT_FAILED; 553 } 554 555 sd = make_sec_desc(ctx,old->revision, old->type, 556 (change_mode == REQUEST_CHOWN) ? &sid : NULL, 557 (change_mode == REQUEST_CHGRP) ? &sid : NULL, 558 NULL, NULL, &sd_size); 559 560 fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS); 561 562 if (fnum == -1) { 563 printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); 564 return EXIT_FAILED; 565 } 566 567 if (!cli_set_secdesc(cli, fnum, sd)) { 568 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); 569 } 570 571 cli_close(cli, fnum); 572 573 return EXIT_OK; 574} 575 576 577/* The MSDN is contradictory over the ordering of ACE entries in an ACL. 578 However NT4 gives a "The information may have been modified by a 579 computer running Windows NT 5.0" if denied ACEs do not appear before 580 allowed ACEs. */ 581 582static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2) 583{ 584 if (sec_ace_equal(ace1, ace2)) 585 return 0; 586 587 if (ace1->type != ace2->type) 588 return ace2->type - ace1->type; 589 590 if (sid_compare(&ace1->trustee, &ace2->trustee)) 591 return sid_compare(&ace1->trustee, &ace2->trustee); 592 593 if (ace1->flags != ace2->flags) 594 return ace1->flags - ace2->flags; 595 596 if (ace1->access_mask != ace2->access_mask) 597 return ace1->access_mask - ace2->access_mask; 598 599 if (ace1->size != ace2->size) 600 return ace1->size - ace2->size; 601 602 return memcmp(ace1, ace2, sizeof(SEC_ACE)); 603} 604 605static void sort_acl(SEC_ACL *the_acl) 606{ 607 uint32 i; 608 if (!the_acl) return; 609 610 qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare); 611 612 for (i=1;i<the_acl->num_aces;) { 613 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) { 614 int j; 615 for (j=i; j<the_acl->num_aces-1; j++) { 616 the_acl->aces[j] = the_acl->aces[j+1]; 617 } 618 the_acl->num_aces--; 619 } else { 620 i++; 621 } 622 } 623} 624 625/***************************************************** 626set the ACLs on a file given an ascii description 627*******************************************************/ 628static int cacl_set(struct cli_state *cli, char *filename, 629 char *the_acl, enum acl_mode mode) 630{ 631 int fnum; 632 SEC_DESC *sd, *old; 633 uint32 i, j; 634 size_t sd_size; 635 int result = EXIT_OK; 636 637 sd = sec_desc_parse(the_acl); 638 639 if (!sd) return EXIT_PARSE_ERROR; 640 if (test_args) return EXIT_OK; 641 642 /* The desired access below is the only one I could find that works 643 with NT4, W2KP and Samba */ 644 645 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); 646 647 if (fnum == -1) { 648 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); 649 return EXIT_FAILED; 650 } 651 652 old = cli_query_secdesc(cli, fnum, ctx); 653 654 if (!old) { 655 printf("calc_set: Failed to query old descriptor\n"); 656 return EXIT_FAILED; 657 } 658 659 cli_close(cli, fnum); 660 661 /* the logic here is rather more complex than I would like */ 662 switch (mode) { 663 case SMB_ACL_DELETE: 664 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 665 BOOL found = False; 666 667 for (j=0;old->dacl && j<old->dacl->num_aces;j++) { 668 if (sec_ace_equal(&sd->dacl->aces[i], 669 &old->dacl->aces[j])) { 670 uint32 k; 671 for (k=j; k<old->dacl->num_aces-1;k++) { 672 old->dacl->aces[k] = old->dacl->aces[k+1]; 673 } 674 old->dacl->num_aces--; 675 found = True; 676 break; 677 } 678 } 679 680 if (!found) { 681 printf("ACL for ACE:"); 682 print_ace(stdout, &sd->dacl->aces[i]); 683 printf(" not found\n"); 684 } 685 } 686 break; 687 688 case SMB_ACL_MODIFY: 689 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 690 BOOL found = False; 691 692 for (j=0;old->dacl && j<old->dacl->num_aces;j++) { 693 if (sid_equal(&sd->dacl->aces[i].trustee, 694 &old->dacl->aces[j].trustee)) { 695 old->dacl->aces[j] = sd->dacl->aces[i]; 696 found = True; 697 } 698 } 699 700 if (!found) { 701 fstring str; 702 703 SidToString(str, &sd->dacl->aces[i].trustee); 704 printf("ACL for SID %s not found\n", str); 705 } 706 } 707 708 if (sd->owner_sid) { 709 old->owner_sid = sd->owner_sid; 710 } 711 712 if (sd->group_sid) { 713 old->group_sid = sd->group_sid; 714 } 715 716 break; 717 718 case SMB_ACL_ADD: 719 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { 720 add_ace(&old->dacl, &sd->dacl->aces[i]); 721 } 722 break; 723 724 case SMB_ACL_SET: 725 old = sd; 726 break; 727 } 728 729 /* Denied ACE entries must come before allowed ones */ 730 sort_acl(old->dacl); 731 732 /* Create new security descriptor and set it */ 733#if 0 734 /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER. 735 But if we're sending an owner, even if it's the same as the one 736 that already exists then W2K3 insists we open with WRITE_OWNER access. 737 I need to check that setting a SD with no owner set works against WNT 738 and W2K. JRA. 739 */ 740 741 sd = make_sec_desc(ctx,old->revision, old->type, old->owner_sid, old->group_sid, 742 NULL, old->dacl, &sd_size); 743 744 fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS); 745#else 746 sd = make_sec_desc(ctx,old->revision, old->type, NULL, NULL, 747 NULL, old->dacl, &sd_size); 748 749 fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS); 750#endif 751 if (fnum == -1) { 752 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); 753 return EXIT_FAILED; 754 } 755 756 if (!cli_set_secdesc(cli, fnum, sd)) { 757 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); 758 result = EXIT_FAILED; 759 } 760 761 /* Clean up */ 762 763 cli_close(cli, fnum); 764 765 return result; 766} 767 768 769/***************************************************** 770return a connection to a server 771*******************************************************/ 772static struct cli_state *connect_one(const char *share) 773{ 774 struct cli_state *c; 775 struct in_addr ip; 776 NTSTATUS nt_status; 777 zero_ip(&ip); 778 779 if (!cmdline_auth_info.got_pass) { 780 char *pass = getpass("Password: "); 781 if (pass) { 782 pstrcpy(cmdline_auth_info.password, pass); 783 cmdline_auth_info.got_pass = True; 784 } 785 } 786 787 if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server, 788 &ip, 0, 789 share, "?????", 790 cmdline_auth_info.username, lp_workgroup(), 791 cmdline_auth_info.password, 0, 792 cmdline_auth_info.signing_state, NULL))) { 793 return c; 794 } else { 795 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); 796 return NULL; 797 } 798} 799 800/**************************************************************************** 801 main program 802****************************************************************************/ 803 int main(int argc, const char *argv[]) 804{ 805 char *share; 806 int opt; 807 enum acl_mode mode = SMB_ACL_SET; 808 static char *the_acl = NULL; 809 enum chown_mode change_mode = REQUEST_NONE; 810 int result; 811 fstring path; 812 pstring filename; 813 poptContext pc; 814 struct poptOption long_options[] = { 815 POPT_AUTOHELP 816 { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" }, 817 { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" }, 818 { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" }, 819 { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" }, 820 { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" }, 821 { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" }, 822 { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" }, 823 { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"}, 824 POPT_COMMON_SAMBA 825 POPT_COMMON_CREDENTIALS 826 { NULL } 827 }; 828 829 struct cli_state *cli; 830 831 load_case_tables(); 832 833 ctx=talloc_init("main"); 834 835 /* set default debug level to 1 regardless of what smb.conf sets */ 836 setup_logging( "smbcacls", True ); 837 DEBUGLEVEL_CLASS[DBGC_ALL] = 1; 838 dbf = x_stderr; 839 x_setbuf( x_stderr, NULL ); 840 841 setlinebuf(stdout); 842 843 lp_load(dyn_CONFIGFILE,True,False,False,True); 844 load_interfaces(); 845 846 pc = poptGetContext("smbcacls", argc, argv, long_options, 0); 847 848 poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: " 849 "'ACL:user:[ALLOWED|DENIED]/flags/permissions'"); 850 851 while ((opt = poptGetNextOpt(pc)) != -1) { 852 switch (opt) { 853 case 'S': 854 the_acl = smb_xstrdup(poptGetOptArg(pc)); 855 mode = SMB_ACL_SET; 856 break; 857 858 case 'D': 859 the_acl = smb_xstrdup(poptGetOptArg(pc)); 860 mode = SMB_ACL_DELETE; 861 break; 862 863 case 'M': 864 the_acl = smb_xstrdup(poptGetOptArg(pc)); 865 mode = SMB_ACL_MODIFY; 866 break; 867 868 case 'a': 869 the_acl = smb_xstrdup(poptGetOptArg(pc)); 870 mode = SMB_ACL_ADD; 871 break; 872 873 case 'C': 874 pstrcpy(owner_username,poptGetOptArg(pc)); 875 change_mode = REQUEST_CHOWN; 876 break; 877 878 case 'G': 879 pstrcpy(owner_username,poptGetOptArg(pc)); 880 change_mode = REQUEST_CHGRP; 881 break; 882 } 883 } 884 885 /* Make connection to server */ 886 if(!poptPeekArg(pc)) { 887 poptPrintUsage(pc, stderr, 0); 888 return -1; 889 } 890 891 fstrcpy(path, poptGetArg(pc)); 892 893 if(!poptPeekArg(pc)) { 894 poptPrintUsage(pc, stderr, 0); 895 return -1; 896 } 897 898 pstrcpy(filename, poptGetArg(pc)); 899 900 all_string_sub(path,"/","\\",0); 901 902 fstrcpy(server,path+2); 903 share = strchr_m(server,'\\'); 904 if (!share) { 905 share = strchr_m(server,'/'); 906 if (!share) { 907 printf("Invalid argument: %s\n", share); 908 return -1; 909 } 910 } 911 912 *share = 0; 913 share++; 914 915 if (!test_args) { 916 cli = connect_one(share); 917 if (!cli) { 918 talloc_destroy(ctx); 919 exit(EXIT_FAILED); 920 } 921 } else { 922 exit(0); 923 } 924 925 all_string_sub(filename, "/", "\\", 0); 926 if (filename[0] != '\\') { 927 pstring s; 928 s[0] = '\\'; 929 safe_strcpy(&s[1], filename, sizeof(pstring)-2); 930 pstrcpy(filename, s); 931 } 932 933 /* Perform requested action */ 934 935 if (change_mode != REQUEST_NONE) { 936 result = owner_set(cli, change_mode, filename, owner_username); 937 } else if (the_acl) { 938 result = cacl_set(cli, filename, the_acl, mode); 939 } else { 940 result = cacl_dump(cli, filename); 941 } 942 943 talloc_destroy(ctx); 944 945 return result; 946} 947