1/* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33#include <err.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <sys/types.h> 38#include <unistd.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <sys/stat.h> 42 43#include <membership.h> 44#include "chmod_acl.h" 45 46extern void usage(void); 47 48#ifdef __APPLE__ 49static struct { 50 acl_perm_t perm; 51 char *name; 52 int flags; 53#define ACL_PERM_DIR (1<<0) 54#define ACL_PERM_FILE (1<<1) 55} acl_perms[] = { 56 {ACL_READ_DATA, "read", ACL_PERM_FILE}, 57 {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, 58 {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, 59 {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, 60 {ACL_EXECUTE, "execute", ACL_PERM_FILE}, 61 {ACL_SEARCH, "search", ACL_PERM_DIR}, 62 {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, 63 {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, 64 {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, 65 {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, 66 {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, 67 {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, 68 {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, 69 {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, 70 {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, 71 {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, 72 {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, 73 {0, NULL, 0} 74}; 75 76static struct { 77 acl_flag_t flag; 78 char *name; 79 int flags; 80} acl_flags[] = { 81 {ACL_ENTRY_INHERITED, "inherited", ACL_PERM_FILE | ACL_PERM_DIR}, 82 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, 83 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, 84 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, 85 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, 86 {0, NULL, 0} 87}; 88 89/* TBD - Many of these routines could potentially be considered for 90 * inclusion in a library. If that is done, either avoid use of "err" 91 * and implement a better fall-through strategy in case of errors, 92 * or use err_set_exit() and make various structures globals. 93 */ 94 95#define NAME_USER (1) 96#define NAME_GROUP (2) 97#define NAME_EITHER (NAME_USER | NAME_GROUP) 98 99/* Perform a name to uuid mapping - calls through to memberd */ 100 101uuid_t * 102name_to_uuid(char *tok, int nametype) { 103 struct passwd *tpass = NULL; 104 struct group *tgrp = NULL; 105 uuid_t *entryg = NULL; 106 107 if ((entryg = (uuid_t *) calloc(1,sizeof(uuid_t))) == NULL) 108 err(1, "Unable to allocate a uuid"); 109 110 if (nametype & NAME_USER) 111 tpass = getpwnam(tok); 112 113 if (NULL == tpass && (nametype & NAME_GROUP)) 114 tgrp = getgrnam(tok); 115 116 if (tpass) { 117 if (0 != mbr_uid_to_uuid(tpass->pw_uid, *entryg)) { 118 errx(1, "mbr_uid_to_uuid(): Unable to translate uid %d", tpass->pw_uid); 119 } 120 } else if (tgrp) { 121 if (0 != mbr_gid_to_uuid(tgrp->gr_gid, *entryg)) { 122 errx(1, "mbr_gid_to_uuid(): Unable to translate gid %d", tgrp->gr_gid); 123 } 124 } else { 125 errx(1, "Unable to translate '%s' to a UID/GID", tok); 126 } 127 return entryg; 128} 129 130/* Convert an acl entry in string form to an acl_entry_t */ 131int 132parse_entry(char *entrybuf, acl_entry_t newent) { 133 char *tok; 134 char *pebuf; 135 uuid_t *entryg; 136 137 acl_tag_t tag; 138 acl_permset_t perms; 139 acl_flagset_t flags; 140 unsigned permcount = 0; 141 unsigned pindex = 0; 142 char *delimiter = " "; 143 int nametype = NAME_EITHER; 144 145 acl_get_permset(newent, &perms); 146 acl_get_flagset_np(newent, &flags); 147 148 pebuf = entrybuf; 149 150 if (0 == strncmp(entrybuf, "user:", 5)) { 151 nametype = NAME_USER; 152 pebuf += 5; 153 } else if (0 == strncmp(entrybuf, "group:", 6)) { 154 nametype = NAME_GROUP; 155 pebuf += 6; 156 } 157 158 if (strchr(pebuf, ':')) /* User/Group names can have spaces */ 159 delimiter = ":"; 160 tok = strsep(&pebuf, delimiter); 161 162 if ((tok == NULL) || *tok == '\0') { 163 errx(1, "Invalid entry format -- expected user or group name"); 164 } 165 166 /* parse the name into a qualifier */ 167 entryg = name_to_uuid(tok, nametype); 168 169 tok = strsep(&pebuf, ": "); /* Stick with delimiter? */ 170 if ((tok == NULL) || *tok == '\0') { 171 errx(1, "Invalid entry format -- expected allow or deny"); 172 } 173 174 /* is the verb 'allow' or 'deny'? */ 175 if (!strcmp(tok, "allow")) { 176 tag = ACL_EXTENDED_ALLOW; 177 } else if (!strcmp(tok, "deny")) { 178 tag = ACL_EXTENDED_DENY; 179 } else { 180 errx(1, "Unknown tag type '%s'", tok); 181 } 182 183 /* parse permissions */ 184 for (; (tok = strsep(&pebuf, ",")) != NULL;) { 185 if (*tok != '\0') { 186 /* is it a permission? */ 187 for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) { 188 if (!strcmp(acl_perms[pindex].name, tok)) { 189 /* got one */ 190 acl_add_perm(perms, acl_perms[pindex].perm); 191 permcount++; 192 goto found; 193 } 194 } 195 /* is it a flag? */ 196 for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) { 197 if (!strcmp(acl_flags[pindex].name, tok)) { 198 /* got one */ 199 acl_add_flag_np(flags, acl_flags[pindex].flag); 200 permcount++; 201 goto found; 202 } 203 } 204 errx(1,"Invalid permission type '%s'", tok); 205 found: 206 continue; 207 } 208 } 209 if (0 == permcount) { 210 errx(1, "No permissions specified"); 211 } 212 acl_set_tag_type(newent, tag); 213 acl_set_qualifier(newent, entryg); 214 acl_set_permset(newent, perms); 215 acl_set_flagset_np(newent, flags); 216 free(entryg); 217 218 return(0); 219} 220 221/* Convert one or more acl entries in string form to an acl_t */ 222acl_t 223parse_acl_entries(const char *input) { 224 acl_t acl_input; 225 acl_entry_t newent; 226 char *inbuf; 227 char *oinbuf; 228 229 char **bufp, *entryv[ACL_MAX_ENTRIES]; 230#if 0 231/* XXX acl_from_text(), when implemented, will presumably use the canonical 232 * text representation format, which is what chmod should be using 233 * We may need to add an entry number to the input 234 */ 235 /* Translate the user supplied ACL entry */ 236 /* acl_input = acl_from_text(input); */ 237#else 238 inbuf = malloc(MAX_ACL_TEXT_SIZE); 239 240 if (inbuf == NULL) 241 err(1, "malloc() failed"); 242 strncpy(inbuf, input, MAX_ACL_TEXT_SIZE); 243 inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0'; 244 245 if ((acl_input = acl_init(1)) == NULL) 246 err(1, "acl_init() failed"); 247 248 oinbuf = inbuf; 249 250 for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;) 251 if (**bufp != '\0') { 252 if (0 != acl_create_entry(&acl_input, &newent)) 253 err(1, "acl_create_entry() failed"); 254 if (0 != parse_entry(*bufp, newent)) { 255 errx(1, "Failed parsing entry '%s'", *bufp); 256 } 257 if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) { 258 errx(1, "Too many entries"); 259 } 260 } 261 262 free(inbuf); 263 return acl_input; 264#endif /* #if 0 */ 265} 266 267/* XXX No Libc support for inherited entries and generation determination yet */ 268unsigned 269get_inheritance_level(acl_entry_t entry) { 270/* XXX to be implemented */ 271 return 1; 272} 273 274/* Determine a "score" for an acl entry. The entry scores higher if it's 275 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher 276 * than inherited entries. 277 */ 278 279int 280score_acl_entry(acl_entry_t entry) { 281 282 acl_tag_t tag; 283 acl_flagset_t flags; 284 acl_permset_t perms; 285 286 int score = 0; 287 288 if (entry == NULL) 289 return (MINIMUM_TIER); 290 291 if (acl_get_tag_type(entry, &tag) != 0) { 292 err(1, "Malformed ACL entry, no tag present"); 293 } 294 if (acl_get_flagset_np(entry, &flags) != 0){ 295 err(1, "Unable to obtain flagset"); 296 } 297 if (acl_get_permset(entry, &perms) != 0) 298 err(1, "Malformed ACL entry, no permset present"); 299 300 switch(tag) { 301 case ACL_EXTENDED_ALLOW: 302 break; 303 case ACL_EXTENDED_DENY: 304 score++; 305 break; 306 default: 307 errx(1, "Unknown tag type %d present in ACL entry", tag); 308 /* NOTREACHED */ 309 } 310 311 if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) 312 score += get_inheritance_level(entry) * INHERITANCE_TIER; 313 314 return score; 315} 316 317int 318compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) { 319 return bcmp(qa, qb, sizeof(uuid_t)); 320} 321 322/* Compare two ACL permsets. 323 * Returns : 324 * MATCH_SUBSET if bperms is a subset of aperms 325 * MATCH_SUPERSET if bperms is a superset of aperms 326 * MATCH_PARTIAL if the two permsets have a common subset 327 * MATCH_EXACT if the two permsets are identical 328 * MATCH_NONE if they are disjoint 329 */ 330 331int 332compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms) 333{ 334 int i; 335/* TBD Implement other match levels as needed */ 336 for (i = 0; acl_perms[i].name != NULL; i++) { 337 if (acl_get_perm_np(aperms, acl_perms[i].perm) != 338 acl_get_perm_np(bperms, acl_perms[i].perm)) 339 return MATCH_NONE; 340 } 341 return MATCH_EXACT; 342} 343 344static int 345compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags) 346{ 347 int i; 348/* TBD Implement other match levels as needed */ 349 for (i = 0; acl_flags[i].name != NULL; i++) { 350 if (acl_get_flag_np(aflags, acl_flags[i].flag) != 351 acl_get_flag_np(bflags, acl_flags[i].flag)) 352 return MATCH_NONE; 353 } 354 return MATCH_EXACT; 355} 356 357/* Compares two ACL entries for equality */ 358int 359compare_acl_entries(acl_entry_t a, acl_entry_t b) 360{ 361 acl_tag_t atag, btag; 362 acl_permset_t aperms, bperms; 363 acl_flagset_t aflags, bflags; 364 int pcmp = 0, fcmp = 0; 365 void *aqual, *bqual; 366 367 aqual = acl_get_qualifier(a); 368 bqual = acl_get_qualifier(b); 369 370 int compare = compare_acl_qualifiers(aqual, bqual); 371 acl_free(aqual); 372 acl_free(bqual); 373 374 if (compare != 0) 375 return MATCH_NONE; 376 377 if (0 != acl_get_tag_type(a, &atag)) 378 err(1, "No tag type present in entry"); 379 if (0!= acl_get_tag_type(b, &btag)) 380 err(1, "No tag type present in entry"); 381 382 if (atag != btag) 383 return MATCH_NONE; 384 385 if ((acl_get_permset(a, &aperms) != 0) || 386 (acl_get_flagset_np(a, &aflags) != 0) || 387 (acl_get_permset(b, &bperms) != 0) || 388 (acl_get_flagset_np(b, &bflags) != 0)) 389 err(1, "error fetching permissions"); 390 391 pcmp = compare_acl_permsets(aperms, bperms); 392 fcmp = compare_acl_flagsets(aflags, bflags); 393 394 if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE)) 395 return(MATCH_PARTIAL); 396 else 397 return(MATCH_EXACT); 398} 399 400/* Verify that an ACL is in canonical order. Currently, the canonical 401 * form is: 402 * local deny 403 * local allow 404 * inherited deny (parent) 405 * inherited allow (parent) 406 * inherited deny (grandparent) 407 * inherited allow (grandparent) 408 * ... 409 */ 410unsigned int 411is_canonical(acl_t acl) { 412 413 unsigned aindex; 414 acl_entry_t entry; 415 int score = 0, next_score = 0; 416 417/* XXX - is a zero entry ACL in canonical form? */ 418 if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) 419 return 1; 420 421 score = score_acl_entry(entry); 422 423 for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0; 424 aindex++) { 425 if (score < (next_score = score_acl_entry(entry))) 426 return 0; 427 score = next_score; 428 } 429 return 1; 430} 431 432 433/* Iterate through an ACL, and find the canonical position for the 434 * specified entry 435 */ 436unsigned int 437find_canonical_position(acl_t acl, acl_entry_t modifier) { 438 439 acl_entry_t entry; 440 int mscore = 0; 441 unsigned mpos = 0; 442 443 /* Check if there's an entry with the same qualifier 444 * and tag type; if not, find the appropriate slot 445 * for the score. 446 */ 447 448 if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) 449 return 0; 450 451 mscore = score_acl_entry(modifier); 452 453 while (mscore < score_acl_entry(entry)) { 454 455 mpos++; 456 457 if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) 458 break; 459 460 } 461 return mpos; 462} 463 464int canonicalize_acl_entries(acl_t acl); 465 466/* For a given acl_entry_t "modifier", find the first exact or 467 * partially matching entry from the specified acl_t acl 468 */ 469 470int 471find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp, 472 unsigned match_inherited) { 473 474 acl_entry_t entry = NULL; 475 476 unsigned aindex; 477 int cmp, fcmp = MATCH_NONE; 478 479 for (aindex = 0; 480 acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : 481 ACL_NEXT_ENTRY, &entry) == 0; 482 aindex++) { 483 cmp = compare_acl_entries(entry, modifier); 484 if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) { 485 if (match_inherited) { 486 acl_flagset_t eflags, mflags; 487 488 if (0 != acl_get_flagset_np(modifier, &mflags)) 489 err(1, "Unable to get flagset"); 490 491 if (0 != acl_get_flagset_np(entry, &eflags)) 492 err(1, "Unable to get flagset"); 493 494 if (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) { 495 *rentryp = entry; 496 fcmp = cmp; 497 } 498 } 499 else { 500 *rentryp = entry; 501 fcmp = cmp; 502 } 503 } 504 if (fcmp == MATCH_EXACT) 505 break; 506 } 507 return fcmp; 508} 509 510/* Remove all perms specified in modifier from rentry*/ 511int 512subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int* valid_perms) 513{ 514 acl_permset_t rperms, mperms; 515 acl_flagset_t rflags, mflags; 516 if (valid_perms) 517 *valid_perms = 0; 518 int i; 519 520 if ((acl_get_permset(rentry, &rperms) != 0) || 521 (acl_get_flagset_np(rentry, &rflags) != 0) || 522 (acl_get_permset(modifier, &mperms) != 0) || 523 (acl_get_flagset_np(modifier, &mflags) != 0)) 524 err(1, "error computing ACL modification"); 525 526 for (i = 0; acl_perms[i].name != NULL; i++) { 527 if (acl_get_perm_np(mperms, acl_perms[i].perm)) 528 acl_delete_perm(rperms, acl_perms[i].perm); 529 else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm)) 530 (*valid_perms)++; 531 } 532 for (i = 0; acl_flags[i].name != NULL; i++) { 533 if (acl_get_flag_np(mflags, acl_flags[i].flag)) 534 acl_delete_flag_np(rflags, acl_flags[i].flag); 535 } 536 acl_set_permset(rentry, rperms); 537 acl_set_flagset_np(rentry, rflags); 538 return 0; 539} 540/* Add the perms specified in modifier to rentry */ 541static int 542merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier) 543{ 544 acl_permset_t rperms, mperms; 545 acl_flagset_t rflags, mflags; 546 int i; 547 548 if ((acl_get_permset(rentry, &rperms) != 0) || 549 (acl_get_flagset_np(rentry, &rflags) != 0) || 550 (acl_get_permset(modifier, &mperms) != 0) || 551 (acl_get_flagset_np(modifier, &mflags) != 0)) 552 err(1, "error computing ACL modification"); 553 554 for (i = 0; acl_perms[i].name != NULL; i++) { 555 if (acl_get_perm_np(mperms, acl_perms[i].perm)) 556 acl_add_perm(rperms, acl_perms[i].perm); 557 } 558 for (i = 0; acl_flags[i].name != NULL; i++) { 559 if (acl_get_flag_np(mflags, acl_flags[i].flag)) 560 acl_add_flag_np(rflags, acl_flags[i].flag); 561 } 562 acl_set_permset(rentry, rperms); 563 acl_set_flagset_np(rentry, rflags); 564 return 0; 565} 566 567int 568modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, 569 int position, int inheritance_level, 570 unsigned flag_new_acl, const char* path) { 571 572 unsigned cpos = 0; 573 acl_entry_t newent = NULL; 574 int dmatch = 0; 575 acl_entry_t rentry = NULL; 576 unsigned retval = 0; 577 acl_t oacl = *oaclp; 578 579/* Add the inherited flag if requested by the user*/ 580 if (modifier && (optflags & ACL_INHERIT_FLAG)) { 581 acl_flagset_t mflags; 582 583 acl_get_flagset_np(modifier, &mflags); 584 acl_add_flag_np(mflags, ACL_ENTRY_INHERITED); 585 acl_set_flagset_np(modifier, mflags); 586 } 587 588 if (optflags & ACL_SET_FLAG) { 589 if (position != -1) { 590 if (0 != acl_create_entry_np(&oacl, &newent, position)) 591 err(1, "acl_create_entry() failed"); 592 acl_copy_entry(newent, modifier); 593 } else { 594/* If an entry exists, add the new permissions to it, else add an 595 * entry in the canonical position. 596 */ 597 598/* First, check for a matching entry - if one exists, merge flags */ 599 dmatch = find_matching_entry(oacl, modifier, &rentry, 1); 600 601 if (dmatch != MATCH_NONE) { 602 if (dmatch == MATCH_EXACT) 603/* Nothing to be done */ 604 goto ma_exit; 605 606 if (dmatch == MATCH_PARTIAL) { 607 merge_entry_perms(rentry, modifier); 608 goto ma_exit; 609 } 610 } 611/* Insert the entry in canonical order */ 612 cpos = find_canonical_position(oacl, modifier); 613 if (0!= acl_create_entry_np(&oacl, &newent, cpos)) 614 err(1, "acl_create_entry() failed"); 615 acl_copy_entry(newent, modifier); 616 } 617 } else if (optflags & ACL_DELETE_FLAG) { 618 if (flag_new_acl) { 619 warnx("No ACL present '%s'", path); 620 retval = 1; 621 } else if (position != -1 ) { 622 if (0 != acl_get_entry(oacl, position, &rentry)) { 623 warnx("Invalid entry number '%s'", path); 624 retval = 1; 625 } else { 626 acl_delete_entry(oacl, rentry); 627 } 628 } else { 629 unsigned match_found = 0, aindex; 630 for (aindex = 0; 631 acl_get_entry(oacl, rentry == NULL ? 632 ACL_FIRST_ENTRY : 633 ACL_NEXT_ENTRY, &rentry) == 0; 634 aindex++) { 635 unsigned cmp; 636 cmp = compare_acl_entries(rentry, modifier); 637 if ((cmp == MATCH_EXACT) || 638 (cmp == MATCH_PARTIAL)) { 639 match_found++; 640 if (cmp == MATCH_EXACT) 641 acl_delete_entry(oacl, rentry); 642 else { 643 int valid_perms; 644/* In the event of a partial match, remove the specified perms from the 645 * entry */ 646 subtract_from_entry(rentry, modifier, &valid_perms); 647 /* if no perms survived then delete the entry */ 648 if (valid_perms == 0) 649 acl_delete_entry(oacl, rentry); 650 } 651 } 652 } 653 if (0 == match_found) { 654 warnx("Entry not found when attempting delete '%s'",path); 655 retval = 1; 656 } 657 } 658 } else if (optflags & ACL_REWRITE_FLAG) { 659 acl_entry_t rentry; 660 661 if (-1 == position) { 662 usage(); 663 } 664 if (0 == flag_new_acl) { 665 if (0 != acl_get_entry(oacl, position, 666 &rentry)) 667 err(1, "Invalid entry number '%s'", path); 668 669 if (0 != acl_delete_entry(oacl, rentry)) 670 err(1, "Unable to delete entry '%s'", path); 671 } 672 if (0!= acl_create_entry_np(&oacl, &newent, position)) 673 err(1, "acl_create_entry() failed"); 674 acl_copy_entry(newent, modifier); 675 } 676ma_exit: 677 *oaclp = oacl; 678 return retval; 679} 680 681int 682modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow) { 683 684 acl_t oacl = NULL; 685 unsigned aindex = 0, flag_new_acl = 0; 686 acl_entry_t newent = NULL; 687 acl_entry_t entry = NULL; 688 unsigned retval = 0 ; 689 690 extern int fflag; 691 692/* XXX acl_get_file() returns a zero entry ACL if an ACL was previously 693 * associated with the file, and has had its entries removed. 694 * However, POSIX 1003.1e states that a zero entry ACL should be 695 * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is 696 * associated with the path; it 697 * does not specifically state that a request for ACL_TYPE_EXTENDED 698 * should not return a zero entry ACL, however. 699 */ 700 701/* Determine if we've been given a zero entry ACL, or create an ACL if 702 * none exists. There are some issues to consider here: Should we create 703 * a zero-entry ACL for a delete or check canonicity operation? 704 */ 705 706 if (path == NULL) 707 usage(); 708 709 if (optflags & ACL_CLEAR_FLAG) { 710 filesec_t fsec = filesec_init(); 711 if (fsec == NULL) 712 err(1, "filesec_init() failed"); 713 if (filesec_set_property(fsec, FILESEC_ACL, 714 _FILESEC_REMOVE_ACL) != 0) 715 err(1, "filesec_set_property() failed"); 716 if (chmodx_np(path, fsec) != 0) { 717 if (!fflag) 718 warn("Failed to clear ACL on file %s", path); 719 retval = 1; 720 } else 721 retval = 0; 722 filesec_free(fsec); 723 return (retval); 724 } 725 726 if (optflags & ACL_FROM_STDIN) { 727 oacl = acl_dup(modifier); 728 } else { 729 if (follow) { 730 oacl = acl_get_file(path, ACL_TYPE_EXTENDED); 731 } else { 732 int fd = open(path, O_SYMLINK); 733 if (fd != -1) { 734 oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED); 735 close(fd); 736 } 737 } 738 if ((oacl == NULL) || 739 (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) { 740 if ((oacl = acl_init(1)) == NULL) 741 err(1, "acl_init() failed"); 742 flag_new_acl = 1; 743 position = 0; 744 } 745 746 if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG | 747 ACL_REMOVE_INHERITED_ENTRIES))) { 748 acl_t facl = NULL; 749 if ((facl = acl_init(1)) == NULL) 750 err(1, "acl_init() failed"); 751 for (aindex = 0; 752 acl_get_entry(oacl, 753 (entry == NULL ? ACL_FIRST_ENTRY : 754 ACL_NEXT_ENTRY), &entry) == 0; 755 aindex++) { 756 acl_flagset_t eflags; 757 acl_entry_t fent = NULL; 758 if (acl_get_flagset_np(entry, &eflags) != 0) { 759 err(1, "Unable to obtain flagset"); 760 } 761 762 if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) { 763 if (optflags & ACL_REMOVE_INHERIT_FLAG) { 764 acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED); 765 acl_set_flagset_np(entry, eflags); 766 acl_create_entry(&facl, &fent); 767 acl_copy_entry(fent, entry); 768 } 769 } 770 else { 771 acl_create_entry(&facl, &fent); 772 acl_copy_entry(fent, entry); 773 } 774 } 775 if (oacl) 776 acl_free(oacl); 777 oacl = facl; 778 } else if (optflags & ACL_TO_STDOUT) { 779 ssize_t len; /* need to get printacl() from ls(1) */ 780 char *text = acl_to_text(oacl, &len); 781 puts(text); 782 acl_free(text); 783 } else if (optflags & ACL_CHECK_CANONICITY) { 784 if (flag_new_acl) { 785 warnx("No ACL currently associated with file '%s'", path); 786 } 787 retval = is_canonical(oacl); 788 } else if ((optflags & ACL_SET_FLAG) && (position == -1) && 789 (!is_canonical(oacl))) { 790 warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path); 791 retval = 1; 792 } else if (((optflags & ACL_DELETE_FLAG) && (position != -1)) 793 || (optflags & ACL_CHECK_CANONICITY)) { 794 retval = modify_acl(&oacl, NULL, optflags, position, 795 inheritance_level, flag_new_acl, path); 796 } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) { 797 warnx("No ACL currently associated with file '%s'", path); 798 retval = 1; 799 } else { 800 if (!modifier) { /* avoid bus error in acl_get_entry */ 801 errx(1, "Internal error: modifier should not be NULL"); 802 } 803 for (aindex = 0; 804 acl_get_entry(modifier, 805 (entry == NULL ? ACL_FIRST_ENTRY : 806 ACL_NEXT_ENTRY), &entry) == 0; 807 aindex++) { 808 809 retval += modify_acl(&oacl, entry, optflags, 810 position, inheritance_level, 811 flag_new_acl, path); 812 } 813 } 814 } 815 816/* XXX Potential race here, since someone else could've modified or 817 * read the ACL on this file (with the intention of modifying it) in 818 * the interval from acl_get_file() to acl_set_file(); we can 819 * minimize one aspect of this window by comparing the original acl 820 * to a fresh one from acl_get_file() but we could consider a 821 * "changeset" mechanism, common locking strategy, or kernel 822 * supplied reservation mechanism to prevent this race. 823 */ 824 if (!(optflags & (ACL_TO_STDOUT|ACL_CHECK_CANONICITY))) { 825 int status = -1; 826 if (follow) { 827 status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl); 828 } else { 829 int fd = open(path, O_SYMLINK); 830 if (fd != -1) { 831 status = acl_set_fd_np(fd, oacl, 832 ACL_TYPE_EXTENDED); 833 close(fd); 834 } 835 } 836 if (status != 0) { 837 if (!fflag) 838 warn("Failed to set ACL on file '%s'", path); 839 retval = 1; 840 } 841 } 842 843 if (oacl) 844 acl_free(oacl); 845 846 return retval; 847} 848 849#endif /*__APPLE__*/ 850