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