2 * Copyright (c) 2004, 2010, 2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/appleapiopts.h>
25 #include <sys/types.h>
27 #include <sys/fcntl.h>
34 #include <membership.h>
35 #include <membershipPriv.h>
39 #include <libkern/OSByteOrder.h>
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.
50 * acl_t -> external representation, portable endianity
53 acl_copy_ext(void *buf
, acl_t acl
, ssize_t size
)
55 struct kauth_filesec
*ext
= (struct kauth_filesec
*)buf
;
59 /* validate arguments, compute required size */
60 reqsize
= acl_size(acl
);
69 ext
->fsec_magic
= OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC
);
71 /* special case for _FILESEC_REMOVE_ACL */
72 if (acl
== (acl_t
)_FILESEC_REMOVE_ACL
) {
73 ext
->fsec_entrycount
= OSSwapHostToBigInt32(KAUTH_FILESEC_NOACL
);
77 /* export the header */
78 ext
->fsec_entrycount
= OSSwapHostToBigInt32(acl
->a_entries
);
79 ext
->fsec_flags
= OSSwapHostToBigInt32(acl
->a_flags
);
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
);
94 * acl_t -> external representation, native system endianity
97 acl_copy_ext_native(void *buf
, acl_t acl
, ssize_t size
)
99 struct kauth_filesec
*ext
= (struct kauth_filesec
*)buf
;
103 /* validate arguments, compute required size */
104 reqsize
= acl_size(acl
);
107 if (reqsize
> size
) {
113 ext
->fsec_magic
= KAUTH_FILESEC_MAGIC
;
115 /* special case for _FILESEC_REMOVE_ACL */
116 if (acl
== (acl_t
)_FILESEC_REMOVE_ACL
) {
117 ext
->fsec_entrycount
= KAUTH_FILESEC_NOACL
;
121 /* export the header */
122 ext
->fsec_entrycount
= acl
->a_entries
;
123 ext
->fsec_flags
= acl
->a_flags
;
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
;
139 * external representation, portable system endianity -> acl_t
141 * Unlike acl_copy_ext, we can't mung the buffer as it doesn't belong to us.
144 acl_copy_int(const void *buf
)
146 struct kauth_filesec
*ext
= (struct kauth_filesec
*)buf
;
150 if (ext
->fsec_magic
!= OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC
)) {
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
);
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
);
173 * external representation, native system endianity -> acl_t
176 acl_copy_int_native(const void *buf
)
178 struct kauth_filesec
*ext
= (struct kauth_filesec
*)buf
;
182 if (ext
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) {
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
;
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
;
204 #define ACL_TYPE_DIR (1<<0)
205 #define ACL_TYPE_FILE (1<<1)
206 #define ACL_TYPE_ACL (1<<2)
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 {ACL_SYNCHRONIZE
, "synchronize", ACL_TYPE_FILE
| ACL_TYPE_DIR
},
239 {ACL_ENTRY_INHERITED
, "inherited", ACL_TYPE_FILE
| ACL_TYPE_DIR
},
240 {ACL_FLAG_DEFER_INHERIT
, "defer_inherit", ACL_TYPE_ACL
},
241 {ACL_ENTRY_FILE_INHERIT
, "file_inherit", ACL_TYPE_DIR
},
242 {ACL_ENTRY_DIRECTORY_INHERIT
, "directory_inherit", ACL_TYPE_DIR
},
243 {ACL_ENTRY_LIMIT_INHERIT
, "limit_inherit", ACL_TYPE_FILE
| ACL_TYPE_DIR
},
244 {ACL_ENTRY_ONLY_INHERIT
, "only_inherit", ACL_TYPE_DIR
},
245 {ACL_FLAG_NO_INHERIT
, "no_inherit", ACL_TYPE_ACL
},
250 * reallocing snprintf with offset
253 raosnprintf(char **buf
, size_t *size
, ssize_t
*offset
, char *fmt
, ...)
263 ret
= vsnprintf(*buf
+ *offset
, *size
- *offset
, fmt
, ap
);
265 if (ret
< (*size
- *offset
))
271 *buf
= reallocf(*buf
, (*size
*= 2));
274 //warn("reallocf failure");
279 uuid_to_name(uuid_t
*uu
, uid_t
*id
, int *isgid
)
281 struct group
*tgrp
= NULL
;
282 struct passwd
*tpass
= NULL
;
284 if (0 == mbr_uuid_to_id(*uu
, id
, isgid
))
289 if (!(tpass
= getpwuid(*id
)))
291 return strdup(tpass
->pw_name
);
294 if (!(tgrp
= getgrgid((gid_t
) *id
)))
296 return strdup(tgrp
->gr_name
);
299 errout
: ; //warn("Unable to translate qualifier on ACL\n");
306 acl_from_text(const char *buf_p
)
308 int i
, error
= 0, need_tag
, ug_tag
;
309 char *buf
, *orig_buf
;
310 char *entry
, *field
, *sub
;
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
;
326 if ((buf
= strdup(buf_p
)) == NULL
)
329 if ((acl_ret
= acl_init(1)) == NULL
)
335 * format: !#acl <version> [<flags>]
337 if ((entry
= strsep(&buf
, "\n")) != NULL
&& *entry
)
340 field
= strsep(&entry
, " ");
341 if (*field
&& strncmp(field
, "!#acl", strlen("!#acl")))
347 /* field 2: <version>
348 * currently only accepts 1
350 field
= strsep(&entry
, " ");
352 if (!*field
|| strtol(field
, NULL
, 0) != 1)
361 if((field
= strsep(&entry
, " ")) != NULL
&& *field
)
363 acl_get_flagset_np(acl_ret
, &flags
);
364 while ((sub
= strsep(&field
, ",")) && *sub
)
366 for (i
= 0; acl_flags
[i
].name
!= NULL
; ++i
)
368 if (acl_flags
[i
].type
& ACL_TYPE_ACL
369 && !strcmp(acl_flags
[i
].name
, sub
))
371 acl_add_flag_np(flags
, acl_flags
[i
].flag
);
375 if (acl_flags
[i
].name
== NULL
)
377 /* couldn't find flag */
388 /* parse each acl line
389 * format: <user|group>:
393 * <allow|deny>[,<flags>]
394 * [:<permissions>[,<permissions>]]
396 * only one of the user/group identifies is required
397 * the first one found is used
399 while ((entry
= strsep(&buf
, "\n")) && *entry
)
404 /* field 1: <user|group> */
405 field
= strsep(&entry
, ":");
408 bzero(uu
, sizeof(uuid_t
));
409 else if((uu
= calloc(1, sizeof(uuid_t
))) == NULL
)
415 if(acl_create_entry(&acl_ret
, &acl_entry
))
421 if (-1 == acl_get_flagset_np(acl_entry
, &flags
)
422 || -1 == acl_get_permset(acl_entry
, &perms
))
431 if(!strcmp(field
, "user"))
432 ug_tag
= ID_TYPE_UID
;
435 if(!strcmp(field
, "group"))
436 ug_tag
= ID_TYPE_GID
;
443 /* field 2: <uuid> */
444 if ((field
= strsep(&entry
, ":")) != NULL
&& *field
)
446 uuid_parse(field
, *uu
);
450 /* field 3: <username|groupname> */
451 if ((field
= strsep(&entry
, ":")) != NULL
&& *field
&& need_tag
)
456 if((tpass
= getpwnam(field
)) != NULL
)
457 if (mbr_uid_to_uuid(tpass
->pw_uid
, *uu
) != 0)
464 if ((tgrp
= getgrnam(field
)) != NULL
)
465 if (mbr_gid_to_uuid(tgrp
->gr_gid
, *uu
) != 0)
478 /* field 4: <uid|gid> */
479 if ((field
= strsep(&entry
, ":")) != NULL
&& *field
&& need_tag
)
484 if((id
= strtol(field
, NULL
, 10)) == 0 && error
)
493 if((tpass
= getpwuid((uid_t
)id
)) != NULL
)
494 if (mbr_uid_to_uuid(tpass
->pw_uid
, *uu
) != 0)
501 if ((tgrp
= getgrgid((gid_t
)id
)) != NULL
)
502 if (mbr_gid_to_uuid(tgrp
->gr_gid
, *uu
) != 0)
512 /* sanity check: nothing set as qualifier */
519 /* field 5: <flags> */
520 if((field
= strsep(&entry
, ":")) == NULL
|| !*field
)
526 for (tag
= 0; (sub
= strsep(&field
, ",")) && *sub
;)
530 if (!strcmp(sub
, "allow"))
531 tag
= ACL_EXTENDED_ALLOW
;
532 else if (!strcmp(sub
, "deny"))
533 tag
= ACL_EXTENDED_DENY
;
541 for (i
= 0; acl_flags
[i
].name
!= NULL
; ++i
)
543 if (acl_flags
[i
].type
& (ACL_TYPE_FILE
| ACL_TYPE_DIR
)
544 && !strcmp(acl_flags
[i
].name
, sub
))
546 acl_add_flag_np(flags
, acl_flags
[i
].flag
);
550 if (acl_flags
[i
].name
== NULL
)
552 /* couldn't find perm */
558 /* field 6: <perms> (can be empty) */
559 if((field
= strsep(&entry
, ":")) != NULL
&& *field
)
561 while ((sub
= strsep(&field
, ",")) && *sub
)
563 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++)
565 if (acl_perms
[i
].type
& (ACL_TYPE_FILE
| ACL_TYPE_DIR
)
566 && !strcmp(acl_perms
[i
].name
, sub
))
568 acl_add_perm(perms
, acl_perms
[i
].perm
);
572 if (acl_perms
[i
].name
== NULL
)
574 /* couldn't find perm */
580 acl_set_tag_type(acl_entry
, tag
);
581 acl_set_qualifier(acl_entry
, *uu
);
597 acl_to_text(acl_t acl
, ssize_t
*len_p
)
600 acl_entry_t entry
= NULL
;
606 size_t bufsize
= 1024;
609 if (!_ACL_VALID_ACL(acl
)) {
615 if ((len_p
= alloca(sizeof(ssize_t
))) == NULL
)
620 if ((buf
= malloc(bufsize
)) == NULL
)
623 if (!raosnprintf(&buf
, &bufsize
, len_p
, "!#acl %d", 1))
626 if (acl_get_flagset_np(acl
, &flags
) == 0)
628 for (i
= 0, first
= 0; acl_flags
[i
].name
!= NULL
; ++i
)
630 if (acl_flags
[i
].type
& ACL_TYPE_ACL
631 && acl_get_flag_np(flags
, acl_flags
[i
].flag
) != 0)
633 if(!raosnprintf(&buf
, &bufsize
, len_p
, "%s%s",
634 first
++ ? "," : " ", acl_flags
[i
].name
))
639 for (;acl_get_entry(acl
,
640 entry
== NULL
? ACL_FIRST_ENTRY
: ACL_NEXT_ENTRY
, &entry
) == 0;)
644 char *str
, uu_str
[37];
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
);
654 uuid_unparse_upper(*uu
, uu_str
);
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",
662 (tag
== ACL_EXTENDED_ALLOW
) ? "allow" : "deny");
664 valid
= raosnprintf(&buf
, &bufsize
, len_p
, "\nuser:%s:::%s",
666 (tag
== ACL_EXTENDED_ALLOW
) ? "allow" : "deny");
675 for (i
= 0; acl_flags
[i
].name
!= NULL
; ++i
)
677 if (acl_flags
[i
].type
& (ACL_TYPE_DIR
| ACL_TYPE_FILE
))
679 if(acl_get_flag_np(flags
, acl_flags
[i
].flag
) != 0)
681 if(!raosnprintf(&buf
, &bufsize
, len_p
, ",%s",
688 for (i
= 0, first
= 0; acl_perms
[i
].name
!= NULL
; ++i
)
690 if (acl_perms
[i
].type
& (ACL_TYPE_DIR
| ACL_TYPE_FILE
))
692 if(acl_get_perm_np(perms
, acl_perms
[i
].perm
) != 0)
694 if(!raosnprintf(&buf
, &bufsize
, len_p
, "%s%s",
695 first
++ ? "," : ":", acl_perms
[i
].name
))
702 if(!raosnprintf(&buf
, &bufsize
, len_p
, "\n")) goto err_nomem
;
716 /* special case for _FILESEC_REMOVE_ACL */
717 if (acl
== (acl_t
)_FILESEC_REMOVE_ACL
)
718 return KAUTH_FILESEC_SIZE(0);
720 _ACL_VALIDATE_ACL(acl
);
722 return(KAUTH_FILESEC_SIZE(acl
->a_entries
));