]> git.saurik.com Git - apple/libc.git/blob - posix1e/acl_translate.c
Libc-391.2.10.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 <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_ENTRY_INHERITED, "inherited", ACL_TYPE_FILE | ACL_TYPE_DIR},
140 {ACL_FLAG_DEFER_INHERIT, "defer_inherit", ACL_TYPE_ACL},
141 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_TYPE_DIR},
142 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_TYPE_DIR},
143 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_TYPE_FILE | ACL_TYPE_DIR},
144 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_TYPE_DIR},
145 {0, NULL, 0}
146 };
147
148 /*
149 * reallocing snprintf with offset
150 */
151
152 static int
153 raosnprintf(char **buf, size_t *size, ssize_t *offset, char *fmt, ...)
154 {
155 va_list ap;
156 int ret;
157
158 do
159 {
160 if (*offset < *size)
161 {
162 va_start(ap, fmt);
163 ret = vsnprintf(*buf + *offset, *size - *offset, fmt, ap);
164 va_end(ap);
165 if (ret < (*size - *offset))
166 {
167 *offset += ret;
168 return ret;
169 }
170 }
171 *buf = reallocf(*buf, (*size *= 2));
172 } while (*buf);
173
174 //warn("reallocf failure");
175 return 0;
176 }
177
178 static char *
179 uuid_to_name(uuid_t *uu, uid_t *id, int *isgid)
180 {
181 struct group *tgrp = NULL;
182 struct passwd *tpass = NULL;
183
184 if (0 == mbr_uuid_to_id(*uu, id, isgid))
185 {
186 switch (*isgid)
187 {
188 case ID_TYPE_UID:
189 if (!(tpass = getpwuid(*id)))
190 goto errout;
191 return strdup(tpass->pw_name);
192 break;
193 case ID_TYPE_GID:
194 if (!(tgrp = getgrgid((gid_t) *id)))
195 goto errout;
196 return strdup(tgrp->gr_name);
197 break;
198 default:
199 errout: ; //warn("Unable to translate qualifier on ACL\n");
200 }
201 }
202 return strdup("");
203 }
204
205 acl_t
206 acl_from_text(const char *buf_p)
207 {
208 int i, error = 0, need_tag = 1, ug_tag = -1;
209 char *buf;
210 char *entry, *field, *sub,
211 *last_field, *last_entry, *last_sub;
212 uuid_t *uu;
213 struct passwd *tpass = NULL;
214 struct group *tgrp = NULL;
215 acl_entry_t acl_entry;
216 acl_flagset_t flags = NULL;
217 acl_permset_t perms = NULL;
218 acl_tag_t tag;
219 acl_t acl_ret;
220
221 if ((acl_ret = acl_init(1)) == NULL)
222 return NULL;
223
224 if (buf_p == NULL)
225 return NULL;
226
227 if ((buf = strdup(buf_p)) == NULL)
228 return NULL;
229
230 /* acl flags */
231 if ((entry = strtok_r(buf, "\n", &last_entry)) != NULL)
232 {
233 /* stamp */
234 field = strtok_r(entry, " ", &last_field);
235 if (field && strncmp(field, "!#acl", strlen("!#acl")))
236 {
237 error = EINVAL;
238 goto exit;
239 }
240
241 /* version */
242 field = strtok_r(NULL, " ", &last_field);
243 errno = 0;
244 if (field == NULL || strtol(field, NULL, 0) != 1)
245 {
246 error = EINVAL;
247 goto exit;
248 }
249
250 /* optional flags */
251 if((field = strtok_r(NULL, " ", &last_field)) != NULL)
252 {
253 acl_get_flagset_np(acl_ret, &flags);
254 for (sub = strtok_r(field, ",", &last_sub); sub;
255 sub = strtok_r(NULL, ",", &last_sub))
256 {
257 for (i = 0; acl_flags[i].name != NULL; ++i)
258 {
259 if (acl_flags[i].type & ACL_TYPE_ACL
260 && !strcmp(acl_flags[i].name, sub))
261 {
262 acl_add_flag_np(flags, acl_flags[i].flag);
263 break;
264 }
265 }
266 if (acl_flags[i].name == NULL)
267 {
268 /* couldn't find flag */
269 error = EINVAL;
270 goto exit;
271 }
272 }
273 }
274 }
275
276 for (entry = strtok_r(NULL, "\n", &last_entry); entry;
277 entry = strtok_r(NULL, "\n", &last_entry))
278 {
279 field = strtok_r(entry, ":", &last_field);
280
281 if((uu = calloc(1, sizeof(uuid_t))) == NULL)
282 goto exit;
283
284 if(acl_create_entry(&acl_ret, &acl_entry))
285 goto exit;
286
287 acl_get_flagset_np(acl_entry, &flags);
288 acl_get_permset(acl_entry, &perms);
289
290 switch(*field)
291 {
292 case 'u':
293 if(!strncmp(buf, "user", strlen(field)))
294 ug_tag = ID_TYPE_UID;
295 break;
296 case 'g':
297 if(!strncmp(buf, "group", strlen(field)))
298 ug_tag = ID_TYPE_GID;
299 break;
300
301 }
302
303 /* uuid */
304 if ((field = strtok_r(NULL, ":", &last_field)) != NULL)
305 {
306 mbr_string_to_uuid(field, *uu);
307 need_tag = 0;
308 }
309 /* name */
310 if (*last_field == ':') // empty username field
311 last_field++;
312 else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag)
313 {
314 switch(ug_tag)
315 {
316 case ID_TYPE_UID:
317 if((tpass = getpwnam(field)) != NULL)
318 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0)
319 {
320 error = EINVAL;
321 goto exit;
322 }
323 break;
324 case ID_TYPE_GID:
325 if ((tgrp = getgrnam(field)) != NULL)
326 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0)
327 {
328 error = EINVAL;
329 goto exit;
330 }
331 break;
332 }
333 need_tag = 0;
334 }
335 /* uid */
336 if (*last_field == ':') // empty uid field
337 last_field++;
338 else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag)
339 {
340 uid_t id;
341 error = 0;
342
343 if((id = strtol(field, NULL, 10)) == 0 && error)
344 {
345 error = EINVAL;
346 goto exit;
347 }
348
349 switch(ug_tag)
350 {
351 case ID_TYPE_UID:
352 if((tpass = getpwuid((uid_t)id)) != NULL)
353 if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0)
354 {
355 error = EINVAL;
356 goto exit;
357 }
358 break;
359 case ID_TYPE_GID:
360 if ((tgrp = getgrgid((gid_t)id)) != NULL)
361 if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0)
362 {
363 error = EINVAL;
364 goto exit;
365 }
366 break;
367 }
368 need_tag = 0;
369 }
370
371 /* nothing do set as qualifier */
372 if (need_tag)
373 {
374 error = EINVAL;
375 goto exit;
376 }
377
378 /* flags */
379 if((field = strtok_r(NULL, ":", &last_field)) == NULL)
380 {
381 error = EINVAL;
382 goto exit;
383 }
384
385 for (tag = 0, sub = strtok_r(field, ",", &last_sub); sub;
386 sub = strtok_r(NULL, ",", &last_sub))
387 {
388 if (!tag && !strcmp(sub, "allow")) {
389 tag = ACL_EXTENDED_ALLOW;
390 continue;
391 } else if (!tag && !strcmp(sub, "deny")) {
392 tag = ACL_EXTENDED_DENY;
393 continue;
394 }
395 for (i = 0; acl_flags[i].name != NULL; ++i)
396 {
397 if (acl_flags[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR)
398 && !strcmp(acl_flags[i].name, sub))
399 {
400 acl_add_flag_np(flags, acl_flags[i].flag);
401 break;
402 }
403 }
404 if (acl_flags[i].name == NULL)
405 {
406 /* couldn't find perm */
407 error = EINVAL;
408 goto exit;
409 }
410 }
411
412 if((field = strtok_r(NULL, ":", &last_field)) != NULL) {
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 }
433 acl_set_tag_type(acl_entry, tag);
434 acl_set_qualifier(acl_entry, *uu);
435 }
436 exit:
437 free(buf);
438 if (error)
439 {
440 acl_free(acl_ret);
441 acl_ret = NULL;
442 errno = error;
443 }
444 return acl_ret;
445 }
446
447 char *
448 acl_to_text(acl_t acl, ssize_t *len_p)
449 {
450 uuid_t *uu;
451 acl_tag_t tag;
452 acl_entry_t entry = NULL;
453 acl_flagset_t flags;
454 acl_permset_t perms;
455 uid_t id;
456 char *str, uu_str[256];
457 int i, first;
458 int isgid;
459 size_t bufsize = 1024;
460 char *buf;
461
462 if (!_ACL_VALID_ACL(acl)) {
463 errno = EINVAL;
464 return NULL;
465 }
466
467 buf = malloc(bufsize);
468 if (len_p == NULL)
469 len_p = alloca(sizeof(ssize_t));
470
471 *len_p = 0;
472
473 if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1))
474 return NULL;
475
476 if (acl_get_flagset_np(acl, &flags) == 0)
477 {
478 for (i = 0, first = 0; acl_flags[i].name != NULL; ++i)
479 {
480 if (acl_flags[i].type & ACL_TYPE_ACL
481 && acl_get_flag_np(flags, acl_flags[i].flag) != 0)
482 {
483 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s",
484 first++ ? "," : " ", acl_flags[i].name))
485 return NULL;
486 }
487 }
488 }
489 for (;acl_get_entry(acl,
490 entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;)
491 {
492 if (((uu = (uuid_t *) acl_get_qualifier(entry)) == NULL)
493 || (acl_get_tag_type(entry, &tag) != 0)
494 || (acl_get_flagset_np(entry, &flags) != 0)
495 || (acl_get_permset(entry, &perms) != 0))
496 continue;
497
498 str = uuid_to_name(uu, &id, &isgid);
499 mbr_uuid_to_string(uu, uu_str); // XXX how big should uu_str be? // XXX error?
500
501 if(!raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s",
502 isgid ? "group" : "user",
503 uu_str,
504 str,
505 id,
506 (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"))
507 return NULL;
508
509 free(str);
510
511 for (i = 0; acl_flags[i].name != NULL; ++i)
512 {
513 if (acl_flags[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE))
514 {
515 if(acl_get_flag_np(flags, acl_flags[i].flag) != 0)
516 {
517 if(!raosnprintf(&buf, &bufsize, len_p, ",%s",
518 acl_flags[i].name))
519 return NULL;
520 }
521 }
522 }
523
524 for (i = 0, first = 0; acl_perms[i].name != NULL; ++i)
525 {
526 if (acl_perms[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE))
527 {
528 if(acl_get_perm_np(perms, acl_perms[i].perm) != 0)
529 {
530 if(!raosnprintf(&buf, &bufsize, len_p, "%s%s",
531 first++ ? "," : ":",
532 acl_perms[i].name))
533 return NULL;
534 }
535 }
536 }
537 }
538 buf[(*len_p)++] = '\n';
539 buf[(*len_p)] = 0;
540 return buf;
541 }
542
543 ssize_t
544 acl_size(acl_t acl)
545 {
546 _ACL_VALIDATE_ACL(acl);
547
548 return(_ACL_HEADER_SIZE + acl->a_entries * _ACL_ENTRY_SIZE);
549 }