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