X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/f74c75964edafe4a1463ed848c56509e4c505b6c..HEAD:/posix1e/acl_translate.c?ds=sidebyside diff --git a/posix1e/acl_translate.c b/posix1e/acl_translate.c index 25f4c8b..57cb01f 100644 --- a/posix1e/acl_translate.c +++ b/posix1e/acl_translate.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004, 2010, 2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,17 +24,31 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include +#include + #include "aclvar.h" +/* + * NOTE: the copy_int/copy_ext functions are duplicated here, one version of each for + * each of native and portable endianity. A more elegant solution might be called for + * if the functions become much more complicated. + */ + +/* + * acl_t -> external representation, portable endianity + */ ssize_t acl_copy_ext(void *buf, acl_t acl, ssize_t size) { @@ -50,12 +64,63 @@ acl_copy_ext(void *buf, acl_t acl, ssize_t size) errno = ERANGE; return(-1); } + + bzero(ext, reqsize); + ext->fsec_magic = OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC); + + /* special case for _FILESEC_REMOVE_ACL */ + if (acl == (acl_t)_FILESEC_REMOVE_ACL) { + ext->fsec_entrycount = OSSwapHostToBigInt32(KAUTH_FILESEC_NOACL); + return(reqsize); + } /* export the header */ + ext->fsec_entrycount = OSSwapHostToBigInt32(acl->a_entries); + ext->fsec_flags = OSSwapHostToBigInt32(acl->a_flags); + + /* copy ACEs */ + for (i = 0; i < acl->a_entries; i++) { + /* ACE contents are almost identical */ + ext->fsec_ace[i].ace_applicable = acl->a_ace[i].ae_applicable; + ext->fsec_ace[i].ace_flags = + OSSwapHostToBigInt32((acl->a_ace[i].ae_tag & KAUTH_ACE_KINDMASK) | (acl->a_ace[i].ae_flags & ~KAUTH_ACE_KINDMASK)); + ext->fsec_ace[i].ace_rights = OSSwapHostToBigInt32(acl->a_ace[i].ae_perms); + } + + return(reqsize); +} + +/* + * acl_t -> external representation, native system endianity + */ +ssize_t +acl_copy_ext_native(void *buf, acl_t acl, ssize_t size) +{ + struct kauth_filesec *ext = (struct kauth_filesec *)buf; + ssize_t reqsize; + int i; + + /* validate arguments, compute required size */ + reqsize = acl_size(acl); + if (reqsize < 0) + return(-1); + if (reqsize > size) { + errno = ERANGE; + return(-1); + } + + bzero(ext, reqsize); ext->fsec_magic = KAUTH_FILESEC_MAGIC; + + /* special case for _FILESEC_REMOVE_ACL */ + if (acl == (acl_t)_FILESEC_REMOVE_ACL) { + ext->fsec_entrycount = KAUTH_FILESEC_NOACL; + return(reqsize); + } + + /* export the header */ ext->fsec_entrycount = acl->a_entries; ext->fsec_flags = acl->a_flags; - /* XXX owner? */ /* copy ACEs */ for (i = 0; i < acl->a_entries; i++) { @@ -70,6 +135,11 @@ acl_copy_ext(void *buf, acl_t acl, ssize_t size) return(reqsize); } +/* + * external representation, portable system endianity -> acl_t + * + * Unlike acl_copy_ext, we can't mung the buffer as it doesn't belong to us. + */ acl_t acl_copy_int(const void *buf) { @@ -77,6 +147,38 @@ acl_copy_int(const void *buf) acl_t ap; int i; + if (ext->fsec_magic != OSSwapHostToBigInt32(KAUTH_FILESEC_MAGIC)) { + errno = EINVAL; + return(NULL); + } + + if ((ap = acl_init(OSSwapBigToHostInt32(ext->fsec_entrycount))) != NULL) { + /* copy useful header fields */ + ap->a_flags = OSSwapBigToHostInt32(ext->fsec_flags); + ap->a_entries = OSSwapBigToHostInt32(ext->fsec_entrycount); + /* copy ACEs */ + for (i = 0; i < ap->a_entries; i++) { + /* ACE contents are literally identical */ + ap->a_ace[i].ae_magic = _ACL_ENTRY_MAGIC; + ap->a_ace[i].ae_applicable = ext->fsec_ace[i].ace_applicable; + ap->a_ace[i].ae_flags = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_flags) & ~KAUTH_ACE_KINDMASK; + ap->a_ace[i].ae_tag = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_flags) & KAUTH_ACE_KINDMASK; + ap->a_ace[i].ae_perms = OSSwapBigToHostInt32(ext->fsec_ace[i].ace_rights); + } + } + return(ap); +} + +/* + * external representation, native system endianity -> acl_t + */ +acl_t +acl_copy_int_native(const void *buf) +{ + struct kauth_filesec *ext = (struct kauth_filesec *)buf; + acl_t ap; + int i; + if (ext->fsec_magic != KAUTH_FILESEC_MAGIC) { errno = EINVAL; return(NULL); @@ -89,9 +191,6 @@ acl_copy_int(const void *buf) /* copy ACEs */ for (i = 0; i < ap->a_entries; i++) { /* ACE contents are literally identical */ -/* XXX Consider writing the magic out to the persistent store - * to detect corruption - */ ap->a_ace[i].ae_magic = _ACL_ENTRY_MAGIC; ap->a_ace[i].ae_applicable = ext->fsec_ace[i].ace_applicable; ap->a_ace[i].ae_flags = ext->fsec_ace[i].ace_flags & ~KAUTH_ACE_KINDMASK; @@ -128,6 +227,7 @@ static struct { {ACL_READ_SECURITY, "readsecurity", ACL_TYPE_FILE | ACL_TYPE_DIR}, {ACL_WRITE_SECURITY, "writesecurity", ACL_TYPE_FILE | ACL_TYPE_DIR}, {ACL_CHANGE_OWNER, "chown", ACL_TYPE_FILE | ACL_TYPE_DIR}, + {ACL_SYNCHRONIZE, "synchronize", ACL_TYPE_FILE | ACL_TYPE_DIR}, {0, NULL, 0} }; @@ -142,13 +242,13 @@ static struct { {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_TYPE_DIR}, {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_TYPE_FILE | ACL_TYPE_DIR}, {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_TYPE_DIR}, + {ACL_FLAG_NO_INHERIT, "no_inherit", ACL_TYPE_ACL}, {0, NULL, 0} }; /* * reallocing snprintf with offset */ - static int raosnprintf(char **buf, size_t *size, ssize_t *offset, char *fmt, ...) { @@ -199,17 +299,16 @@ uuid_to_name(uuid_t *uu, uid_t *id, int *isgid) errout: ; //warn("Unable to translate qualifier on ACL\n"); } } - return strdup(""); + return NULL; } acl_t acl_from_text(const char *buf_p) { - int i, error = 0, need_tag = 1, ug_tag = -1; - char *buf; - char *entry, *field, *sub, - *last_field, *last_entry, *last_sub; - uuid_t *uu; + int i, error = 0, need_tag, ug_tag; + char *buf, *orig_buf; + char *entry, *field, *sub; + uuid_t *uu = NULL; struct passwd *tpass = NULL; struct group *tgrp = NULL; acl_entry_t acl_entry; @@ -218,41 +317,51 @@ acl_from_text(const char *buf_p) acl_tag_t tag; acl_t acl_ret; - if ((acl_ret = acl_init(1)) == NULL) - return NULL; - if (buf_p == NULL) + { + errno = EINVAL; return NULL; + } if ((buf = strdup(buf_p)) == NULL) return NULL; - /* acl flags */ - if ((entry = strtok_r(buf, "\n", &last_entry)) != NULL) + if ((acl_ret = acl_init(1)) == NULL) + return NULL; + + orig_buf = buf; + + /* global acl flags + * format: !#acl [] + */ + if ((entry = strsep(&buf, "\n")) != NULL && *entry) { - /* stamp */ - field = strtok_r(entry, " ", &last_field); - if (field && strncmp(field, "!#acl", strlen("!#acl"))) + /* field 1: !#acl */ + field = strsep(&entry, " "); + if (*field && strncmp(field, "!#acl", strlen("!#acl"))) { error = EINVAL; goto exit; } - /* version */ - field = strtok_r(NULL, " ", &last_field); + /* field 2: + * currently only accepts 1 + */ + field = strsep(&entry, " "); errno = 0; - if (field == NULL || strtol(field, NULL, 0) != 1) + if (field == NULL || !*field || strtol(field, NULL, 0) != 1) { error = EINVAL; goto exit; } - /* optional flags */ - if((field = strtok_r(NULL, " ", &last_field)) != NULL) + /* field 3: + * optional + */ + if((field = strsep(&entry, " ")) != NULL && *field) { acl_get_flagset_np(acl_ret, &flags); - for (sub = strtok_r(field, ",", &last_sub); sub; - sub = strtok_r(NULL, ",", &last_sub)) + while ((sub = strsep(&field, ",")) && *sub) { for (i = 0; acl_flags[i].name != NULL; ++i) { @@ -271,45 +380,75 @@ acl_from_text(const char *buf_p) } } } + } else { + error = EINVAL; + goto exit; } - for (entry = strtok_r(NULL, "\n", &last_entry); entry; - entry = strtok_r(NULL, "\n", &last_entry)) + /* parse each acl line + * format: : + * []: + * []: + * []: + * [,] + * [:[,]] + * + * only one of the user/group identifies is required + * the first one found is used + */ + while ((entry = strsep(&buf, "\n")) && *entry) { - field = strtok_r(entry, ":", &last_field); + need_tag = 1; + ug_tag = -1; + + /* field 1: */ + field = strsep(&entry, ":"); - if((uu = calloc(1, sizeof(uuid_t))) == NULL) + if(uu) + bzero(uu, sizeof(uuid_t)); + else if((uu = calloc(1, sizeof(uuid_t))) == NULL) + { + error = errno; goto exit; + } if(acl_create_entry(&acl_ret, &acl_entry)) + { + error = errno; goto exit; + } - acl_get_flagset_np(acl_entry, &flags); - acl_get_permset(acl_entry, &perms); + if (-1 == acl_get_flagset_np(acl_entry, &flags) + || -1 == acl_get_permset(acl_entry, &perms)) + { + error = errno; + goto exit; + } switch(*field) { case 'u': - if(!strncmp(buf, "user", strlen(field))) + if(!strcmp(field, "user")) ug_tag = ID_TYPE_UID; break; case 'g': - if(!strncmp(buf, "group", strlen(field))) + if(!strcmp(field, "group")) ug_tag = ID_TYPE_GID; break; - + default: + error = EINVAL; + goto exit; } - /* uuid */ - if ((field = strtok_r(NULL, ":", &last_field)) != NULL) + /* field 2: */ + if ((field = strsep(&entry, ":")) != NULL && *field) { - mbr_string_to_uuid(field, *uu); + uuid_parse(field, *uu); need_tag = 0; } - /* name */ - if (*last_field == ':') // empty username field - last_field++; - else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag) + + /* field 3: */ + if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) { switch(ug_tag) { @@ -329,13 +468,15 @@ acl_from_text(const char *buf_p) goto exit; } break; + default: + error = EINVAL; + goto exit; } need_tag = 0; } - /* uid */ - if (*last_field == ':') // empty uid field - last_field++; - else if ((field = strtok_r(NULL, ":", &last_field)) != NULL && need_tag) + + /* field 4: */ + if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) { uid_t id; error = 0; @@ -368,30 +509,35 @@ acl_from_text(const char *buf_p) need_tag = 0; } - /* nothing do set as qualifier */ + /* sanity check: nothing set as qualifier */ if (need_tag) { error = EINVAL; goto exit; } - /* flags */ - if((field = strtok_r(NULL, ":", &last_field)) == NULL) + /* field 5: */ + if((field = strsep(&entry, ":")) == NULL || !*field) { error = EINVAL; goto exit; } - for (tag = 0, sub = strtok_r(field, ",", &last_sub); sub; - sub = strtok_r(NULL, ",", &last_sub)) + for (tag = 0; (sub = strsep(&field, ",")) && *sub;) { - if (!tag && !strcmp(sub, "allow")) { + if (!tag) + { + if (!strcmp(sub, "allow")) tag = ACL_EXTENDED_ALLOW; - continue; - } else if (!tag && !strcmp(sub, "deny")) { + else if (!strcmp(sub, "deny")) tag = ACL_EXTENDED_DENY; - continue; + else { + error = EINVAL; + goto exit; + } + continue; } + for (i = 0; acl_flags[i].name != NULL; ++i) { if (acl_flags[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR) @@ -409,9 +555,10 @@ acl_from_text(const char *buf_p) } } - if((field = strtok_r(NULL, ":", &last_field)) != NULL) { - for (sub = strtok_r(field, ",", &last_sub); sub; - sub = strtok_r(NULL, ",", &last_sub)) + /* field 6: (can be empty) */ + if((field = strsep(&entry, ":")) != NULL && *field) + { + while ((sub = strsep(&field, ",")) && *sub) { for (i = 0; acl_perms[i].name != NULL; i++) { @@ -434,7 +581,9 @@ acl_from_text(const char *buf_p) acl_set_qualifier(acl_entry, *uu); } exit: - free(buf); + if(uu) + free(uu); + free(orig_buf); if (error) { acl_free(acl_ret); @@ -447,31 +596,32 @@ exit: char * acl_to_text(acl_t acl, ssize_t *len_p) { - uuid_t *uu; acl_tag_t tag; acl_entry_t entry = NULL; acl_flagset_t flags; acl_permset_t perms; uid_t id; - char *str, uu_str[256]; int i, first; int isgid; size_t bufsize = 1024; - char *buf; + char *buf = NULL; if (!_ACL_VALID_ACL(acl)) { errno = EINVAL; return NULL; } - buf = malloc(bufsize); if (len_p == NULL) - len_p = alloca(sizeof(ssize_t)); + if ((len_p = alloca(sizeof(ssize_t))) == NULL) + goto err_nomem; *len_p = 0; + if ((buf = malloc(bufsize)) == NULL) + goto err_nomem; + if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1)) - return NULL; + goto err_nomem; if (acl_get_flagset_np(acl, &flags) == 0) { @@ -482,31 +632,45 @@ acl_to_text(acl_t acl, ssize_t *len_p) { if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", first++ ? "," : " ", acl_flags[i].name)) - return NULL; + goto err_nomem; } } } for (;acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;) { + int valid; + uuid_t *uu; + char *str, uu_str[37]; + if (((uu = (uuid_t *) acl_get_qualifier(entry)) == NULL) || (acl_get_tag_type(entry, &tag) != 0) || (acl_get_flagset_np(entry, &flags) != 0) - || (acl_get_permset(entry, &perms) != 0)) + || (acl_get_permset(entry, &perms) != 0)) { + if (uu != NULL) acl_free(uu); continue; + } - str = uuid_to_name(uu, &id, &isgid); - mbr_uuid_to_string(uu, uu_str); // XXX how big should uu_str be? // XXX error? - - if(!raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s", - isgid ? "group" : "user", - uu_str, - str, - id, - (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny")) - return NULL; + uuid_unparse_upper(*uu, uu_str); + + if ((str = uuid_to_name(uu, &id, &isgid)) != NULL) { + valid = raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s", + isgid ? "group" : "user", + uu_str, + str, + id, + (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); + } else { + valid = raosnprintf(&buf, &bufsize, len_p, "\nuser:%s:::%s", + uu_str, + (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); + } free(str); + acl_free(uu); + + if (!valid) + goto err_nomem; for (i = 0; acl_flags[i].name != NULL; ++i) { @@ -516,7 +680,7 @@ acl_to_text(acl_t acl, ssize_t *len_p) { if(!raosnprintf(&buf, &bufsize, len_p, ",%s", acl_flags[i].name)) - return NULL; + goto err_nomem; } } } @@ -528,22 +692,32 @@ acl_to_text(acl_t acl, ssize_t *len_p) if(acl_get_perm_np(perms, acl_perms[i].perm) != 0) { if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", - first++ ? "," : ":", - acl_perms[i].name)) - return NULL; + first++ ? "," : ":", acl_perms[i].name)) + goto err_nomem; } } } } - buf[(*len_p)++] = '\n'; - buf[(*len_p)] = 0; + + if(!raosnprintf(&buf, &bufsize, len_p, "\n")) goto err_nomem; return buf; + +err_nomem: + if (buf != NULL) + free(buf); + + errno = ENOMEM; + return NULL; } ssize_t acl_size(acl_t acl) { + /* special case for _FILESEC_REMOVE_ACL */ + if (acl == (acl_t)_FILESEC_REMOVE_ACL) + return KAUTH_FILESEC_SIZE(0); + _ACL_VALIDATE_ACL(acl); - return(_ACL_HEADER_SIZE + acl->a_entries * _ACL_ENTRY_SIZE); + return(KAUTH_FILESEC_SIZE(acl->a_entries)); }