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