1/* 2 * Copyright (c) 2004, 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <sys/appleapiopts.h> 25#include <sys/types.h> 26#include <sys/acl.h> 27#include <sys/fcntl.h> 28#include <errno.h> 29#include <stdio.h> 30#include <stdarg.h> 31#include <stdlib.h> 32#include <string.h> 33#include <strings.h> 34#include <membership.h> 35#include <membershipPriv.h> 36#include <pwd.h> 37#include <grp.h> 38 39#include <libkern/OSByteOrder.h> 40 41#include "aclvar.h" 42 43/* 44 * NOTE: the copy_int/copy_ext functions are duplicated here, one version of each for 45 * each of native and portable endianity. A more elegant solution might be called for 46 * if the functions become much more complicated. 47 */ 48 49/* 50 * acl_t -> external representation, portable endianity 51 */ 52ssize_t 53acl_copy_ext(void *buf, acl_t acl, ssize_t size) 54{ 55 struct kauth_filesec *ext = (struct kauth_filesec *)buf; 56 ssize_t reqsize; 57 int i; 58 59 /* validate arguments, compute required size */ 60 reqsize = acl_size(acl); 61 if (reqsize < 0) 62 return(-1); 63 if (reqsize > size) { 64 errno = ERANGE; 65 return(-1); 66 } 67 68 bzero(ext, reqsize); 69 ext->fsec_magic = OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC); 70 71 /* special case for _FILESEC_REMOVE_ACL */ 72 if (acl == (acl_t)_FILESEC_REMOVE_ACL) { 73 ext->fsec_entrycount = OSSwapHostToBigInt32(KAUTH_FILESEC_NOACL); 74 return(reqsize); 75 } 76 77 /* export the header */ 78 ext->fsec_entrycount = OSSwapHostToBigInt32(acl->a_entries); 79 ext->fsec_flags = OSSwapHostToBigInt32(acl->a_flags); 80 81 /* copy ACEs */ 82 for (i = 0; i < acl->a_entries; i++) { 83 /* ACE contents are almost identical */ 84 ext->fsec_ace[i].ace_applicable = acl->a_ace[i].ae_applicable; 85 ext->fsec_ace[i].ace_flags = 86 OSSwapHostToBigInt32((acl->a_ace[i].ae_tag & KAUTH_ACE_KINDMASK) | (acl->a_ace[i].ae_flags & ~KAUTH_ACE_KINDMASK)); 87 ext->fsec_ace[i].ace_rights = OSSwapHostToBigInt32(acl->a_ace[i].ae_perms); 88 } 89 90 return(reqsize); 91} 92 93/* 94 * acl_t -> external representation, native system endianity 95 */ 96ssize_t 97acl_copy_ext_native(void *buf, acl_t acl, ssize_t size) 98{ 99 struct kauth_filesec *ext = (struct kauth_filesec *)buf; 100 ssize_t reqsize; 101 int i; 102 103 /* validate arguments, compute required size */ 104 reqsize = acl_size(acl); 105 if (reqsize < 0) 106 return(-1); 107 if (reqsize > size) { 108 errno = ERANGE; 109 return(-1); 110 } 111 112 bzero(ext, reqsize); 113 ext->fsec_magic = KAUTH_FILESEC_MAGIC; 114 115 /* special case for _FILESEC_REMOVE_ACL */ 116 if (acl == (acl_t)_FILESEC_REMOVE_ACL) { 117 ext->fsec_entrycount = KAUTH_FILESEC_NOACL; 118 return(reqsize); 119 } 120 121 /* export the header */ 122 ext->fsec_entrycount = acl->a_entries; 123 ext->fsec_flags = acl->a_flags; 124 125 /* copy ACEs */ 126 for (i = 0; i < acl->a_entries; i++) { 127 /* ACE contents are almost identical */ 128 ext->fsec_ace[i].ace_applicable = acl->a_ace[i].ae_applicable; 129 ext->fsec_ace[i].ace_flags = 130 (acl->a_ace[i].ae_tag & KAUTH_ACE_KINDMASK) | 131 (acl->a_ace[i].ae_flags & ~KAUTH_ACE_KINDMASK); 132 ext->fsec_ace[i].ace_rights = acl->a_ace[i].ae_perms; 133 } 134 135 return(reqsize); 136} 137 138/* 139 * external representation, portable system endianity -> acl_t 140 * 141 * Unlike acl_copy_ext, we can't mung the buffer as it doesn't belong to us. 142 */ 143acl_t 144acl_copy_int(const void *buf) 145{ 146 struct kauth_filesec *ext = (struct kauth_filesec *)buf; 147 acl_t ap; 148 int i; 149 150 if (ext->fsec_magic != OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC)) { 151 errno = EINVAL; 152 return(NULL); 153 } 154 155 if ((ap = acl_init(OSSwapBigToHostInt32(ext->fsec_entrycount))) != NULL) { 156 /* copy useful header fields */ 157 ap->a_flags = OSSwapBigToHostInt32(ext->fsec_flags); 158 ap->a_entries = OSSwapBigToHostInt32(ext->fsec_entrycount); 159 /* copy ACEs */ 160 for (i = 0; i < ap->a_entries; i++) { 161 /* ACE contents are literally identical */ 162 ap->a_ace[i].ae_magic = _ACL_ENTRY_MAGIC; 163 ap->a_ace[i].ae_applicable = ext->fsec_ace[i].ace_applicable; 164 ap->a_ace[i].ae_flags = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_flags) & ~KAUTH_ACE_KINDMASK; 165 ap->a_ace[i].ae_tag = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_flags) & KAUTH_ACE_KINDMASK; 166 ap->a_ace[i].ae_perms = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_rights); 167 } 168 } 169 return(ap); 170} 171 172/* 173 * external representation, native system endianity -> acl_t 174 */ 175acl_t 176acl_copy_int_native(const void *buf) 177{ 178 struct kauth_filesec *ext = (struct kauth_filesec *)buf; 179 acl_t ap; 180 int i; 181 182 if (ext->fsec_magic != KAUTH_FILESEC_MAGIC) { 183 errno = EINVAL; 184 return(NULL); 185 } 186 187 if ((ap = acl_init(ext->fsec_entrycount)) != NULL) { 188 /* copy useful header fields */ 189 ap->a_flags = ext->fsec_flags; 190 ap->a_entries = ext->fsec_entrycount; 191 /* copy ACEs */ 192 for (i = 0; i < ap->a_entries; i++) { 193 /* ACE contents are literally identical */ 194 ap->a_ace[i].ae_magic = _ACL_ENTRY_MAGIC; 195 ap->a_ace[i].ae_applicable = ext->fsec_ace[i].ace_applicable; 196 ap->a_ace[i].ae_flags = ext->fsec_ace[i].ace_flags & ~KAUTH_ACE_KINDMASK; 197 ap->a_ace[i].ae_tag = ext->fsec_ace[i].ace_flags & KAUTH_ACE_KINDMASK; 198 ap->a_ace[i].ae_perms = ext->fsec_ace[i].ace_rights; 199 } 200 } 201 return(ap); 202} 203 204#define ACL_TYPE_DIR (1<<0) 205#define ACL_TYPE_FILE (1<<1) 206#define ACL_TYPE_ACL (1<<2) 207 208static struct { 209 acl_perm_t perm; 210 char *name; 211 int type; 212} acl_perms[] = { 213 {ACL_READ_DATA, "read", ACL_TYPE_FILE}, 214// {ACL_LIST_DIRECTORY, "list", ACL_TYPE_DIR}, 215 {ACL_WRITE_DATA, "write", ACL_TYPE_FILE}, 216// {ACL_ADD_FILE, "add_file", ACL_TYPE_DIR}, 217 {ACL_EXECUTE, "execute", ACL_TYPE_FILE}, 218// {ACL_SEARCH, "search", ACL_TYPE_DIR}, 219 {ACL_DELETE, "delete", ACL_TYPE_FILE | ACL_TYPE_DIR}, 220 {ACL_APPEND_DATA, "append", ACL_TYPE_FILE}, 221// {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_TYPE_DIR}, 222 {ACL_DELETE_CHILD, "delete_child", ACL_TYPE_DIR}, 223 {ACL_READ_ATTRIBUTES, "readattr", ACL_TYPE_FILE | ACL_TYPE_DIR}, 224 {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_TYPE_FILE | ACL_TYPE_DIR}, 225 {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_TYPE_FILE | ACL_TYPE_DIR}, 226 {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_TYPE_FILE | ACL_TYPE_DIR}, 227 {ACL_READ_SECURITY, "readsecurity", ACL_TYPE_FILE | ACL_TYPE_DIR}, 228 {ACL_WRITE_SECURITY, "writesecurity", ACL_TYPE_FILE | ACL_TYPE_DIR}, 229 {ACL_CHANGE_OWNER, "chown", ACL_TYPE_FILE | ACL_TYPE_DIR}, 230 {0, NULL, 0} 231}; 232 233static struct { 234 acl_flag_t flag; 235 char *name; 236 int type; 237} acl_flags[] = { 238 {ACL_ENTRY_INHERITED, "inherited", ACL_TYPE_FILE | ACL_TYPE_DIR}, 239 {ACL_FLAG_DEFER_INHERIT, "defer_inherit", ACL_TYPE_ACL}, 240 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_TYPE_DIR}, 241 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_TYPE_DIR}, 242 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_TYPE_FILE | ACL_TYPE_DIR}, 243 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_TYPE_DIR}, 244 {ACL_FLAG_NO_INHERIT, "no_inherit", ACL_TYPE_ACL}, 245 {0, NULL, 0} 246}; 247 248/* 249 * reallocing snprintf with offset 250 */ 251 252static int 253raosnprintf(char **buf, size_t *size, ssize_t *offset, char *fmt, ...) 254{ 255 va_list ap; 256 int ret; 257 258 do 259 { 260 if (*offset < *size) 261 { 262 va_start(ap, fmt); 263 ret = vsnprintf(*buf + *offset, *size - *offset, fmt, ap); 264 va_end(ap); 265 if (ret < (*size - *offset)) 266 { 267 *offset += ret; 268 return ret; 269 } 270 } 271 *buf = reallocf(*buf, (*size *= 2)); 272 } while (*buf); 273 274 //warn("reallocf failure"); 275 return 0; 276} 277 278static char * 279uuid_to_name(uuid_t *uu, uid_t *id, int *isgid) 280{ 281 struct group *tgrp = NULL; 282 struct passwd *tpass = NULL; 283 284 if (0 == mbr_uuid_to_id(*uu, id, isgid)) 285 { 286 switch (*isgid) 287 { 288 case ID_TYPE_UID: 289 if (!(tpass = getpwuid(*id))) 290 goto errout; 291 return strdup(tpass->pw_name); 292 break; 293 case ID_TYPE_GID: 294 if (!(tgrp = getgrgid((gid_t) *id))) 295 goto errout; 296 return strdup(tgrp->gr_name); 297 break; 298 default: 299errout: ; //warn("Unable to translate qualifier on ACL\n"); 300 } 301 } 302 return NULL; 303} 304 305acl_t 306acl_from_text(const char *buf_p) 307{ 308 int i, error = 0, need_tag, ug_tag; 309 char *buf, *orig_buf; 310 char *entry, *field, *sub; 311 uuid_t *uu = NULL; 312 struct passwd *tpass = NULL; 313 struct group *tgrp = NULL; 314 acl_entry_t acl_entry; 315 acl_flagset_t flags = NULL; 316 acl_permset_t perms = NULL; 317 acl_tag_t tag; 318 acl_t acl_ret; 319 320 if (buf_p == NULL) 321 { 322 errno = EINVAL; 323 return NULL; 324 } 325 326 if ((buf = strdup(buf_p)) == NULL) 327 return NULL; 328 329 if ((acl_ret = acl_init(1)) == NULL) 330 return NULL; 331 332 orig_buf = buf; 333 334 /* global acl flags 335 * format: !#acl <version> [<flags>] 336 */ 337 if ((entry = strsep(&buf, "\n")) != NULL && *entry) 338 { 339 /* field 1: !#acl */ 340 field = strsep(&entry, " "); 341 if (*field && strncmp(field, "!#acl", strlen("!#acl"))) 342 { 343 error = EINVAL; 344 goto exit; 345 } 346 347 /* field 2: <version> 348 * currently only accepts 1 349 */ 350 field = strsep(&entry, " "); 351 errno = 0; 352 if (!*field || strtol(field, NULL, 0) != 1) 353 { 354 error = EINVAL; 355 goto exit; 356 } 357 358 /* field 3: <flags> 359 * optional 360 */ 361 if((field = strsep(&entry, " ")) != NULL && *field) 362 { 363 acl_get_flagset_np(acl_ret, &flags); 364 while ((sub = strsep(&field, ",")) && *sub) 365 { 366 for (i = 0; acl_flags[i].name != NULL; ++i) 367 { 368 if (acl_flags[i].type & ACL_TYPE_ACL 369 && !strcmp(acl_flags[i].name, sub)) 370 { 371 acl_add_flag_np(flags, acl_flags[i].flag); 372 break; 373 } 374 } 375 if (acl_flags[i].name == NULL) 376 { 377 /* couldn't find flag */ 378 error = EINVAL; 379 goto exit; 380 } 381 } 382 } 383 } else { 384 error = EINVAL; 385 goto exit; 386 } 387 388 /* parse each acl line 389 * format: <user|group>: 390 * [<uuid>]: 391 * [<user|group>]: 392 * [<uid|gid>]: 393 * <allow|deny>[,<flags>] 394 * [:<permissions>[,<permissions>]] 395 * 396 * only one of the user/group identifies is required 397 * the first one found is used 398 */ 399 while ((entry = strsep(&buf, "\n")) && *entry) 400 { 401 need_tag = 1; 402 ug_tag = -1; 403 404 /* field 1: <user|group> */ 405 field = strsep(&entry, ":"); 406 407 if(uu) 408 bzero(uu, sizeof(uuid_t)); 409 else if((uu = calloc(1, sizeof(uuid_t))) == NULL) 410 { 411 error = errno; 412 goto exit; 413 } 414 415 if(acl_create_entry(&acl_ret, &acl_entry)) 416 { 417 error = errno; 418 goto exit; 419 } 420 421 if (-1 == acl_get_flagset_np(acl_entry, &flags) 422 || -1 == acl_get_permset(acl_entry, &perms)) 423 { 424 error = errno; 425 goto exit; 426 } 427 428 switch(*field) 429 { 430 case 'u': 431 if(!strcmp(field, "user")) 432 ug_tag = ID_TYPE_UID; 433 break; 434 case 'g': 435 if(!strcmp(field, "group")) 436 ug_tag = ID_TYPE_GID; 437 break; 438 default: 439 error = EINVAL; 440 goto exit; 441 } 442 443 /* field 2: <uuid> */ 444 if ((field = strsep(&entry, ":")) != NULL && *field) 445 { 446 uuid_parse(field, *uu); 447 need_tag = 0; 448 } 449 450 /* field 3: <username|groupname> */ 451 if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) 452 { 453 switch(ug_tag) 454 { 455 case ID_TYPE_UID: 456 if((tpass = getpwnam(field)) != NULL) 457 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0) 458 { 459 error = EINVAL; 460 goto exit; 461 } 462 break; 463 case ID_TYPE_GID: 464 if ((tgrp = getgrnam(field)) != NULL) 465 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0) 466 { 467 error = EINVAL; 468 goto exit; 469 } 470 break; 471 default: 472 error = EINVAL; 473 goto exit; 474 } 475 need_tag = 0; 476 } 477 478 /* field 4: <uid|gid> */ 479 if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) 480 { 481 uid_t id; 482 error = 0; 483 484 if((id = strtol(field, NULL, 10)) == 0 && error) 485 { 486 error = EINVAL; 487 goto exit; 488 } 489 490 switch(ug_tag) 491 { 492 case ID_TYPE_UID: 493 if((tpass = getpwuid((uid_t)id)) != NULL) 494 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0) 495 { 496 error = EINVAL; 497 goto exit; 498 } 499 break; 500 case ID_TYPE_GID: 501 if ((tgrp = getgrgid((gid_t)id)) != NULL) 502 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0) 503 { 504 error = EINVAL; 505 goto exit; 506 } 507 break; 508 } 509 need_tag = 0; 510 } 511 512 /* sanity check: nothing set as qualifier */ 513 if (need_tag) 514 { 515 error = EINVAL; 516 goto exit; 517 } 518 519 /* field 5: <flags> */ 520 if((field = strsep(&entry, ":")) == NULL || !*field) 521 { 522 error = EINVAL; 523 goto exit; 524 } 525 526 for (tag = 0; (sub = strsep(&field, ",")) && *sub;) 527 { 528 if (!tag) 529 { 530 if (!strcmp(sub, "allow")) 531 tag = ACL_EXTENDED_ALLOW; 532 else if (!strcmp(sub, "deny")) 533 tag = ACL_EXTENDED_DENY; 534 else { 535 error = EINVAL; 536 goto exit; 537 } 538 continue; 539 } 540 541 for (i = 0; acl_flags[i].name != NULL; ++i) 542 { 543 if (acl_flags[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR) 544 && !strcmp(acl_flags[i].name, sub)) 545 { 546 acl_add_flag_np(flags, acl_flags[i].flag); 547 break; 548 } 549 } 550 if (acl_flags[i].name == NULL) 551 { 552 /* couldn't find perm */ 553 error = EINVAL; 554 goto exit; 555 } 556 } 557 558 /* field 6: <perms> (can be empty) */ 559 if((field = strsep(&entry, ":")) != NULL && *field) 560 { 561 while ((sub = strsep(&field, ",")) && *sub) 562 { 563 for (i = 0; acl_perms[i].name != NULL; i++) 564 { 565 if (acl_perms[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR) 566 && !strcmp(acl_perms[i].name, sub)) 567 { 568 acl_add_perm(perms, acl_perms[i].perm); 569 break; 570 } 571 } 572 if (acl_perms[i].name == NULL) 573 { 574 /* couldn't find perm */ 575 error = EINVAL; 576 goto exit; 577 } 578 } 579 } 580 acl_set_tag_type(acl_entry, tag); 581 acl_set_qualifier(acl_entry, *uu); 582 } 583exit: 584 if(uu) 585 free(uu); 586 free(orig_buf); 587 if (error) 588 { 589 acl_free(acl_ret); 590 acl_ret = NULL; 591 errno = error; 592 } 593 return acl_ret; 594} 595 596char * 597acl_to_text(acl_t acl, ssize_t *len_p) 598{ 599 acl_tag_t tag; 600 acl_entry_t entry = NULL; 601 acl_flagset_t flags; 602 acl_permset_t perms; 603 uid_t id; 604 int i, first; 605 int isgid; 606 size_t bufsize = 1024; 607 char *buf = NULL; 608 609 if (!_ACL_VALID_ACL(acl)) { 610 errno = EINVAL; 611 return NULL; 612 } 613 614 if (len_p == NULL) 615 if ((len_p = alloca(sizeof(ssize_t))) == NULL) 616 goto err_nomem; 617 618 *len_p = 0; 619 620 if ((buf = malloc(bufsize)) == NULL) 621 goto err_nomem; 622 623 if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1)) 624 goto err_nomem; 625 626 if (acl_get_flagset_np(acl, &flags) == 0) 627 { 628 for (i = 0, first = 0; acl_flags[i].name != NULL; ++i) 629 { 630 if (acl_flags[i].type & ACL_TYPE_ACL 631 && acl_get_flag_np(flags, acl_flags[i].flag) != 0) 632 { 633 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", 634 first++ ? "," : " ", acl_flags[i].name)) 635 goto err_nomem; 636 } 637 } 638 } 639 for (;acl_get_entry(acl, 640 entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;) 641 { 642 int valid; 643 uuid_t *uu; 644 char *str, uu_str[37]; 645 646 if (((uu = (uuid_t *) acl_get_qualifier(entry)) == NULL) 647 || (acl_get_tag_type(entry, &tag) != 0) 648 || (acl_get_flagset_np(entry, &flags) != 0) 649 || (acl_get_permset(entry, &perms) != 0)) { 650 if (uu != NULL) acl_free(uu); 651 continue; 652 } 653 654 uuid_unparse_upper(*uu, uu_str); 655 656 if ((str = uuid_to_name(uu, &id, &isgid)) != NULL) { 657 valid = raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s", 658 isgid ? "group" : "user", 659 uu_str, 660 str, 661 id, 662 (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); 663 } else { 664 valid = raosnprintf(&buf, &bufsize, len_p, "\nuser:%s:::%s", 665 uu_str, 666 (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); 667 } 668 669 free(str); 670 acl_free(uu); 671 672 if (!valid) 673 goto err_nomem; 674 675 for (i = 0; acl_flags[i].name != NULL; ++i) 676 { 677 if (acl_flags[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE)) 678 { 679 if(acl_get_flag_np(flags, acl_flags[i].flag) != 0) 680 { 681 if(!raosnprintf(&buf, &bufsize, len_p, ",%s", 682 acl_flags[i].name)) 683 goto err_nomem; 684 } 685 } 686 } 687 688 for (i = 0, first = 0; acl_perms[i].name != NULL; ++i) 689 { 690 if (acl_perms[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE)) 691 { 692 if(acl_get_perm_np(perms, acl_perms[i].perm) != 0) 693 { 694 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", 695 first++ ? "," : ":", acl_perms[i].name)) 696 goto err_nomem; 697 } 698 } 699 } 700 } 701 buf[(*len_p)++] = '\n'; 702 buf[(*len_p)] = 0; 703 return buf; 704 705err_nomem: 706 if (buf != NULL) 707 free(buf); 708 709 errno = ENOMEM; 710 return NULL; 711} 712 713ssize_t 714acl_size(acl_t acl) 715{ 716 /* special case for _FILESEC_REMOVE_ACL */ 717 if (acl == (acl_t)_FILESEC_REMOVE_ACL) 718 return KAUTH_FILESEC_SIZE(0); 719 720 _ACL_VALIDATE_ACL(acl); 721 722 return(KAUTH_FILESEC_SIZE(acl->a_entries)); 723} 724