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