]> git.saurik.com Git - apple/libc.git/blame_incremental - posix1e/acl_translate.c
Libc-763.11.tar.gz
[apple/libc.git] / posix1e / acl_translate.c
... / ...
CommitLineData
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 mbr_string_to_uuid(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}