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 /*LINTLIBRARY*/ 29 30 #include <grp.h> 31 #include <pwd.h> 32 #include <string.h> 33 #include <limits.h> 34 #include <stdlib.h> 35 #include <errno.h> 36 #include <sys/param.h> 37 #include <sys/types.h> 38 #include <sys/acl.h> 39 #include <aclutils.h> 40 #include <libintl.h> 41 42 43 extern acl_t *acl_alloc(enum acl_type); 44 45 /* 46 * acltotext() converts each ACL entry to look like this: 47 * 48 * entry_type:uid^gid^name:perms 49 * 50 * The maximum length of entry_type is 14 ("defaultgroup::" and 51 * "defaultother::") hence ENTRYTYPELEN is set to 14. 52 * 53 * The max length of a uid^gid^name entry (in theory) is 8, hence we use 54 * LOGNAME_MAX. 55 * 56 * The length of a perms entry is 4 to allow for the comma appended to each 57 * to each acl entry. Hence PERMS is set to 4. 58 */ 59 60 #define ENTRYTYPELEN 14 61 #define PERMS 4 62 #define ACL_ENTRY_SIZE (ENTRYTYPELEN + LOGNAME_MAX + PERMS) 63 64 #define UPDATE_WHERE where = dstr->aclexport + strlen(dstr->aclexport) 65 66 struct dynaclstr { 67 size_t bufsize; /* current size of aclexport */ 68 char *aclexport; 69 }; 70 71 static char *strappend(char *, char *); 72 static char *convert_perm(char *, o_mode_t); 73 static int increase_length(struct dynaclstr *, size_t); 74 75 static int 76 acl_str_to_id(char *str, int *id) 77 { 78 char *end; 79 uid_t value; 80 81 value = strtol(str, &end, 10); 82 83 if (errno != 0 || *end != '\0') 84 return (EACL_INVALID_USER_GROUP); 85 86 *id = value; 87 88 return (0); 89 } 90 91 /* 92 * Convert internal acl representation to external representation. 93 * 94 * The length of a non-owning user name or non-owning group name ie entries 95 * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX. We 96 * thus check the length of these entries, and if greater than LOGNAME_MAX, 97 * we realloc() via increase_length(). 98 * 99 * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always 100 * adhered to. 101 */ 102 char * 103 acltotext(aclent_t *aclp, int aclcnt) 104 { 105 char *aclexport; 106 char *where; 107 struct group *groupp; 108 struct passwd *passwdp; 109 struct dynaclstr *dstr; 110 int i, rtn; 111 size_t excess = 0; 112 113 if (aclp == NULL) 114 return (NULL); 115 if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL) 116 return (NULL); 117 dstr->bufsize = aclcnt * ACL_ENTRY_SIZE; 118 if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) { 119 free(dstr); 120 return (NULL); 121 } 122 *dstr->aclexport = '\0'; 123 where = dstr->aclexport; 124 125 for (i = 0; i < aclcnt; i++, aclp++) { 126 switch (aclp->a_type) { 127 case DEF_USER_OBJ: 128 case USER_OBJ: 129 if (aclp->a_type == USER_OBJ) 130 where = strappend(where, "user::"); 131 else 132 where = strappend(where, "defaultuser::"); 133 where = convert_perm(where, aclp->a_perm); 134 break; 135 case DEF_USER: 136 case USER: 137 if (aclp->a_type == USER) 138 where = strappend(where, "user:"); 139 else 140 where = strappend(where, "defaultuser:"); 141 passwdp = getpwuid(aclp->a_id); 142 if (passwdp == (struct passwd *)NULL) { 143 /* put in uid instead */ 144 (void) sprintf(where, "%d", aclp->a_id); 145 UPDATE_WHERE; 146 } else { 147 excess = strlen(passwdp->pw_name) - LOGNAME_MAX; 148 if (excess > 0) { 149 rtn = increase_length(dstr, excess); 150 if (rtn == 1) { 151 UPDATE_WHERE; 152 } else { 153 free(dstr->aclexport); 154 free(dstr); 155 return (NULL); 156 } 157 } 158 where = strappend(where, passwdp->pw_name); 159 } 160 where = strappend(where, ":"); 161 where = convert_perm(where, aclp->a_perm); 162 break; 163 case DEF_GROUP_OBJ: 164 case GROUP_OBJ: 165 if (aclp->a_type == GROUP_OBJ) 166 where = strappend(where, "group::"); 167 else 168 where = strappend(where, "defaultgroup::"); 169 where = convert_perm(where, aclp->a_perm); 170 break; 171 case DEF_GROUP: 172 case GROUP: 173 if (aclp->a_type == GROUP) 174 where = strappend(where, "group:"); 175 else 176 where = strappend(where, "defaultgroup:"); 177 groupp = getgrgid(aclp->a_id); 178 if (groupp == (struct group *)NULL) { 179 /* put in gid instead */ 180 (void) sprintf(where, "%d", aclp->a_id); 181 UPDATE_WHERE; 182 } else { 183 excess = strlen(groupp->gr_name) - LOGNAME_MAX; 184 if (excess > 0) { 185 rtn = increase_length(dstr, excess); 186 if (rtn == 1) { 187 UPDATE_WHERE; 188 } else { 189 free(dstr->aclexport); 190 free(dstr); 191 return (NULL); 192 } 193 } 194 where = strappend(where, groupp->gr_name); 195 } 196 where = strappend(where, ":"); 197 where = convert_perm(where, aclp->a_perm); 198 break; 199 case DEF_CLASS_OBJ: 200 case CLASS_OBJ: 201 if (aclp->a_type == CLASS_OBJ) 202 where = strappend(where, "mask:"); 203 else 204 where = strappend(where, "defaultmask:"); 205 where = convert_perm(where, aclp->a_perm); 206 break; 207 case DEF_OTHER_OBJ: 208 case OTHER_OBJ: 209 if (aclp->a_type == OTHER_OBJ) 210 where = strappend(where, "other:"); 211 else 212 where = strappend(where, "defaultother:"); 213 where = convert_perm(where, aclp->a_perm); 214 break; 215 default: 216 free(dstr->aclexport); 217 free(dstr); 218 return (NULL); 219 220 } 221 if (i < aclcnt - 1) 222 where = strappend(where, ","); 223 } 224 aclexport = dstr->aclexport; 225 free(dstr); 226 return (aclexport); 227 } 228 229 /* 230 * Convert external acl representation to internal representation. 231 * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,] 232 * The comma at the end is not prescribed by the man pages. 233 * But it is needed not to break the old programs. 234 */ 235 static int 236 aclent_aclfromtext(char *aclstr, acl_t **ret_aclp) 237 { 238 char *fieldp; 239 char *tp; 240 char *nextp; 241 char *allocp; 242 char *aclimport; 243 int entry_type; 244 int id; 245 int len; 246 int error; 247 o_mode_t perm; 248 aclent_t *tmpaclp; 249 acl_t *aclp; 250 struct group *groupp; 251 struct passwd *passwdp; 252 253 aclp = NULL; 254 255 if (! aclstr) 256 return (NULL); 257 258 aclp = acl_alloc(ACLENT_T); 259 if (aclp == NULL) { 260 return (EACL_MEM_ERROR); 261 } 262 263 *ret_aclp = NULL; 264 265 len = strlen(aclstr); 266 267 if ((aclimport = allocp = strdup(aclstr)) == NULL) { 268 return (EACL_MEM_ERROR); 269 } 270 271 if (aclimport[len - 1] == ',') 272 aclimport[len - 1] = '\0'; 273 274 for (; aclimport; ) { 275 /* look for an ACL entry */ 276 tp = strchr(aclimport, ','); 277 if (tp == NULL) { 278 nextp = NULL; 279 } else { 280 *tp = '\0'; 281 nextp = tp + 1; 282 } 283 284 aclp->acl_cnt += 1; 285 286 /* 287 * get additional memory: 288 * can be more efficient by allocating a bigger block 289 * each time. 290 */ 291 if (aclp->acl_cnt > 1) 292 tmpaclp = (aclent_t *)realloc(aclp->acl_aclp, 293 sizeof (aclent_t) * (aclp->acl_cnt)); 294 else 295 tmpaclp = (aclent_t *)malloc(sizeof (aclent_t)); 296 if (tmpaclp == NULL) { 297 free(allocp); 298 acl_free(aclp); 299 return (EACL_MEM_ERROR); 300 } 301 aclp->acl_aclp = tmpaclp; 302 tmpaclp = (aclent_t *)aclp->acl_aclp + (aclp->acl_cnt - 1); 303 304 /* look for entry type field */ 305 tp = strchr(aclimport, ':'); 306 if (tp == NULL) { 307 free(allocp); 308 if (aclp) 309 acl_free(aclp); 310 return (EACL_ENTRY_ERROR); 311 } else 312 *tp = '\0'; 313 if (strcmp(aclimport, "user") == 0) { 314 if (*(tp+1) == ':') 315 entry_type = USER_OBJ; 316 else 317 entry_type = USER; 318 } else if (strcmp(aclimport, "group") == 0) { 319 if (*(tp+1) == ':') 320 entry_type = GROUP_OBJ; 321 else 322 entry_type = GROUP; 323 } else if (strcmp(aclimport, "other") == 0) 324 entry_type = OTHER_OBJ; 325 else if (strcmp(aclimport, "mask") == 0) 326 entry_type = CLASS_OBJ; 327 else if (strcmp(aclimport, "defaultuser") == 0) { 328 if (*(tp+1) == ':') 329 entry_type = DEF_USER_OBJ; 330 else 331 entry_type = DEF_USER; 332 } else if (strcmp(aclimport, "defaultgroup") == 0) { 333 if (*(tp+1) == ':') 334 entry_type = DEF_GROUP_OBJ; 335 else 336 entry_type = DEF_GROUP; 337 } else if (strcmp(aclimport, "defaultmask") == 0) 338 entry_type = DEF_CLASS_OBJ; 339 else if (strcmp(aclimport, "defaultother") == 0) 340 entry_type = DEF_OTHER_OBJ; 341 else { 342 free(allocp); 343 acl_free(aclp); 344 return (EACL_ENTRY_ERROR); 345 } 346 347 /* look for user/group name */ 348 if (entry_type != CLASS_OBJ && entry_type != OTHER_OBJ && 349 entry_type != DEF_CLASS_OBJ && 350 entry_type != DEF_OTHER_OBJ) { 351 fieldp = tp + 1; 352 tp = strchr(fieldp, ':'); 353 if (tp == NULL) { 354 free(allocp); 355 acl_free(aclp); 356 return (EACL_INVALID_USER_GROUP); 357 } else 358 *tp = '\0'; 359 if (fieldp != tp) { 360 /* 361 * The second field could be empty. We only care 362 * when the field has user/group name. 363 */ 364 if (entry_type == USER || 365 entry_type == DEF_USER) { 366 /* 367 * The reentrant interface getpwnam_r() 368 * is uncommitted and subject to 369 * change. Use the friendlier interface 370 * getpwnam(). 371 */ 372 error = 0; 373 passwdp = getpwnam(fieldp); 374 if (passwdp == NULL) { 375 error = acl_str_to_id(fieldp, 376 &id); 377 } else { 378 id = passwdp->pw_uid; 379 } 380 381 if (error) { 382 free(allocp); 383 acl_free(aclp); 384 return (error); 385 } 386 387 } else { 388 error = 0; 389 if (entry_type == GROUP || 390 entry_type == DEF_GROUP) { 391 groupp = getgrnam(fieldp); 392 if (groupp == NULL) { 393 error = acl_str_to_id( 394 fieldp, &id); 395 } 396 if (error == 0) 397 id = groupp->gr_gid; 398 } 399 if (error) { 400 free(allocp); 401 acl_free(aclp); 402 return (error); 403 } 404 } 405 } else { 406 /* 407 * The second field is empty. 408 * Treat it as undefined (-1) 409 */ 410 id = -1; 411 } 412 } else { 413 /* 414 * Let's not break the old applications 415 * that use mask::rwx, other::rwx format, 416 * though they violate the man pages. 417 */ 418 if (*(tp + 1) == ':') 419 *++tp = 0; 420 } 421 422 /* next field: permission */ 423 fieldp = tp + 1; 424 if (strlen(fieldp) != 3) { 425 /* not "rwx" format */ 426 free(allocp); 427 acl_free(aclp); 428 return (EACL_PERM_MASK_ERROR); 429 } else { 430 char s[] = "rwx"; 431 int mask = 0x04; 432 int i; 433 perm = 0; 434 435 for (i = 0; i < 3; i++, mask /= 2) { 436 if (fieldp[i] == s[i]) 437 perm |= mask; 438 else if (fieldp[i] != '-') { 439 free(allocp); 440 acl_free(aclp); 441 return (EACL_PERM_MASK_ERROR); 442 } 443 } 444 } 445 446 tmpaclp->a_type = entry_type; 447 tmpaclp->a_id = id; 448 tmpaclp->a_perm = perm; 449 aclimport = nextp; 450 } 451 free(allocp); 452 *ret_aclp = aclp; 453 return (0); 454 } 455 456 aclent_t * 457 aclfromtext(char *aclstr, int *aclcnt) 458 { 459 acl_t *aclp; 460 aclent_t *aclentp; 461 int error; 462 463 error = aclent_aclfromtext(aclstr, &aclp); 464 if (error) 465 return (NULL); 466 467 aclentp = aclp->acl_aclp; 468 aclp->acl_aclp = NULL; 469 acl_free(aclp); 470 471 *aclcnt = aclp->acl_cnt; 472 return (aclentp); 473 } 474 475 476 static char * 477 strappend(char *where, char *newstr) 478 { 479 (void) strcat(where, newstr); 480 return (where + strlen(newstr)); 481 } 482 483 static char * 484 convert_perm(char *where, o_mode_t perm) 485 { 486 if (perm & 04) 487 where = strappend(where, "r"); 488 else 489 where = strappend(where, "-"); 490 if (perm & 02) 491 where = strappend(where, "w"); 492 else 493 where = strappend(where, "-"); 494 if (perm & 01) 495 where = strappend(where, "x"); 496 else 497 where = strappend(where, "-"); 498 /* perm is the last field */ 499 return (where); 500 } 501 502 static char * 503 ace_convert_perm(char *where, mode_t perm, int isdir, int iflags) 504 { 505 char *start = where; 506 507 /* 508 * The following mneumonics all have the 509 * same value. The only difference is the 510 * first value is for files and second for directories 511 * ACE_READ_DATA/ACE_LIST_DIRECTORY 512 * ACE_WRITE_DATA/ACE_ADD_FILE 513 * ACE_APPEND_DATA/ACE_ADD_SUBDIRECTORY 514 */ 515 516 /* 517 * If ACE is a directory, but inheritance indicates its 518 * for a file then print permissions for file rather than 519 * dir. 520 */ 521 if (isdir) { 522 if (perm & ACE_LIST_DIRECTORY) { 523 if (iflags == ACE_FILE_INHERIT_ACE) 524 where = strappend(where, "read_data/"); 525 else 526 where = strappend(where, 527 "list_directory/read_data/"); 528 } 529 if (perm & ACE_ADD_FILE) { 530 if (iflags == ACE_FILE_INHERIT_ACE) 531 where = strappend(where, "write_data/"); 532 else 533 where = strappend(where, 534 "add_file/write_data/"); 535 } 536 if (perm & ACE_ADD_SUBDIRECTORY) { 537 if (iflags == ACE_FILE_INHERIT_ACE) 538 where = strappend(where, "append_data/"); 539 else 540 where = strappend(where, 541 "add_subdirectory/append_data/"); 542 } 543 } else { 544 if (perm & ACE_READ_DATA) 545 where = strappend(where, "read_data/"); 546 if (perm & ACE_WRITE_DATA) 547 where = strappend(where, "write_data/"); 548 if (perm & ACE_APPEND_DATA) 549 where = strappend(where, "append_data/"); 550 } 551 if (perm & ACE_READ_NAMED_ATTRS) 552 where = strappend(where, "read_xattr/"); 553 if (perm & ACE_WRITE_NAMED_ATTRS) 554 where = strappend(where, "write_xattr/"); 555 if (perm & ACE_EXECUTE) 556 where = strappend(where, "execute/"); 557 if (perm & ACE_DELETE_CHILD) 558 where = strappend(where, "delete_child/"); 559 if (perm & ACE_READ_ATTRIBUTES) 560 where = strappend(where, "read_attributes/"); 561 if (perm & ACE_WRITE_ATTRIBUTES) 562 where = strappend(where, "write_attributes/"); 563 if (perm & ACE_DELETE) 564 where = strappend(where, "delete/"); 565 if (perm & ACE_READ_ACL) 566 where = strappend(where, "read_acl/"); 567 if (perm & ACE_WRITE_ACL) 568 where = strappend(where, "write_acl/"); 569 if (perm & ACE_WRITE_OWNER) 570 where = strappend(where, "write_owner/"); 571 if (perm & ACE_SYNCHRONIZE) 572 where = strappend(where, "synchronize"); 573 574 if (start[strlen(start) - 1] == '/') { 575 start[strlen(start) - 1] = '\0'; 576 where = start + strlen(start); 577 } 578 return (where); 579 } 580 581 int 582 ace_permask(char *perm_tok, int *perm) 583 { 584 if (strcmp(perm_tok, "read_data") == 0) 585 *perm |= ACE_READ_DATA; 586 else if (strcmp(perm_tok, "list_directory") == 0) 587 *perm |= ACE_LIST_DIRECTORY; 588 else if (strcmp(perm_tok, "write_data") == 0) 589 *perm |= ACE_WRITE_DATA; 590 else if (strcmp(perm_tok, "add_file") == 0) 591 *perm |= ACE_ADD_FILE; 592 else if (strcmp(perm_tok, "append_data") == 0) 593 *perm |= ACE_APPEND_DATA; 594 else if (strcmp(perm_tok, "add_subdirectory") == 0) 595 *perm |= ACE_ADD_SUBDIRECTORY; 596 else if (strcmp(perm_tok, "read_xattr") == 0) 597 *perm |= ACE_READ_NAMED_ATTRS; 598 else if (strcmp(perm_tok, "write_xattr") == 0) 599 *perm |= ACE_WRITE_NAMED_ATTRS; 600 else if (strcmp(perm_tok, "execute") == 0) 601 *perm |= ACE_EXECUTE; 602 else if (strcmp(perm_tok, "delete_child") == 0) 603 *perm |= ACE_DELETE_CHILD; 604 else if (strcmp(perm_tok, "read_attributes") == 0) 605 *perm |= ACE_READ_ATTRIBUTES; 606 else if (strcmp(perm_tok, "write_attributes") == 0) 607 *perm |= ACE_WRITE_ATTRIBUTES; 608 else if (strcmp(perm_tok, "delete") == 0) 609 *perm |= ACE_DELETE; 610 else if (strcmp(perm_tok, "read_acl") == 0) 611 *perm |= ACE_READ_ACL; 612 else if (strcmp(perm_tok, "write_acl") == 0) 613 *perm |= ACE_WRITE_ACL; 614 else if (strcmp(perm_tok, "write_owner") == 0) 615 *perm |= ACE_WRITE_OWNER; 616 else if (strcmp(perm_tok, "synchronize") == 0) 617 *perm |= ACE_SYNCHRONIZE; 618 else { 619 return (1); 620 } 621 622 return (0); 623 } 624 625 /* 626 * Callers should check the return code as this routine may change the string 627 * pointer in dynaclstr. 628 */ 629 static int 630 increase_length(struct dynaclstr *dacl, size_t increase) 631 { 632 char *tptr; 633 size_t newsize; 634 635 newsize = dacl->bufsize + increase; 636 tptr = realloc(dacl->aclexport, newsize); 637 if (tptr != NULL) { 638 dacl->aclexport = tptr; 639 dacl->bufsize = newsize; 640 return (1); 641 } else 642 return (0); 643 } 644 645 /* 646 * ace_acltotext() conver each ace formatted acl to look like this: 647 * 648 * entry_type:uid^gid^name:perms:allow^deny[:flags][,] 649 * 650 * The maximum length of entry_type is 5 ("group") 651 * 652 * The max length of a uid^gid^name entry (in theory) is 8, hence we use 653 * LOGNAME_MAX. 654 * 655 * The length of a perms entry is 144 i.e read_data/write_data... 656 * to each acl entry. 657 * 658 * iflags: file_inherit/dir_inherit/inherit_only/no_propagate 659 * 660 */ 661 662 #define ACE_ENTRYTYPLEN 6 663 #define IFLAGS_SIZE 51 664 #define ACCESS_TYPE_SIZE 5 665 #define COLON_CNT 3 666 #define PERMS_LEN 216 667 #define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + LOGNAME_MAX + PERMS_LEN +\ 668 ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT) 669 670 static char * 671 ace_acltotext(acl_t *aceaclp) 672 { 673 ace_t *aclp = aceaclp->acl_aclp; 674 int aclcnt = aceaclp->acl_cnt; 675 char *aclexport; 676 char *where; 677 char *start; 678 struct group *groupp; 679 struct passwd *passwdp; 680 struct dynaclstr *dstr; 681 int i, rtn; 682 int isdir = (aceaclp->acl_flags & ACL_IS_DIR); 683 size_t excess = 0; 684 685 if (aclp == NULL) 686 return (NULL); 687 if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL) 688 return (NULL); 689 dstr->bufsize = aclcnt * ACE_ENTRY_SIZE; 690 if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) 691 return (NULL); 692 *dstr->aclexport = '\0'; 693 where = dstr->aclexport; 694 695 for (i = 0; i < aclcnt; i++, aclp++) { 696 switch (aclp->a_flags & 0xf040) { 697 case ACE_OWNER: 698 case 0: 699 if ((aclp->a_flags & 0xf040) == ACE_OWNER) 700 where = strappend(where, "owner@"); 701 else 702 where = strappend(where, "user:"); 703 if ((aclp->a_flags & 0xf040) == 0) { 704 passwdp = getpwuid(aclp->a_who); 705 if (passwdp == (struct passwd *)NULL) { 706 /* put in uid instead */ 707 (void) sprintf(where, "%d", 708 aclp->a_who); 709 } else { 710 excess = strlen(passwdp->pw_name) - 711 LOGNAME_MAX; 712 if (excess > 0) { 713 rtn = increase_length(dstr, 714 excess); 715 if (rtn == 1) 716 /* reset where */ 717 where = 718 dstr->aclexport + 719 strlen( 720 dstr->aclexport); 721 else 722 return (NULL); 723 } 724 where = strappend(where, 725 passwdp->pw_name); 726 } 727 } else { 728 where = strappend(where, ""); 729 } 730 where = strappend(where, ":"); 731 break; 732 case ACE_GROUP|ACE_IDENTIFIER_GROUP: 733 case ACE_IDENTIFIER_GROUP: 734 if ((aclp->a_flags & 0xf040) == 735 (ACE_GROUP | ACE_IDENTIFIER_GROUP)) 736 where = strappend(where, "group@"); 737 else 738 where = strappend(where, "group:"); 739 if (!(aclp->a_flags & ACE_GROUP)) { 740 groupp = getgrgid(aclp->a_who); 741 if (groupp == (struct group *)NULL) { 742 /* put in gid instead */ 743 (void) sprintf(where, 744 "%d", aclp->a_who); 745 } else { 746 excess = strlen(groupp->gr_name) - 747 LOGNAME_MAX; 748 if (excess > 0) { 749 rtn = increase_length(dstr, 750 excess); 751 if (rtn == 1) 752 /* reset where */ 753 where = 754 dstr->aclexport + 755 strlen( 756 dstr->aclexport); 757 else 758 return (NULL); 759 } 760 where = strappend(where, 761 groupp->gr_name); 762 } 763 } else { 764 where = strappend(where, ""); 765 } 766 where = strappend(where, ":"); 767 break; 768 case ACE_EVERYONE: 769 where = strappend(where, "everyone@:"); 770 break; 771 default: 772 free(dstr->aclexport); 773 free(dstr); 774 return (NULL); 775 776 } 777 where = ace_convert_perm(where, aclp->a_access_mask, 778 isdir, (aclp->a_flags & 779 (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE))); 780 where = strappend(where, 781 (aclp->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) ? 782 ":allow" : ":deny"); 783 784 /* 785 * slap on inheritance flags if we have any 786 */ 787 788 if (aclp->a_flags & 0xf) { 789 where = strappend(where, ":"); 790 start = where; 791 if (aclp->a_flags & ACE_FILE_INHERIT_ACE) 792 where = strappend(where, "file_inherit/"); 793 if (aclp->a_flags & ACE_DIRECTORY_INHERIT_ACE) 794 where = strappend(where, "dir_inherit/"); 795 if (aclp->a_flags & ACE_NO_PROPAGATE_INHERIT_ACE) 796 where = strappend(where, "no_propagate/"); 797 if (aclp->a_flags & ACE_INHERIT_ONLY_ACE) 798 where = strappend(where, "inherit_only"); 799 800 /* 801 * chop off trailing slash, if present 802 */ 803 if (start[strlen(start) - 1] == '/') { 804 start[strlen(start) - 1] = '\0'; 805 where = start + strlen(start); 806 } 807 } 808 if (i < aclcnt - 1) 809 where = strappend(where, ","); 810 } 811 aclexport = dstr->aclexport; 812 free(dstr); 813 return (aclexport); 814 } 815 816 static int 817 build_iflags(char *str, int *iflags) 818 { 819 820 char *tok; 821 *iflags = 0; 822 823 tok = strtok(str, "/"); 824 825 if (tok == NULL) 826 return (1); 827 828 do { 829 if (strcmp(tok, "file_inherit") == 0) 830 *iflags |= ACE_FILE_INHERIT_ACE; 831 else if (strcmp(tok, "dir_inherit") == 0) 832 *iflags |= ACE_DIRECTORY_INHERIT_ACE; 833 else if (strcmp(tok, "inherit_only") == 0) 834 *iflags |= ACE_INHERIT_ONLY_ACE; 835 else if (strcmp(tok, "no_propagate") == 0) 836 *iflags |= ACE_NO_PROPAGATE_INHERIT_ACE; 837 else 838 return (1); 839 } while (tok = strtok(NULL, "/")); 840 return (0); 841 } 842 843 /* 844 * Convert external acl representation to internal representation. 845 * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,] 846 * The comma at the end is not prescribed by the man pages. 847 * But it is needed not to break the old programs. 848 */ 849 850 int 851 ace_aclfromtext(char *aclstr, acl_t **ret_aclp) 852 { 853 char *fieldp; 854 char *tp; 855 char *nextp; 856 char *allocp; 857 char *aclimport; 858 char *str; 859 char *perm_tok; 860 int entry_type; 861 int id; 862 int type; 863 int iflags; 864 int len; 865 int error; 866 int32_t perm; 867 ace_t *tmpaclp; 868 acl_t *aclp; 869 struct group *groupp; 870 struct passwd *passwdp; 871 872 if (! aclstr) 873 return (EACL_INVALID_STR); 874 875 len = strlen(aclstr); 876 877 aclp = acl_alloc(ACE_T); 878 if (aclp == NULL) { 879 return (EACL_MEM_ERROR); 880 } 881 882 *ret_aclp = NULL; 883 884 if ((aclimport = allocp = strdup(aclstr)) == NULL) { 885 return (EACL_MEM_ERROR); 886 } 887 888 889 if (aclimport[len - 1] == ',') 890 aclimport[len - 1] = '\0'; 891 892 for (; aclimport; ) { 893 /* look for an ACL entry */ 894 tp = strchr(aclimport, ','); 895 if (tp == NULL) { 896 nextp = NULL; 897 } else { 898 *tp = '\0'; 899 nextp = tp + 1; 900 } 901 902 aclp->acl_cnt += 1; 903 904 /* 905 * get additional memory: 906 * can be more efficient by allocating a bigger block 907 * each time. 908 */ 909 if (aclp->acl_cnt > 1) 910 tmpaclp = (ace_t *)realloc(aclp->acl_aclp, 911 sizeof (ace_t) * (aclp->acl_cnt)); 912 else 913 tmpaclp = (ace_t *)malloc(sizeof (ace_t)); 914 if (tmpaclp == NULL) { 915 free(allocp); 916 acl_free(aclp); 917 return (EACL_MEM_ERROR); 918 } 919 aclp->acl_aclp = tmpaclp; 920 tmpaclp = (ace_t *)aclp->acl_aclp + (aclp->acl_cnt - 1); 921 922 /* look for entry type field */ 923 tp = strchr(aclimport, ':'); 924 if (tp == NULL) { 925 free(allocp); 926 acl_free(aclp); 927 return (EACL_ENTRY_ERROR); 928 } else 929 *tp = '\0'; 930 if (strcmp(aclimport, "owner@") == 0) { 931 entry_type = ACE_OWNER; 932 } else if (strcmp(aclimport, "group@") == 0) { 933 entry_type = ACE_GROUP | ACE_IDENTIFIER_GROUP; 934 } else if (strcmp(aclimport, "everyone@") == 0) { 935 entry_type = ACE_EVERYONE; 936 } else if (strcmp(aclimport, "group") == 0) { 937 entry_type = ACE_IDENTIFIER_GROUP; 938 } else if (strcmp(aclimport, "user") == 0) { 939 entry_type = 0; 940 } else { 941 free(allocp); 942 acl_free(aclp); 943 return (EACL_ENTRY_ERROR); 944 } 945 946 /* 947 * If not an abstraction owner@, group@ or everyone@ 948 * then we must have a user/group name next 949 */ 950 951 if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) { 952 fieldp = tp + 1; 953 tp = strchr(fieldp, ':'); 954 if (tp == NULL) { 955 free(allocp); 956 acl_free(aclp); 957 return (EACL_INVALID_USER_GROUP); 958 } else 959 *tp = '\0'; 960 if (fieldp != tp) { 961 /* 962 * The second field could be empty. We only care 963 * when the field has user/group name. 964 */ 965 if (entry_type == 0) { 966 /* 967 * The reentrant interface getpwnam_r() 968 * is uncommitted and subject to 969 * change. Use the friendlier interface 970 * getpwnam(). 971 */ 972 error = 0; 973 passwdp = getpwnam(fieldp); 974 if (passwdp == NULL) { 975 error = acl_str_to_id( 976 fieldp, &id); 977 } else { 978 id = passwdp->pw_uid; 979 } 980 981 if (error) { 982 free(allocp); 983 acl_free(aclp); 984 return (error); 985 } 986 } else { 987 error = 0; 988 if (entry_type == 989 ACE_IDENTIFIER_GROUP) { 990 groupp = getgrnam(fieldp); 991 if (groupp == NULL) { 992 /* no group? */ 993 error = acl_str_to_id( 994 fieldp, &id); 995 } else 996 id = groupp->gr_gid; 997 998 } else if ((entry_type == ACE_OWNER) || 999 (entry_type == 1000 (ACE_IDENTIFIER_GROUP|ACE_GROUP)) || 1001 (entry_type != ACE_EVERYONE)) { 1002 error = EACL_FIELD_NOT_BLANK; 1003 } else { 1004 error = EACL_ENTRY_ERROR; 1005 } 1006 1007 if (error) { 1008 free(allocp); 1009 acl_free(aclp); 1010 return (error); 1011 } 1012 } 1013 } 1014 } else { 1015 id = -1; 1016 } 1017 1018 /* next field: permission */ 1019 fieldp = tp + 1; 1020 tp = strchr(fieldp, ':'); 1021 if (tp == NULL) { 1022 free(allocp); 1023 acl_free(aclp); 1024 return (EACL_PERM_MASK_ERROR); 1025 } else 1026 *tp = '\0'; 1027 1028 perm = 0; 1029 1030 perm_tok = strtok(fieldp, "/"); 1031 if (perm_tok == NULL) { 1032 perm = 0; 1033 } else { 1034 do { 1035 if (ace_permask(perm_tok, &perm) != 0) { 1036 free(allocp); 1037 acl_free(aclp); 1038 return (EACL_PERM_MASK_ERROR); 1039 } 1040 } while (perm_tok = strtok(NULL, "/")); 1041 } 1042 1043 /* grab allow/deny */ 1044 fieldp = tp + 1; 1045 tp = strchr(fieldp, ':'); 1046 if (tp != NULL) 1047 *tp = '\0'; 1048 1049 if (strcmp(fieldp, "allow") == 0) 1050 type = ACE_ACCESS_ALLOWED_ACE_TYPE; 1051 else if (strcmp(fieldp, "deny") == 0) 1052 type = ACE_ACCESS_DENIED_ACE_TYPE; 1053 else { 1054 free(allocp); 1055 acl_free(aclp); 1056 return (EACL_INVALID_ACCESS_TYPE); 1057 } 1058 1059 /* grab option inherit flags */ 1060 1061 iflags = 0; 1062 if (tp != NULL) { 1063 fieldp = tp + 1; 1064 if (fieldp != NULL) { 1065 *tp = '\0'; 1066 str = fieldp; 1067 if (build_iflags(str, &iflags) != 0) { 1068 free(allocp); 1069 acl_free(aclp); 1070 return (EACL_INHERIT_ERROR); 1071 } 1072 } else { 1073 free(allocp); 1074 acl_free(aclp); 1075 return (EACL_UNKNOWN_DATA); 1076 } 1077 } 1078 /* slap fields into ace_t structure */ 1079 1080 tmpaclp->a_flags = entry_type; 1081 tmpaclp->a_flags |= iflags; 1082 tmpaclp->a_who = id; 1083 tmpaclp->a_access_mask = perm; 1084 tmpaclp->a_type = type; 1085 aclimport = nextp; 1086 } 1087 free(allocp); 1088 *ret_aclp = aclp; 1089 return (0); 1090 } 1091 1092 char 1093 *acl_totext(acl_t *aclp) 1094 { 1095 if (aclp == NULL) 1096 return (NULL); 1097 1098 switch (aclp->acl_type) { 1099 case ACE_T: 1100 return (ace_acltotext(aclp)); 1101 case ACLENT_T: 1102 return (acltotext(aclp->acl_aclp, aclp->acl_cnt)); 1103 } 1104 return (NULL); 1105 } 1106 1107 int 1108 acl_fromtext(const char *acltextp, acl_t **ret_aclp) 1109 { 1110 acl_t *aclp; 1111 char *token; 1112 char *ptr; 1113 char *textp; 1114 enum acl_type flavor; 1115 int colon_cnt = 0; 1116 int error; 1117 1118 /* 1119 * first try and detect what type of acl entries we have 1120 * 1121 * aclent_t can have 1, 2 or 3 colons 1122 * if 3 then must have word default: 1123 * 1124 * ace_t can have 2, 3 or 4 1125 * for 2 then must be owner@, group@ or everyone@ 1126 */ 1127 1128 textp = strdup(acltextp); 1129 if (textp == NULL) 1130 return (-1); 1131 1132 token = strtok(textp, ","); 1133 if (token == NULL) { 1134 free(textp); 1135 return (-1); 1136 } 1137 1138 for (ptr = token; *ptr; ptr++) { 1139 if (*ptr == ':') 1140 colon_cnt++; 1141 } 1142 1143 if (colon_cnt == 1 || colon_cnt == 2) { 1144 if ((strncmp(acltextp, "owner@", 6) == 0) || 1145 (strncmp(acltextp, "group@", 6) == 0) || 1146 (strncmp(acltextp, "everyone@", 9) == 0)) 1147 flavor = ACE_T; 1148 else 1149 flavor = ACLENT_T; 1150 } else if (colon_cnt == 3) { 1151 ptr = strtok(token, ":"); 1152 if (ptr == NULL) { 1153 free(textp); 1154 return (EACL_MISSING_FIELDS); 1155 } else if (strcmp(ptr, "default") == 0) { 1156 flavor = ACLENT_T; 1157 } else { 1158 flavor = ACE_T; 1159 } 1160 } else if (colon_cnt == 4) { 1161 flavor = ACE_T; 1162 } else { 1163 free(textp); 1164 return (EACL_MISSING_FIELDS); 1165 } 1166 1167 1168 free(textp); 1169 1170 if (flavor == ACLENT_T) 1171 error = aclent_aclfromtext((char *)acltextp, &aclp); 1172 else 1173 error = ace_aclfromtext((char *)acltextp, &aclp); 1174 1175 *ret_aclp = aclp; 1176 return (error); 1177 } 1178