]> git.saurik.com Git - apple/libc.git/blob - posix1e/acl_translate.c
3a06fffaa802aef9dd9b58fdf7ec44e9449d5d3b
[apple/libc.git] / posix1e / acl_translate.c
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 */
52 ssize_t
53 acl_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 */
96 ssize_t
97 acl_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 */
143 acl_t
144 acl_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 */
175 acl_t
176 acl_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
208 static 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 {ACL_SYNCHRONIZE, "synchronize", ACL_TYPE_FILE | ACL_TYPE_DIR},
231 {0, NULL, 0}
232 };
233
234 static struct {
235 acl_flag_t flag;
236 char *name;
237 int type;
238 } acl_flags[] = {
239 {ACL_ENTRY_INHERITED, "inherited", ACL_TYPE_FILE | ACL_TYPE_DIR},
240 {ACL_FLAG_DEFER_INHERIT, "defer_inherit", ACL_TYPE_ACL},
241 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_TYPE_DIR},
242 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_TYPE_DIR},
243 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_TYPE_FILE | ACL_TYPE_DIR},
244 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_TYPE_DIR},
245 {ACL_FLAG_NO_INHERIT, "no_inherit", ACL_TYPE_ACL},
246 {0, NULL, 0}
247 };
248
249 /*
250 * reallocing snprintf with offset
251 */
252
253 static int
254 raosnprintf(char **buf, size_t *size, ssize_t *offset, char *fmt, ...)
255 {
256 va_list ap;
257 int ret;
258
259 do
260 {
261 if (*offset < *size)
262 {
263 va_start(ap, fmt);
264 ret = vsnprintf(*buf + *offset, *size - *offset, fmt, ap);
265 va_end(ap);
266 if (ret < (*size - *offset))
267 {
268 *offset += ret;
269 return ret;
270 }
271 }
272 *buf = reallocf(*buf, (*size *= 2));
273 } while (*buf);
274
275 //warn("reallocf failure");
276 return 0;
277 }
278
279 static char *
280 uuid_to_name(uuid_t *uu, uid_t *id, int *isgid)
281 {
282 struct group *tgrp = NULL;
283 struct passwd *tpass = NULL;
284
285 if (0 == mbr_uuid_to_id(*uu, id, isgid))
286 {
287 switch (*isgid)
288 {
289 case ID_TYPE_UID:
290 if (!(tpass = getpwuid(*id)))
291 goto errout;
292 return strdup(tpass->pw_name);
293 break;
294 case ID_TYPE_GID:
295 if (!(tgrp = getgrgid((gid_t) *id)))
296 goto errout;
297 return strdup(tgrp->gr_name);
298 break;
299 default:
300 errout: ; //warn("Unable to translate qualifier on ACL\n");
301 }
302 }
303 return NULL;
304 }
305
306 acl_t
307 acl_from_text(const char *buf_p)
308 {
309 int i, error = 0, need_tag, ug_tag;
310 char *buf, *orig_buf;
311 char *entry, *field, *sub;
312 uuid_t *uu = NULL;
313 struct passwd *tpass = NULL;
314 struct group *tgrp = NULL;
315 acl_entry_t acl_entry;
316 acl_flagset_t flags = NULL;
317 acl_permset_t perms = NULL;
318 acl_tag_t tag;
319 acl_t acl_ret;
320
321 if (buf_p == NULL)
322 {
323 errno = EINVAL;
324 return NULL;
325 }
326
327 if ((buf = strdup(buf_p)) == NULL)
328 return NULL;
329
330 if ((acl_ret = acl_init(1)) == NULL)
331 return NULL;
332
333 orig_buf = buf;
334
335 /* global acl flags
336 * format: !#acl <version> [<flags>]
337 */
338 if ((entry = strsep(&buf, "\n")) != NULL && *entry)
339 {
340 /* field 1: !#acl */
341 field = strsep(&entry, " ");
342 if (*field && strncmp(field, "!#acl", strlen("!#acl")))
343 {
344 error = EINVAL;
345 goto exit;
346 }
347
348 /* field 2: <version>
349 * currently only accepts 1
350 */
351 field = strsep(&entry, " ");
352 errno = 0;
353 if (!*field || strtol(field, NULL, 0) != 1)
354 {
355 error = EINVAL;
356 goto exit;
357 }
358
359 /* field 3: <flags>
360 * optional
361 */
362 if((field = strsep(&entry, " ")) != NULL && *field)
363 {
364 acl_get_flagset_np(acl_ret, &flags);
365 while ((sub = strsep(&field, ",")) && *sub)
366 {
367 for (i = 0; acl_flags[i].name != NULL; ++i)
368 {
369 if (acl_flags[i].type & ACL_TYPE_ACL
370 && !strcmp(acl_flags[i].name, sub))
371 {
372 acl_add_flag_np(flags, acl_flags[i].flag);
373 break;
374 }
375 }
376 if (acl_flags[i].name == NULL)
377 {
378 /* couldn't find flag */
379 error = EINVAL;
380 goto exit;
381 }
382 }
383 }
384 } else {
385 error = EINVAL;
386 goto exit;
387 }
388
389 /* parse each acl line
390 * format: <user|group>:
391 * [<uuid>]:
392 * [<user|group>]:
393 * [<uid|gid>]:
394 * <allow|deny>[,<flags>]
395 * [:<permissions>[,<permissions>]]
396 *
397 * only one of the user/group identifies is required
398 * the first one found is used
399 */
400 while ((entry = strsep(&buf, "\n")) && *entry)
401 {
402 need_tag = 1;
403 ug_tag = -1;
404
405 /* field 1: <user|group> */
406 field = strsep(&entry, ":");
407
408 if(uu)
409 bzero(uu, sizeof(uuid_t));
410 else if((uu = calloc(1, sizeof(uuid_t))) == NULL)
411 {
412 error = errno;
413 goto exit;
414 }
415
416 if(acl_create_entry(&acl_ret, &acl_entry))
417 {
418 error = errno;
419 goto exit;
420 }
421
422 if (-1 == acl_get_flagset_np(acl_entry, &flags)
423 || -1 == acl_get_permset(acl_entry, &perms))
424 {
425 error = errno;
426 goto exit;
427 }
428
429 switch(*field)
430 {
431 case 'u':
432 if(!strcmp(field, "user"))
433 ug_tag = ID_TYPE_UID;
434 break;
435 case 'g':
436 if(!strcmp(field, "group"))
437 ug_tag = ID_TYPE_GID;
438 break;
439 default:
440 error = EINVAL;
441 goto exit;
442 }
443
444 /* field 2: <uuid> */
445 if ((field = strsep(&entry, ":")) != NULL && *field)
446 {
447 uuid_parse(field, *uu);
448 need_tag = 0;
449 }
450
451 /* field 3: <username|groupname> */
452 if ((field = strsep(&entry, ":")) != NULL && *field && need_tag)
453 {
454 switch(ug_tag)
455 {
456 case ID_TYPE_UID:
457 if((tpass = getpwnam(field)) != NULL)
458 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0)
459 {
460 error = EINVAL;
461 goto exit;
462 }
463 break;
464 case ID_TYPE_GID:
465 if ((tgrp = getgrnam(field)) != NULL)
466 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0)
467 {
468 error = EINVAL;
469 goto exit;
470 }
471 break;
472 default:
473 error = EINVAL;
474 goto exit;
475 }
476 need_tag = 0;
477 }
478
479 /* field 4: <uid|gid> */
480 if ((field = strsep(&entry, ":")) != NULL && *field && need_tag)
481 {
482 uid_t id;
483 error = 0;
484
485 if((id = strtol(field, NULL, 10)) == 0 && error)
486 {
487 error = EINVAL;
488 goto exit;
489 }
490
491 switch(ug_tag)
492 {
493 case ID_TYPE_UID:
494 if((tpass = getpwuid((uid_t)id)) != NULL)
495 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0)
496 {
497 error = EINVAL;
498 goto exit;
499 }
500 break;
501 case ID_TYPE_GID:
502 if ((tgrp = getgrgid((gid_t)id)) != NULL)
503 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0)
504 {
505 error = EINVAL;
506 goto exit;
507 }
508 break;
509 }
510 need_tag = 0;
511 }
512
513 /* sanity check: nothing set as qualifier */
514 if (need_tag)
515 {
516 error = EINVAL;
517 goto exit;
518 }
519
520 /* field 5: <flags> */
521 if((field = strsep(&entry, ":")) == NULL || !*field)
522 {
523 error = EINVAL;
524 goto exit;
525 }
526
527 for (tag = 0; (sub = strsep(&field, ",")) && *sub;)
528 {
529 if (!tag)
530 {
531 if (!strcmp(sub, "allow"))
532 tag = ACL_EXTENDED_ALLOW;
533 else if (!strcmp(sub, "deny"))
534 tag = ACL_EXTENDED_DENY;
535 else {
536 error = EINVAL;
537 goto exit;
538 }
539 continue;
540 }
541
542 for (i = 0; acl_flags[i].name != NULL; ++i)
543 {
544 if (acl_flags[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR)
545 && !strcmp(acl_flags[i].name, sub))
546 {
547 acl_add_flag_np(flags, acl_flags[i].flag);
548 break;
549 }
550 }
551 if (acl_flags[i].name == NULL)
552 {
553 /* couldn't find perm */
554 error = EINVAL;
555 goto exit;
556 }
557 }
558
559 /* field 6: <perms> (can be empty) */
560 if((field = strsep(&entry, ":")) != NULL && *field)
561 {
562 while ((sub = strsep(&field, ",")) && *sub)
563 {
564 for (i = 0; acl_perms[i].name != NULL; i++)
565 {
566 if (acl_perms[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR)
567 && !strcmp(acl_perms[i].name, sub))
568 {
569 acl_add_perm(perms, acl_perms[i].perm);
570 break;
571 }
572 }
573 if (acl_perms[i].name == NULL)
574 {
575 /* couldn't find perm */
576 error = EINVAL;
577 goto exit;
578 }
579 }
580 }
581 acl_set_tag_type(acl_entry, tag);
582 acl_set_qualifier(acl_entry, *uu);
583 }
584 exit:
585 if(uu)
586 free(uu);
587 free(orig_buf);
588 if (error)
589 {
590 acl_free(acl_ret);
591 acl_ret = NULL;
592 errno = error;
593 }
594 return acl_ret;
595 }
596
597 char *
598 acl_to_text(acl_t acl, ssize_t *len_p)
599 {
600 acl_tag_t tag;
601 acl_entry_t entry = NULL;
602 acl_flagset_t flags;
603 acl_permset_t perms;
604 uid_t id;
605 int i, first;
606 int isgid;
607 size_t bufsize = 1024;
608 char *buf = NULL;
609
610 if (!_ACL_VALID_ACL(acl)) {
611 errno = EINVAL;
612 return NULL;
613 }
614
615 if (len_p == NULL)
616 if ((len_p = alloca(sizeof(ssize_t))) == NULL)
617 goto err_nomem;
618
619 *len_p = 0;
620
621 if ((buf = malloc(bufsize)) == NULL)
622 goto err_nomem;
623
624 if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1))
625 goto err_nomem;
626
627 if (acl_get_flagset_np(acl, &flags) == 0)
628 {
629 for (i = 0, first = 0; acl_flags[i].name != NULL; ++i)
630 {
631 if (acl_flags[i].type & ACL_TYPE_ACL
632 && acl_get_flag_np(flags, acl_flags[i].flag) != 0)
633 {
634 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s",
635 first++ ? "," : " ", acl_flags[i].name))
636 goto err_nomem;
637 }
638 }
639 }
640 for (;acl_get_entry(acl,
641 entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;)
642 {
643 int valid;
644 uuid_t *uu;
645 char *str, uu_str[37];
646
647 if (((uu = (uuid_t *) acl_get_qualifier(entry)) == NULL)
648 || (acl_get_tag_type(entry, &tag) != 0)
649 || (acl_get_flagset_np(entry, &flags) != 0)
650 || (acl_get_permset(entry, &perms) != 0)) {
651 if (uu != NULL) acl_free(uu);
652 continue;
653 }
654
655 uuid_unparse_upper(*uu, uu_str);
656
657 if ((str = uuid_to_name(uu, &id, &isgid)) != NULL) {
658 valid = raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s",
659 isgid ? "group" : "user",
660 uu_str,
661 str,
662 id,
663 (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny");
664 } else {
665 valid = raosnprintf(&buf, &bufsize, len_p, "\nuser:%s:::%s",
666 uu_str,
667 (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny");
668 }
669
670 free(str);
671 acl_free(uu);
672
673 if (!valid)
674 goto err_nomem;
675
676 for (i = 0; acl_flags[i].name != NULL; ++i)
677 {
678 if (acl_flags[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE))
679 {
680 if(acl_get_flag_np(flags, acl_flags[i].flag) != 0)
681 {
682 if(!raosnprintf(&buf, &bufsize, len_p, ",%s",
683 acl_flags[i].name))
684 goto err_nomem;
685 }
686 }
687 }
688
689 for (i = 0, first = 0; acl_perms[i].name != NULL; ++i)
690 {
691 if (acl_perms[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE))
692 {
693 if(acl_get_perm_np(perms, acl_perms[i].perm) != 0)
694 {
695 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s",
696 first++ ? "," : ":", acl_perms[i].name))
697 goto err_nomem;
698 }
699 }
700 }
701 }
702 buf[(*len_p)++] = '\n';
703 buf[(*len_p)] = 0;
704 return buf;
705
706 err_nomem:
707 if (buf != NULL)
708 free(buf);
709
710 errno = ENOMEM;
711 return NULL;
712 }
713
714 ssize_t
715 acl_size(acl_t acl)
716 {
717 /* special case for _FILESEC_REMOVE_ACL */
718 if (acl == (acl_t)_FILESEC_REMOVE_ACL)
719 return KAUTH_FILESEC_SIZE(0);
720
721 _ACL_VALIDATE_ACL(acl);
722
723 return(KAUTH_FILESEC_SIZE(acl->a_entries));
724 }