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