]>
Commit | Line | Data |
---|---|---|
3d9156a7 A |
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 */ | |
f74c7596 A |
92 | /* XXX Consider writing the magic out to the persistent store |
93 | * to detect corruption | |
94 | */ | |
3d9156a7 A |
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[] = { | |
7c78c529 | 139 | {ACL_ENTRY_INHERITED, "inherited", ACL_TYPE_FILE | ACL_TYPE_DIR}, |
3d9156a7 A |
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); | |
7c78c529 | 165 | if (ret < (*size - *offset)) |
3d9156a7 A |
166 | { |
167 | *offset += ret; | |
168 | return ret; | |
169 | } | |
170 | } | |
7c78c529 | 171 | *buf = reallocf(*buf, (*size *= 2)); |
3d9156a7 A |
172 | } while (*buf); |
173 | ||
7c78c529 | 174 | //warn("reallocf failure"); |
3d9156a7 A |
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 | } | |
7c78c529 | 202 | return strdup(""); |
3d9156a7 A |
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 */ | |
7c78c529 A |
310 | if (*last_field == ':') // empty username field |
311 | last_field++; | |
312 | else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag) | |
3d9156a7 A |
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 */ | |
7c78c529 A |
336 | if (*last_field == ':') // empty uid field |
337 | last_field++; | |
338 | else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag) | |
3d9156a7 A |
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 | ||
7c78c529 A |
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)) | |
3d9156a7 | 415 | { |
7c78c529 | 416 | for (i = 0; acl_perms[i].name != NULL; i++) |
3d9156a7 | 417 | { |
7c78c529 A |
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; | |
3d9156a7 | 430 | } |
3d9156a7 A |
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; | |
3d9156a7 | 459 | size_t bufsize = 1024; |
7c78c529 A |
460 | char *buf; |
461 | ||
462 | if (!_ACL_VALID_ACL(acl)) { | |
463 | errno = EINVAL; | |
464 | return NULL; | |
465 | } | |
3d9156a7 | 466 | |
7c78c529 | 467 | buf = malloc(bufsize); |
3d9156a7 A |
468 | if (len_p == NULL) |
469 | len_p = alloca(sizeof(ssize_t)); | |
470 | ||
471 | *len_p = 0; | |
472 | ||
7c78c529 | 473 | if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1)) |
3d9156a7 A |
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'; | |
7c78c529 | 539 | buf[(*len_p)] = 0; |
3d9156a7 A |
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 | } |