1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <limits.h> 33 #include <grp.h> 34 #include <pwd.h> 35 #include <sys/types.h> 36 #include <sys/acl.h> 37 #include <errno.h> 38 #include <sys/stat.h> 39 #include <locale.h> 40 #include <aclutils.h> 41 #include <acl_common.h> 42 43 #define ACL_PATH 0 44 #define ACL_FD 1 45 46 #define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ 47 ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ 48 ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) 49 50 51 #define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 52 #define ACL_SYNCHRONIZE_SET_DENY 0x0000001 53 54 #define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 55 #define ACL_WRITE_OWNER_SET_DENY 0x0000010 56 57 #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 58 #define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 59 60 #define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 61 62 #define ACL_DELETE_SET_ALLOW 0x0000200 63 #define ACL_DELETE_SET_DENY 0x0000100 64 65 #define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 66 67 #define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 68 #define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 69 70 #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 71 #define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 72 73 #define ACL_WRITE_OWNER_ERR_DENY 0x0000040 74 #define ACL_READ_NAMED_READER_SET_DENY 0x1000000 75 #define ACL_WRITE_NAMED_WRITER_SET_ALLO W0x0200000 76 typedef union { 77 const char *file; 78 int fd; 79 } acl_inp; 80 81 acl_t * 82 acl_alloc(enum acl_type type) 83 { 84 acl_t *aclp; 85 86 aclp = malloc(sizeof (acl_t)); 87 88 if (aclp == NULL) 89 return (NULL); 90 91 aclp->acl_aclp = NULL; 92 aclp->acl_cnt = 0; 93 94 switch (type) { 95 case ACE_T: 96 aclp->acl_type = ACE_T; 97 aclp->acl_entry_size = sizeof (ace_t); 98 break; 99 case ACLENT_T: 100 aclp->acl_type = ACLENT_T; 101 aclp->acl_entry_size = sizeof (aclent_t); 102 break; 103 default: 104 acl_free(aclp); 105 aclp = NULL; 106 } 107 return (aclp); 108 } 109 110 /* 111 * Free acl_t structure 112 */ 113 void 114 acl_free(acl_t *aclp) 115 { 116 if (aclp == NULL) 117 return; 118 119 if (aclp->acl_aclp) 120 free(aclp->acl_aclp); 121 free(aclp); 122 } 123 124 /* 125 * Determine whether a file has a trivial ACL 126 * returns: 0 = trivial 127 * 1 = nontrivial 128 * <0 some other system failure, such as ENOENT or EPERM 129 */ 130 int 131 acl_trivial(const char *filename) 132 { 133 int acl_flavor; 134 int aclcnt; 135 int cntcmd; 136 int val = 0; 137 ace_t *acep; 138 139 acl_flavor = pathconf(filename, _PC_ACL_ENABLED); 140 if (acl_flavor == -1) 141 return (-1); 142 143 if (acl_flavor == _ACL_ACE_ENABLED) 144 cntcmd = ACE_GETACLCNT; 145 else 146 cntcmd = GETACLCNT; 147 148 aclcnt = acl(filename, cntcmd, 0, NULL); 149 if (aclcnt > 0) { 150 if (acl_flavor == _ACL_ACE_ENABLED) { 151 if (aclcnt != 6) 152 val = 1; 153 else { 154 acep = malloc(sizeof (ace_t) * aclcnt); 155 if (acep == NULL) 156 return (-1); 157 if (acl(filename, ACE_GETACL, 158 aclcnt, acep) < 0) { 159 free(acep); 160 return (-1); 161 } 162 163 val = ace_trivial(acep, aclcnt); 164 free(acep); 165 } 166 } else if (aclcnt > MIN_ACL_ENTRIES) 167 val = 1; 168 } 169 return (val); 170 } 171 172 static uint32_t 173 access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow) 174 { 175 uint32_t access_mask = 0; 176 int acl_produce; 177 int synchronize_set = 0, write_owner_set = 0; 178 int delete_set = 0, write_attrs_set = 0; 179 int read_named_set = 0, write_named_set = 0; 180 181 acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW | 182 ACL_WRITE_ATTRS_OWNER_SET_ALLOW | 183 ACL_WRITE_ATTRS_WRITER_SET_DENY); 184 185 if (isallow) { 186 synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW; 187 write_owner_set = ACL_WRITE_OWNER_SET_ALLOW; 188 delete_set = ACL_DELETE_SET_ALLOW; 189 if (hasreadperm) 190 read_named_set = ACL_READ_NAMED_READER_SET_ALLOW; 191 if (haswriteperm) 192 write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW; 193 if (isowner) 194 write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; 195 else if (haswriteperm) 196 write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; 197 } else { 198 199 synchronize_set = ACL_SYNCHRONIZE_SET_DENY; 200 write_owner_set = ACL_WRITE_OWNER_SET_DENY; 201 delete_set = ACL_DELETE_SET_DENY; 202 if (hasreadperm) 203 read_named_set = ACL_READ_NAMED_READER_SET_DENY; 204 if (haswriteperm) 205 write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY; 206 if (isowner) 207 write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY; 208 else if (haswriteperm) 209 write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY; 210 else 211 /* 212 * If the entity is not the owner and does not 213 * have write permissions ACE_WRITE_ATTRIBUTES will 214 * always go in the DENY ACE. 215 */ 216 access_mask |= ACE_WRITE_ATTRIBUTES; 217 } 218 219 if (acl_produce & synchronize_set) 220 access_mask |= ACE_SYNCHRONIZE; 221 if (acl_produce & write_owner_set) 222 access_mask |= ACE_WRITE_OWNER; 223 if (acl_produce & delete_set) 224 access_mask |= ACE_DELETE; 225 if (acl_produce & write_attrs_set) 226 access_mask |= ACE_WRITE_ATTRIBUTES; 227 if (acl_produce & read_named_set) 228 access_mask |= ACE_READ_NAMED_ATTRS; 229 if (acl_produce & write_named_set) 230 access_mask |= ACE_WRITE_NAMED_ATTRS; 231 232 return (access_mask); 233 } 234 235 /* 236 * Given an mode_t, convert it into an access_mask as used 237 * by nfsace, assuming aclent_t -> nfsace semantics. 238 */ 239 static uint32_t 240 mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow) 241 { 242 uint32_t access = 0; 243 int haswriteperm = 0; 244 int hasreadperm = 0; 245 246 if (isallow) { 247 haswriteperm = (mode & 02); 248 hasreadperm = (mode & 04); 249 } else { 250 haswriteperm = !(mode & 02); 251 hasreadperm = !(mode & 04); 252 } 253 254 /* 255 * The following call takes care of correctly setting the following 256 * mask bits in the access_mask: 257 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE, 258 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS 259 */ 260 access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow); 261 262 if (isallow) { 263 access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES; 264 if (isowner) 265 access |= ACE_WRITE_ACL; 266 } else { 267 if (! isowner) 268 access |= ACE_WRITE_ACL; 269 } 270 271 /* read */ 272 if (mode & 04) { 273 access |= ACE_READ_DATA; 274 } 275 /* write */ 276 if (mode & 02) { 277 access |= ACE_WRITE_DATA | 278 ACE_APPEND_DATA; 279 if (isdir) 280 access |= ACE_DELETE_CHILD; 281 } 282 /* exec */ 283 if (mode & 01) { 284 access |= ACE_EXECUTE; 285 } 286 287 return (access); 288 } 289 290 /* 291 * Given an nfsace (presumably an ALLOW entry), make a 292 * corresponding DENY entry at the address given. 293 */ 294 static void 295 ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner) 296 { 297 (void) memcpy(deny, allow, sizeof (ace_t)); 298 299 deny->a_who = allow->a_who; 300 301 deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 302 deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS; 303 if (isdir) 304 deny->a_access_mask ^= ACE_DELETE_CHILD; 305 306 deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER | 307 ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | 308 ACE_WRITE_NAMED_ATTRS); 309 deny->a_access_mask |= access_mask_set((allow->a_access_mask & 310 ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner, 311 B_FALSE); 312 } 313 /* 314 * Make an initial pass over an array of aclent_t's. Gather 315 * information such as an ACL_MASK (if any), number of users, 316 * number of groups, and whether the array needs to be sorted. 317 */ 318 static int 319 ln_aent_preprocess(aclent_t *aclent, int n, 320 int *hasmask, mode_t *mask, 321 int *numuser, int *numgroup, int *needsort) 322 { 323 int error = 0; 324 int i; 325 int curtype = 0; 326 327 *hasmask = 0; 328 *mask = 07; 329 *needsort = 0; 330 *numuser = 0; 331 *numgroup = 0; 332 333 for (i = 0; i < n; i++) { 334 if (aclent[i].a_type < curtype) 335 *needsort = 1; 336 else if (aclent[i].a_type > curtype) 337 curtype = aclent[i].a_type; 338 if (aclent[i].a_type & USER) 339 (*numuser)++; 340 if (aclent[i].a_type & (GROUP | GROUP_OBJ)) 341 (*numgroup)++; 342 if (aclent[i].a_type & CLASS_OBJ) { 343 if (*hasmask) { 344 error = EINVAL; 345 goto out; 346 } else { 347 *hasmask = 1; 348 *mask = aclent[i].a_perm; 349 } 350 } 351 } 352 353 if ((! *hasmask) && (*numuser + *numgroup > 1)) { 354 error = EINVAL; 355 goto out; 356 } 357 358 out: 359 return (error); 360 } 361 362 /* 363 * Convert an array of aclent_t into an array of nfsace entries, 364 * following POSIX draft -> nfsv4 conversion semantics as outlined in 365 * the IETF draft. 366 */ 367 static int 368 ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir) 369 { 370 int error = 0; 371 mode_t mask; 372 int numuser, numgroup, needsort; 373 int resultsize = 0; 374 int i, groupi = 0, skip; 375 ace_t *acep, *result = NULL; 376 int hasmask; 377 378 error = ln_aent_preprocess(aclent, n, &hasmask, &mask, 379 &numuser, &numgroup, &needsort); 380 if (error != 0) 381 goto out; 382 383 /* allow + deny for each aclent */ 384 resultsize = n * 2; 385 if (hasmask) { 386 /* 387 * stick extra deny on the group_obj and on each 388 * user|group for the mask (the group_obj was added 389 * into the count for numgroup) 390 */ 391 resultsize += numuser + numgroup; 392 /* ... and don't count the mask itself */ 393 resultsize -= 2; 394 } 395 396 /* sort the source if necessary */ 397 if (needsort) 398 ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls); 399 400 result = acep = calloc(1, resultsize * sizeof (ace_t)); 401 if (result == NULL) 402 goto out; 403 404 for (i = 0; i < n; i++) { 405 /* 406 * don't process CLASS_OBJ (mask); mask was grabbed in 407 * ln_aent_preprocess() 408 */ 409 if (aclent[i].a_type & CLASS_OBJ) 410 continue; 411 412 /* If we need an ACL_MASK emulator, prepend it now */ 413 if ((hasmask) && 414 (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) { 415 acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 416 acep->a_flags = 0; 417 if (aclent[i].a_type & GROUP_OBJ) { 418 acep->a_who = -1; 419 acep->a_flags |= 420 (ACE_IDENTIFIER_GROUP|ACE_GROUP); 421 } else if (aclent[i].a_type & USER) { 422 acep->a_who = aclent[i].a_id; 423 } else { 424 acep->a_who = aclent[i].a_id; 425 acep->a_flags |= ACE_IDENTIFIER_GROUP; 426 } 427 if (aclent[i].a_type & ACL_DEFAULT) { 428 acep->a_flags |= ACE_INHERIT_ONLY_ACE | 429 ACE_FILE_INHERIT_ACE | 430 ACE_DIRECTORY_INHERIT_ACE; 431 } 432 /* 433 * Set the access mask for the prepended deny 434 * ace. To do this, we invert the mask (found 435 * in ln_aent_preprocess()) then convert it to an 436 * DENY ace access_mask. 437 */ 438 acep->a_access_mask = mode_to_ace_access((mask ^ 07), 439 isdir, 0, 0); 440 acep += 1; 441 } 442 443 /* handle a_perm -> access_mask */ 444 acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm, 445 isdir, aclent[i].a_type & USER_OBJ, 1); 446 447 /* emulate a default aclent */ 448 if (aclent[i].a_type & ACL_DEFAULT) { 449 acep->a_flags |= ACE_INHERIT_ONLY_ACE | 450 ACE_FILE_INHERIT_ACE | 451 ACE_DIRECTORY_INHERIT_ACE; 452 } 453 454 /* 455 * handle a_perm and a_id 456 * 457 * this must be done last, since it involves the 458 * corresponding deny aces, which are handled 459 * differently for each different a_type. 460 */ 461 if (aclent[i].a_type & USER_OBJ) { 462 acep->a_who = -1; 463 acep->a_flags |= ACE_OWNER; 464 ace_make_deny(acep, acep + 1, isdir, B_TRUE); 465 acep += 2; 466 } else if (aclent[i].a_type & USER) { 467 acep->a_who = aclent[i].a_id; 468 ace_make_deny(acep, acep + 1, isdir, B_FALSE); 469 acep += 2; 470 } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) { 471 if (aclent[i].a_type & GROUP_OBJ) { 472 acep->a_who = -1; 473 acep->a_flags |= ACE_GROUP; 474 } else { 475 acep->a_who = aclent[i].a_id; 476 } 477 acep->a_flags |= ACE_IDENTIFIER_GROUP; 478 /* 479 * Set the corresponding deny for the group ace. 480 * 481 * The deny aces go after all of the groups, unlike 482 * everything else, where they immediately follow 483 * the allow ace. 484 * 485 * We calculate "skip", the number of slots to 486 * skip ahead for the deny ace, here. 487 * 488 * The pattern is: 489 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3 490 * thus, skip is 491 * (2 * numgroup) - 1 - groupi 492 * (2 * numgroup) to account for MD + A 493 * - 1 to account for the fact that we're on the 494 * access (A), not the mask (MD) 495 * - groupi to account for the fact that we have 496 * passed up groupi number of MD's. 497 */ 498 skip = (2 * numgroup) - 1 - groupi; 499 ace_make_deny(acep, acep + skip, isdir, B_FALSE); 500 /* 501 * If we just did the last group, skip acep past 502 * all of the denies; else, just move ahead one. 503 */ 504 if (++groupi >= numgroup) 505 acep += numgroup + 1; 506 else 507 acep += 1; 508 } else if (aclent[i].a_type & OTHER_OBJ) { 509 acep->a_who = -1; 510 acep->a_flags |= ACE_EVERYONE; 511 ace_make_deny(acep, acep + 1, isdir, B_FALSE); 512 acep += 2; 513 } else { 514 error = EINVAL; 515 goto out; 516 } 517 } 518 519 *acepp = result; 520 *rescount = resultsize; 521 522 out: 523 if (error != 0) { 524 if ((result != NULL) && (resultsize > 0)) { 525 free(result); 526 } 527 } 528 529 return (error); 530 } 531 532 static int 533 convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir, 534 ace_t **retacep, int *retacecnt) 535 { 536 ace_t *acep; 537 ace_t *dfacep; 538 ace_t *newacep; 539 int acecnt = 0; 540 int dfacecnt = 0; 541 int dfaclstart = 0; 542 int dfaclcnt = 0; 543 aclent_t *aclp; 544 int i; 545 int error; 546 547 ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls); 548 549 for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) { 550 if (aclp->a_type & ACL_DEFAULT) 551 break; 552 } 553 554 if (i < aclcnt) { 555 dfaclstart = aclcnt - i; 556 dfaclcnt = i; 557 } 558 559 if (dfaclcnt && isdir == 0) { 560 return (-1); 561 } 562 563 error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir); 564 if (error) 565 return (-1); 566 567 if (dfaclcnt) { 568 error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt, 569 &dfacep, &dfacecnt, isdir); 570 if (error) { 571 if (acep) { 572 free(acep); 573 } 574 return (-1); 575 } 576 } 577 578 newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt)); 579 if (newacep == NULL) 580 return (-1); 581 582 (void) memcpy(newacep, acep, sizeof (ace_t) * acecnt); 583 if (dfaclcnt) { 584 (void) memcpy(newacep + acecnt, dfacep, 585 sizeof (ace_t) * dfacecnt); 586 } 587 free(acep); 588 if (dfaclcnt) 589 free(dfacep); 590 591 *retacecnt = acecnt + dfacecnt; 592 *retacep = newacep; 593 return (0); 594 } 595 596 597 static int 598 cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp) 599 { 600 const char *fname; 601 int fd; 602 int ace_acl = 0; 603 int error; 604 int getcmd, cntcmd; 605 acl_t *acl_info; 606 int save_errno; 607 int stat_error; 608 struct stat64 statbuf; 609 610 *aclp = NULL; 611 if (type == ACL_PATH) { 612 fname = inp.file; 613 ace_acl = pathconf(fname, _PC_ACL_ENABLED); 614 } else { 615 fd = inp.fd; 616 ace_acl = fpathconf(fd, _PC_ACL_ENABLED); 617 } 618 619 if (ace_acl == -1) 620 return (-1); 621 622 /* 623 * if acl's aren't supported then 624 * send it through the old GETACL interface 625 */ 626 if (ace_acl == 0) { 627 ace_acl = _ACL_ACLENT_ENABLED; 628 } 629 630 if (ace_acl & _ACL_ACE_ENABLED) { 631 cntcmd = ACE_GETACLCNT; 632 getcmd = ACE_GETACL; 633 acl_info = acl_alloc(ACE_T); 634 } else { 635 cntcmd = GETACLCNT; 636 getcmd = GETACL; 637 acl_info = acl_alloc(ACLENT_T); 638 } 639 640 if (acl_info == NULL) 641 return (-1); 642 643 if (type == ACL_PATH) { 644 acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL); 645 } else { 646 acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL); 647 } 648 649 save_errno = errno; 650 if (acl_info->acl_cnt < 0) { 651 acl_free(acl_info); 652 errno = save_errno; 653 return (-1); 654 } 655 656 if (acl_info->acl_cnt == 0) { 657 acl_free(acl_info); 658 errno = save_errno; 659 return (0); 660 } 661 662 acl_info->acl_aclp = 663 malloc(acl_info->acl_cnt * acl_info->acl_entry_size); 664 save_errno = errno; 665 666 if (acl_info->acl_aclp == NULL) { 667 acl_free(acl_info); 668 errno = save_errno; 669 return (-1); 670 } 671 672 if (type == ACL_PATH) { 673 stat_error = stat64(fname, &statbuf); 674 error = acl(fname, getcmd, acl_info->acl_cnt, 675 acl_info->acl_aclp); 676 } else { 677 stat_error = fstat64(fd, &statbuf); 678 error = facl(fd, getcmd, acl_info->acl_cnt, 679 acl_info->acl_aclp); 680 } 681 682 save_errno = errno; 683 if (error == -1) { 684 acl_free(acl_info); 685 errno = save_errno; 686 return (-1); 687 } 688 689 690 if (stat_error == 0) { 691 acl_info->acl_flags = 692 (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0); 693 } else 694 acl_info->acl_flags = 0; 695 696 switch (acl_info->acl_type) { 697 case ACLENT_T: 698 if (acl_info->acl_cnt <= MIN_ACL_ENTRIES) 699 acl_info->acl_flags |= ACL_IS_TRIVIAL; 700 break; 701 case ACE_T: 702 if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0) 703 acl_info->acl_flags |= ACL_IS_TRIVIAL; 704 break; 705 default: 706 errno = EINVAL; 707 acl_free(acl_info); 708 return (-1); 709 } 710 711 if ((acl_info->acl_flags & ACL_IS_TRIVIAL) && 712 (get_flag & ACL_NO_TRIVIAL)) { 713 acl_free(acl_info); 714 errno = 0; 715 return (0); 716 } 717 718 *aclp = acl_info; 719 return (0); 720 } 721 722 /* 723 * return -1 on failure, otherwise the number of acl 724 * entries is returned 725 */ 726 int 727 acl_get(const char *path, int get_flag, acl_t **aclp) 728 { 729 acl_inp acl_inp; 730 acl_inp.file = path; 731 732 return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp)); 733 } 734 735 int 736 facl_get(int fd, int get_flag, acl_t **aclp) 737 { 738 739 acl_inp acl_inp; 740 acl_inp.fd = fd; 741 742 return (cacl_get(acl_inp, get_flag, ACL_FD, aclp)); 743 } 744 745 /* 746 * Set an ACL, translates acl to ace_t when appropriate. 747 */ 748 static int 749 cacl_set(acl_inp *acl_inp, acl_t *aclp, int type) 750 { 751 int error = 0; 752 int acl_flavor_target; 753 ace_t *acep = NULL; 754 int acecnt; 755 struct stat64 statbuf; 756 int stat_error; 757 int isdir; 758 759 760 if (type == ACL_PATH) { 761 stat_error = stat64(acl_inp->file, &statbuf); 762 if (stat_error) 763 return (-1); 764 acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED); 765 } else { 766 stat_error = fstat64(acl_inp->fd, &statbuf); 767 if (stat_error) 768 return (-1); 769 acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED); 770 } 771 772 isdir = S_ISDIR(statbuf.st_mode); 773 774 if (acl_flavor_target == -1) 775 return (-1); 776 777 /* 778 * Translate aclent_t ACL's to ACE ACL's. 779 */ 780 if (acl_flavor_target == _ACL_ACE_ENABLED && 781 aclp->acl_type == ACLENT_T) { 782 error = convert_aent_to_ace(aclp->acl_aclp, 783 aclp->acl_cnt, isdir, &acep, &acecnt); 784 if (error) { 785 errno = ENOTSUP; 786 return (-1); 787 } 788 /* 789 * replace old acl with newly translated acl 790 */ 791 free(aclp->acl_aclp); 792 aclp->acl_aclp = acep; 793 aclp->acl_cnt = acecnt; 794 aclp->acl_type = ACE_T; 795 } 796 797 if (type == ACL_PATH) { 798 error = acl(acl_inp->file, 799 (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 800 aclp->acl_cnt, aclp->acl_aclp); 801 } else { 802 error = facl(acl_inp->fd, 803 (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 804 aclp->acl_cnt, aclp->acl_aclp); 805 } 806 807 return (error); 808 } 809 810 int 811 acl_set(const char *path, acl_t *aclp) 812 { 813 acl_inp acl_inp; 814 815 acl_inp.file = path; 816 817 return (cacl_set(&acl_inp, aclp, ACL_PATH)); 818 } 819 820 int 821 facl_set(int fd, acl_t *aclp) 822 { 823 acl_inp acl_inp; 824 825 acl_inp.fd = fd; 826 827 return (cacl_set(&acl_inp, aclp, ACL_FD)); 828 } 829 830 int 831 acl_cnt(acl_t *aclp) 832 { 833 return (aclp->acl_cnt); 834 } 835 836 int 837 acl_type(acl_t *aclp) 838 { 839 return (aclp->acl_type); 840 } 841 842 acl_t * 843 acl_dup(acl_t *aclp) 844 { 845 acl_t *newaclp; 846 847 newaclp = acl_alloc(aclp->acl_type); 848 if (newaclp == NULL) 849 return (NULL); 850 851 newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt); 852 if (newaclp->acl_aclp == NULL) { 853 acl_free(newaclp); 854 return (NULL); 855 } 856 857 (void) memcpy(newaclp->acl_aclp, 858 aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt); 859 newaclp->acl_cnt = aclp->acl_cnt; 860 861 return (newaclp); 862 } 863 864 int 865 acl_flags(acl_t *aclp) 866 { 867 return (aclp->acl_flags); 868 } 869 870 void * 871 acl_data(acl_t *aclp) 872 { 873 return (aclp->acl_aclp); 874 } 875 876 /* 877 * Remove an ACL from a file and create a trivial ACL based 878 * off of the mode argument. After acl has been set owner/group 879 * are updated to match owner,group arguments 880 */ 881 int 882 acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode) 883 { 884 int error = 0; 885 aclent_t min_acl[MIN_ACL_ENTRIES]; 886 ace_t min_ace_acl[6]; /* owner, group, everyone + complement denies */ 887 int acl_flavor; 888 int aclcnt; 889 890 acl_flavor = pathconf(file, _PC_ACL_ENABLED); 891 892 if (acl_flavor == -1) 893 return (-1); 894 /* 895 * force it through aclent flavor when file system doesn't 896 * understand question 897 */ 898 if (acl_flavor == 0) 899 acl_flavor = _ACL_ACLENT_ENABLED; 900 901 if (acl_flavor & _ACL_ACLENT_ENABLED) { 902 min_acl[0].a_type = USER_OBJ; 903 min_acl[0].a_id = owner; 904 min_acl[0].a_perm = ((mode & 0700) >> 6); 905 min_acl[1].a_type = GROUP_OBJ; 906 min_acl[1].a_id = group; 907 min_acl[1].a_perm = ((mode & 0070) >> 3); 908 min_acl[2].a_type = CLASS_OBJ; 909 min_acl[2].a_id = (uid_t)-1; 910 min_acl[2].a_perm = ((mode & 0070) >> 3); 911 min_acl[3].a_type = OTHER_OBJ; 912 min_acl[3].a_id = (uid_t)-1; 913 min_acl[3].a_perm = (mode & 0007); 914 aclcnt = 4; 915 error = acl(file, SETACL, aclcnt, min_acl); 916 } else if (acl_flavor & _ACL_ACE_ENABLED) { 917 (void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6); 918 919 /* 920 * Make aces match request mode 921 */ 922 adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6); 923 adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3); 924 adjust_ace_pair(&min_ace_acl[4], mode & 0007); 925 926 error = acl(file, ACE_SETACL, 6, min_ace_acl); 927 } else { 928 errno = EINVAL; 929 error = 1; 930 } 931 932 if (error == 0) 933 error = chown(file, owner, group); 934 return (error); 935 } 936 937 static int 938 ace_match(void *entry1, void *entry2) 939 { 940 ace_t *p1 = (ace_t *)entry1; 941 ace_t *p2 = (ace_t *)entry2; 942 ace_t ace1, ace2; 943 944 ace1 = *p1; 945 ace2 = *p2; 946 947 /* 948 * Need to fixup who field for abstrations for 949 * accurate comparison, since field is undefined. 950 */ 951 if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 952 ace1.a_who = -1; 953 if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 954 ace2.a_who = -1; 955 return (memcmp(&ace1, &ace2, sizeof (ace_t))); 956 } 957 958 static int 959 aclent_match(void *entry1, void *entry2) 960 { 961 aclent_t *aclent1 = (aclent_t *)entry1; 962 aclent_t *aclent2 = (aclent_t *)entry2; 963 964 return (memcmp(aclent1, aclent2, sizeof (aclent_t))); 965 } 966 967 /* 968 * Find acl entries in acl that correspond to removeacl. Search 969 * is started from slot. The flag argument indicates whether to 970 * remove all matches or just the first match. 971 */ 972 int 973 acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag) 974 { 975 int i, j; 976 int match; 977 int (*acl_match)(void *acl1, void *acl2); 978 void *acl_entry, *remove_entry; 979 void *start; 980 int found = 0; 981 982 if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST) 983 flag = ACL_REMOVE_FIRST; 984 985 if (acl == NULL || removeacl == NULL) 986 return (EACL_NO_ACL_ENTRY); 987 988 if (acl->acl_type != removeacl->acl_type) 989 return (EACL_DIFF_TYPE); 990 991 if (acl->acl_type == ACLENT_T) 992 acl_match = aclent_match; 993 else 994 acl_match = ace_match; 995 996 for (i = 0, remove_entry = removeacl->acl_aclp; 997 i != removeacl->acl_cnt; i++) { 998 999 j = 0; 1000 acl_entry = (char *)acl->acl_aclp + 1001 (acl->acl_entry_size * start_slot); 1002 for (;;) { 1003 match = acl_match(acl_entry, remove_entry); 1004 if (match == 0) { 1005 found++; 1006 start = (char *)acl_entry + 1007 acl->acl_entry_size; 1008 (void) memmove(acl_entry, start, 1009 acl->acl_entry_size * 1010 acl->acl_cnt-- - (j + 1)); 1011 1012 if (flag == ACL_REMOVE_FIRST) 1013 break; 1014 /* 1015 * List has changed, restart search from 1016 * beginning. 1017 */ 1018 acl_entry = acl->acl_aclp; 1019 j = 0; 1020 continue; 1021 } 1022 acl_entry = ((char *)acl_entry + acl->acl_entry_size); 1023 if (++j >= acl->acl_cnt) { 1024 break; 1025 } 1026 } 1027 } 1028 1029 return ((found == 0) ? EACL_NO_ACL_ENTRY : 0); 1030 } 1031 1032 /* 1033 * Replace entires entries in acl1 with the corresponding entries 1034 * in newentries. The where argument specifies where to begin 1035 * the replacement. If the where argument is 1 greater than the 1036 * number of acl entries in acl1 then they are appended. If the 1037 * where argument is 2+ greater than the number of acl entries then 1038 * EACL_INVALID_SLOT is returned. 1039 */ 1040 int 1041 acl_modifyentries(acl_t *acl1, acl_t *newentries, int where) 1042 { 1043 1044 int slot; 1045 int slots_needed; 1046 int slots_left; 1047 int newsize; 1048 1049 if (acl1 == NULL || newentries == NULL) 1050 return (EACL_NO_ACL_ENTRY); 1051 1052 if (where < 0 || where >= acl1->acl_cnt) 1053 return (EACL_INVALID_SLOT); 1054 1055 if (acl1->acl_type != newentries->acl_type) 1056 return (EACL_DIFF_TYPE); 1057 1058 slot = where; 1059 1060 slots_left = acl1->acl_cnt - slot + 1; 1061 if (slots_left < newentries->acl_cnt) { 1062 slots_needed = newentries->acl_cnt - slots_left; 1063 newsize = (acl1->acl_entry_size * acl1->acl_cnt) + 1064 (acl1->acl_entry_size * slots_needed); 1065 acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 1066 if (acl1->acl_aclp == NULL) 1067 return (-1); 1068 } 1069 (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot), 1070 newentries->acl_aclp, 1071 newentries->acl_entry_size * newentries->acl_cnt); 1072 1073 /* 1074 * Did ACL grow? 1075 */ 1076 1077 if ((slot + newentries->acl_cnt) > acl1->acl_cnt) { 1078 acl1->acl_cnt = slot + newentries->acl_cnt; 1079 } 1080 1081 return (0); 1082 } 1083 1084 /* 1085 * Add acl2 entries into acl1. The where argument specifies where 1086 * to add the entries. 1087 */ 1088 int 1089 acl_addentries(acl_t *acl1, acl_t *acl2, int where) 1090 { 1091 1092 int newsize; 1093 int len; 1094 void *start; 1095 void *to; 1096 1097 if (acl1 == NULL || acl2 == NULL) 1098 return (EACL_NO_ACL_ENTRY); 1099 1100 if (acl1->acl_type != acl2->acl_type) 1101 return (EACL_DIFF_TYPE); 1102 1103 /* 1104 * allow where to specify 1 past last slot for an append operation 1105 * but anything greater is an error. 1106 */ 1107 if (where < 0 || where > acl1->acl_cnt) 1108 return (EACL_INVALID_SLOT); 1109 1110 newsize = (acl2->acl_entry_size * acl2->acl_cnt) + 1111 (acl1->acl_entry_size * acl1->acl_cnt); 1112 acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 1113 if (acl1->acl_aclp == NULL) 1114 return (-1); 1115 1116 /* 1117 * first push down entries where new ones will be inserted 1118 */ 1119 1120 to = (void *)((char *)acl1->acl_aclp + 1121 ((where + acl2->acl_cnt) * acl1->acl_entry_size)); 1122 1123 start = (void *)((char *)acl1->acl_aclp + 1124 where * acl1->acl_entry_size); 1125 1126 if (where < acl1->acl_cnt) { 1127 len = (acl1->acl_cnt - where) * acl1->acl_entry_size; 1128 (void) memmove(to, start, len); 1129 } 1130 1131 /* 1132 * now stick in new entries. 1133 */ 1134 1135 (void) memmove(start, acl2->acl_aclp, 1136 acl2->acl_cnt * acl2->acl_entry_size); 1137 1138 acl1->acl_cnt += acl2->acl_cnt; 1139 return (0); 1140 } 1141 1142 static void 1143 aclent_perms(int perm, char *txt_perms) 1144 { 1145 if (perm & S_IROTH) 1146 txt_perms[0] = 'r'; 1147 else 1148 txt_perms[0] = '-'; 1149 if (perm & S_IWOTH) 1150 txt_perms[1] = 'w'; 1151 else 1152 txt_perms[1] = '-'; 1153 if (perm & S_IXOTH) 1154 txt_perms[2] = 'x'; 1155 else 1156 txt_perms[2] = '-'; 1157 txt_perms[3] = '\0'; 1158 } 1159 1160 static char * 1161 pruname(uid_t uid) 1162 { 1163 struct passwd *passwdp; 1164 static char uidp[10]; /* big enough */ 1165 1166 passwdp = getpwuid(uid); 1167 if (passwdp == (struct passwd *)NULL) { 1168 /* could not get passwd information: display uid instead */ 1169 (void) sprintf(uidp, "%ld", (long)uid); 1170 return (uidp); 1171 } else 1172 return (passwdp->pw_name); 1173 } 1174 1175 static char * 1176 prgname(gid_t gid) 1177 { 1178 struct group *groupp; 1179 static char gidp[10]; /* big enough */ 1180 1181 groupp = getgrgid(gid); 1182 if (groupp == (struct group *)NULL) { 1183 /* could not get group information: display gid instead */ 1184 (void) sprintf(gidp, "%ld", (long)gid); 1185 return (gidp); 1186 } else 1187 return (groupp->gr_name); 1188 } 1189 static void 1190 aclent_printacl(acl_t *aclp) 1191 { 1192 aclent_t *tp; 1193 int aclcnt; 1194 int mask; 1195 int slot = 0; 1196 char perm[4]; 1197 1198 /* display ACL: assume it is sorted. */ 1199 aclcnt = aclp->acl_cnt; 1200 for (tp = aclp->acl_aclp; aclcnt--; tp++) { 1201 if (tp->a_type == CLASS_OBJ) 1202 mask = tp->a_perm; 1203 } 1204 aclcnt = aclp->acl_cnt; 1205 for (tp = aclp->acl_aclp; aclcnt--; tp++) { 1206 (void) printf(" %d:", slot++); 1207 switch (tp->a_type) { 1208 case USER: 1209 aclent_perms(tp->a_perm, perm); 1210 (void) printf("user:%s:%s\t\t", 1211 pruname(tp->a_id), perm); 1212 aclent_perms((tp->a_perm & mask), perm); 1213 (void) printf("#effective:%s\n", perm); 1214 break; 1215 case USER_OBJ: 1216 /* no need to display uid */ 1217 aclent_perms(tp->a_perm, perm); 1218 (void) printf("user::%s\n", perm); 1219 break; 1220 case GROUP: 1221 aclent_perms(tp->a_perm, perm); 1222 (void) printf("group:%s:%s\t\t", 1223 prgname(tp->a_id), perm); 1224 aclent_perms(tp->a_perm & mask, perm); 1225 (void) printf("#effective:%s\n", perm); 1226 break; 1227 case GROUP_OBJ: 1228 aclent_perms(tp->a_perm, perm); 1229 (void) printf("group::%s\t\t", perm); 1230 aclent_perms(tp->a_perm & mask, perm); 1231 (void) printf("#effective:%s\n", perm); 1232 break; 1233 case CLASS_OBJ: 1234 aclent_perms(tp->a_perm, perm); 1235 (void) printf("mask:%s\n", perm); 1236 break; 1237 case OTHER_OBJ: 1238 aclent_perms(tp->a_perm, perm); 1239 (void) printf("other:%s\n", perm); 1240 break; 1241 case DEF_USER: 1242 aclent_perms(tp->a_perm, perm); 1243 (void) printf("default:user:%s:%s\n", 1244 pruname(tp->a_id), perm); 1245 break; 1246 case DEF_USER_OBJ: 1247 aclent_perms(tp->a_perm, perm); 1248 (void) printf("default:user::%s\n", perm); 1249 break; 1250 case DEF_GROUP: 1251 aclent_perms(tp->a_perm, perm); 1252 (void) printf("default:group:%s:%s\n", 1253 prgname(tp->a_id), perm); 1254 break; 1255 case DEF_GROUP_OBJ: 1256 aclent_perms(tp->a_perm, perm); 1257 (void) printf("default:group::%s\n", perm); 1258 break; 1259 case DEF_CLASS_OBJ: 1260 aclent_perms(tp->a_perm, perm); 1261 (void) printf("default:mask:%s\n", perm); 1262 break; 1263 case DEF_OTHER_OBJ: 1264 aclent_perms(tp->a_perm, perm); 1265 (void) printf("default:other:%s\n", perm); 1266 break; 1267 default: 1268 (void) fprintf(stderr, 1269 gettext("unrecognized entry\n")); 1270 break; 1271 } 1272 } 1273 } 1274 1275 static void 1276 split_line(char *str, int cols) 1277 { 1278 char *ptr; 1279 int len; 1280 int i; 1281 int last_split; 1282 char pad[11]; 1283 int pad_len; 1284 1285 len = strlen(str); 1286 ptr = str; 1287 (void) strcpy(pad, ""); 1288 pad_len = 0; 1289 1290 ptr = str; 1291 last_split = 0; 1292 for (i = 0; i != len; i++) { 1293 if ((i + pad_len + 4) >= cols) { 1294 (void) printf("%s%.*s\n", pad, last_split, ptr); 1295 ptr = &ptr[last_split]; 1296 len = strlen(ptr); 1297 i = 0; 1298 pad_len = 4; 1299 (void) strcpy(pad, " "); 1300 } else { 1301 if (ptr[i] == '/' || ptr[i] == ':') { 1302 last_split = i; 1303 } 1304 } 1305 } 1306 if (i == len) { 1307 (void) printf("%s%s\n", pad, ptr); 1308 } 1309 } 1310 1311 static void 1312 ace_printacl(acl_t *aclp, int cols) 1313 { 1314 int slot = 0; 1315 char *token; 1316 char *acltext; 1317 1318 acltext = acl_totext(aclp); 1319 1320 if (acltext == NULL) 1321 return; 1322 1323 token = strtok(acltext, ","); 1324 if (token == NULL) { 1325 free(acltext); 1326 return; 1327 } 1328 1329 do { 1330 (void) printf(" %d:", slot++); 1331 split_line(token, cols - 5); 1332 } while (token = strtok(NULL, ",")); 1333 free(acltext); 1334 } 1335 1336 /* 1337 * pretty print an ACL. 1338 * For aclent_t ACL's the format is 1339 * similar to the old format used by getfacl, 1340 * with the addition of adding a "slot" number 1341 * before each entry. 1342 * 1343 * for ace_t ACL's the cols variable will break up 1344 * the long lines into multiple lines and will also 1345 * print a "slot" number. 1346 */ 1347 void 1348 acl_printacl(acl_t *aclp, int cols) 1349 { 1350 1351 switch (aclp->acl_type) { 1352 case ACLENT_T: 1353 aclent_printacl(aclp); 1354 break; 1355 case ACE_T: 1356 ace_printacl(aclp, cols); 1357 break; 1358 } 1359 } 1360 1361 1362 /* 1363 * return text for an ACL error. 1364 */ 1365 char * 1366 acl_strerror(int errnum) 1367 { 1368 switch (errnum) { 1369 case EACL_GRP_ERROR: 1370 return (dgettext(TEXT_DOMAIN, 1371 "There is more than one user group owner entry")); 1372 case EACL_USER_ERROR: 1373 return (dgettext(TEXT_DOMAIN, 1374 "There is more than one user owner entry")); 1375 case EACL_OTHER_ERROR: 1376 return (dgettext(TEXT_DOMAIN, 1377 "There is more than one other entry")); 1378 case EACL_CLASS_ERROR: 1379 return (dgettext(TEXT_DOMAIN, 1380 "There is more than one mask entry")); 1381 case EACL_DUPLICATE_ERROR: 1382 return (dgettext(TEXT_DOMAIN, 1383 "Duplicate user or group entries")); 1384 case EACL_MISS_ERROR: 1385 return (dgettext(TEXT_DOMAIN, 1386 "Missing user/group owner, other, mask entry")); 1387 case EACL_MEM_ERROR: 1388 return (dgettext(TEXT_DOMAIN, 1389 "Memory error")); 1390 case EACL_ENTRY_ERROR: 1391 return (dgettext(TEXT_DOMAIN, 1392 "Unrecognized entry type")); 1393 case EACL_INHERIT_ERROR: 1394 return (dgettext(TEXT_DOMAIN, 1395 "Invalid inheritance flags")); 1396 case EACL_FLAGS_ERROR: 1397 return (dgettext(TEXT_DOMAIN, 1398 "Unrecognized entry flags")); 1399 case EACL_PERM_MASK_ERROR: 1400 return (dgettext(TEXT_DOMAIN, 1401 "Invalid ACL permissions")); 1402 case EACL_COUNT_ERROR: 1403 return (dgettext(TEXT_DOMAIN, 1404 "Invalid ACL count")); 1405 case EACL_INVALID_SLOT: 1406 return (dgettext(TEXT_DOMAIN, 1407 "Invalid ACL entry number specified")); 1408 case EACL_NO_ACL_ENTRY: 1409 return (dgettext(TEXT_DOMAIN, 1410 "ACL entry doesn't exist")); 1411 case EACL_DIFF_TYPE: 1412 return (dgettext(TEXT_DOMAIN, 1413 "ACL type's are different")); 1414 case EACL_INVALID_USER_GROUP: 1415 return (dgettext(TEXT_DOMAIN, "Invalid user or group")); 1416 case EACL_INVALID_STR: 1417 return (dgettext(TEXT_DOMAIN, "ACL string is invalid")); 1418 case EACL_FIELD_NOT_BLANK: 1419 return (dgettext(TEXT_DOMAIN, "Field expected to be blank")); 1420 case EACL_INVALID_ACCESS_TYPE: 1421 return (dgettext(TEXT_DOMAIN, "Invalid access type")); 1422 case EACL_UNKNOWN_DATA: 1423 return (dgettext(TEXT_DOMAIN, "Unrecognized entry")); 1424 case EACL_MISSING_FIELDS: 1425 return (dgettext(TEXT_DOMAIN, 1426 "ACL specification missing required fields")); 1427 case EACL_INHERIT_NOTDIR: 1428 return (dgettext(TEXT_DOMAIN, 1429 "Inheritance flags are only allowed on directories")); 1430 case -1: 1431 return (strerror(errno)); 1432 default: 1433 errno = EINVAL; 1434 return (dgettext(TEXT_DOMAIN, "Unknown error")); 1435 } 1436 } 1437