X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/c38e3ce98599a410a47dc10253faa4d5830f13b2..427c49bcad63d042b29ada2ac27e3dfc4845c779:/authd/authitems.c?ds=sidebyside diff --git a/authd/authitems.c b/authd/authitems.c new file mode 100644 index 00000000..a55850b9 --- /dev/null +++ b/authd/authitems.c @@ -0,0 +1,1168 @@ +/* Copyright (c) 2012 Apple Inc. All rights reserved. */ + +#include "authitems.h" +#include "crc.h" +#include "debugging.h" + +#include "authutilities.h" +#include + +typedef struct _auth_item_s * auth_item_t; + +#pragma mark - +#pragma mark auth_item_t + +struct _auth_item_s { + __AUTH_BASE_STRUCT_HEADER__; + + AuthorizationItem data; + uint32_t type; + size_t bufLen; + + CFStringRef cfKey; +}; + +static const char * +auth_item_get_string(auth_item_t item) +{ + if (item->bufLen <= item->data.valueLength) { + item->bufLen = item->data.valueLength+1; // make sure buffer has a null char + item->data.value = realloc(item->data.value, item->bufLen); + if (item->data.value == NULL) { + // this is added to prevent running off into random memory if a string buffer doesn't have a null char + LOGE("realloc failed"); + abort(); + } + ((uint8_t*)item->data.value)[item->bufLen-1] = '\0'; + } + return item->data.value; +} + +static CFStringRef +auth_item_get_cf_key(auth_item_t item) +{ + if (!item->cfKey) { + item->cfKey = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, item->data.name, kCFStringEncodingUTF8, kCFAllocatorNull); + } + return item->cfKey; +} + +static AuthorizationItem * +auth_item_get_auth_item(auth_item_t item) +{ + return &item->data; +} + +static xpc_object_t +auth_item_copy_auth_item_xpc(auth_item_t item) +{ + xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->data.name); + if (item->data.value) { + xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, item->data.value, item->data.valueLength); + } + xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->data.flags); + xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_TYPE, item->type); + return xpc_data; +} + +static void +_auth_item_finalize(CFTypeRef value) +{ + auth_item_t item = (auth_item_t)value; + + CFReleaseSafe(item->cfKey); + + if (item->data.name) { + free((void*)item->data.name); + } + + if (item->data.value) { + memset(item->data.value, 0, item->data.valueLength); + free(item->data.value); + } +} + +static Boolean +_auth_item_equal(CFTypeRef value1, CFTypeRef value2) +{ + return (CFHash(value1) == CFHash(value2)); +} + +static CFStringRef +_auth_item_copy_description(CFTypeRef value) +{ + bool hidden = false; + auth_item_t item = (auth_item_t)value; + +#ifndef DEBUG + static size_t passLen = strlen(kAuthorizationEnvironmentPassword); + if (strncasecmp(item->data.name, kAuthorizationEnvironmentPassword, passLen) == 0) { + hidden = true; + } +#endif + + CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFStringAppendFormat(desc, NULL, CFSTR("auth_item: %s, type=%i, length=%li, flags=%x"), + item->data.name, item->type, + hidden ? 0 : item->data.valueLength, item->data.flags); + + switch (item->type) { + case AI_TYPE_STRING: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%s"), hidden ? "(hidden)" : auth_item_get_string(item)); + break; + case AI_TYPE_INT: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%i"), *(int32_t*)item->data.value); + break; + case AI_TYPE_UINT: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%u"), *(uint32_t*)item->data.value); + break; + case AI_TYPE_INT64: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%lli"), *(int64_t*)item->data.value); + break; + case AI_TYPE_UINT64: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%llu"), *(uint64_t*)item->data.value); + break; + case AI_TYPE_BOOL: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%i"), *(bool*)item->data.value); + break; + case AI_TYPE_DOUBLE: + CFStringAppendFormat(desc, NULL, CFSTR(" value=%f"), *(double*)item->data.value); + break; + case AI_TYPE_DATA: + case AI_TYPE_UNKNOWN: + if (hidden) { + CFStringAppendFormat(desc, NULL, CFSTR(" value=(hidden)")); + } else { + CFStringAppendFormat(desc, NULL, CFSTR(" value=0x")); + size_t i = item->data.valueLength < 10 ? item->data.valueLength : 10; + uint8_t * data = item->data.value; + for (; i > 0; i--) { + CFStringAppendFormat(desc, NULL, CFSTR("%02x"), data[i-1]); + } + } + break; + default: + break; + } + return desc; +} + +static CFHashCode +_auth_item_hash(CFTypeRef value) +{ + auth_item_t item = (auth_item_t)value; + uint64_t crc = crc64_init(); + crc = crc64_update(crc, item->data.name, strlen(item->data.name)); + if (item->data.value) { + crc = crc64_update(crc, item->data.value, item->data.valueLength); + } + crc = crc64_update(crc, &item->data.flags, sizeof(item->data.flags)); + + crc = crc64_final(crc); + return crc; +} + +AUTH_TYPE_INSTANCE(auth_item, + .init = NULL, + .copy = NULL, + .finalize = _auth_item_finalize, + .equal = _auth_item_equal, + .hash = _auth_item_hash, + .copyFormattingDesc = NULL, + .copyDebugDesc = _auth_item_copy_description + ); + +static CFTypeID auth_item_get_type_id() { + static CFTypeID type_id = _kCFRuntimeNotATypeID; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + type_id = _CFRuntimeRegisterClass(&_auth_type_auth_item); + }); + + return type_id; +} + +static auth_item_t +_auth_item_create() +{ + auth_item_t item = NULL; + + item = (auth_item_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_item_get_type_id(), AUTH_CLASS_SIZE(auth_item), NULL); + require(item != NULL, done); + +done: + return item; +} + +static auth_item_t +auth_item_create(uint32_t type, const char * name, const void * value, size_t valueLen, uint32_t flags) +{ + auth_item_t item = NULL; + require(name != NULL, done); + + item = _auth_item_create(); + require(item != NULL, done); + + item->type = type; + item->data.flags = flags; + item->data.name = _copy_string(name); + item->data.valueLength = valueLen; + item->bufLen = valueLen; + if (value) { + if (item->type == AI_TYPE_STRING) { + item->bufLen++; + item->data.value = calloc(1u, item->bufLen); + } else if (valueLen) { + item->data.value = calloc(1u, item->bufLen); + } + if (valueLen) { + memcpy(item->data.value, value, valueLen); + } + } + +done: + return item; +} + +static auth_item_t +auth_item_create_with_xpc(xpc_object_t data) +{ + auth_item_t item = NULL; + require(data != NULL, done); + require(xpc_get_type(data) == XPC_TYPE_DICTIONARY, done); + require(xpc_dictionary_get_string(data, AUTH_XPC_ITEM_NAME) != NULL, done); + + item = _auth_item_create(); + require(item != NULL, done); + + item->data.name = _copy_string(xpc_dictionary_get_string(data, AUTH_XPC_ITEM_NAME)); + item->data.flags = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_FLAGS); + item->type = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_TYPE); + + size_t len; + const void * value = xpc_dictionary_get_data(data, AUTH_XPC_ITEM_VALUE, &len); + if (value) { + item->bufLen = len; + item->data.valueLength = len; + item->data.value = calloc(1u, len); + memcpy(item->data.value, value, len); + } + +done: + return item; +} + +#pragma mark - +#pragma mark auth_items_t + +struct _auth_items_s { + __AUTH_BASE_STRUCT_HEADER__; + + CFMutableDictionaryRef dictionary; + AuthorizationItemSet set; +}; + +static void +_auth_items_finalize(CFTypeRef value) +{ + auth_items_t items = (auth_items_t)value; + + CFReleaseNull(items->dictionary); + free_safe(items->set.items) +} + +static Boolean +_auth_items_equal(CFTypeRef value1, CFTypeRef value2) +{ + auth_items_t items1 = (auth_items_t)value1; + auth_items_t items2 = (auth_items_t)value2; + + return CFEqual(items1->dictionary, items2->dictionary); +} + +static CFStringRef +_auth_items_copy_description(CFTypeRef value) +{ + auth_items_t items = (auth_items_t)value; + return CFCopyDescription(items->dictionary); +} + +AUTH_TYPE_INSTANCE(auth_items, + .init = NULL, + .copy = NULL, + .finalize = _auth_items_finalize, + .equal = _auth_items_equal, + .hash = NULL, + .copyFormattingDesc = NULL, + .copyDebugDesc = _auth_items_copy_description + ); + +CFTypeID auth_items_get_type_id() +{ + static CFTypeID type_id = _kCFRuntimeNotATypeID; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + type_id = _CFRuntimeRegisterClass(&_auth_type_auth_items); + }); + + return type_id; +} + +static auth_items_t +_auth_items_create(bool createDict) +{ + auth_items_t items = NULL; + + items = (auth_items_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_items_get_type_id(), AUTH_CLASS_SIZE(auth_items), NULL); + require(items != NULL, done); + + if (createDict) { + items->dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + +done: + return items; +} + +auth_items_t +auth_items_create() +{ + auth_items_t items = NULL; + + items = _auth_items_create(true); + require(items != NULL, done); + +done: + return items; +} + +static bool +_auth_items_parse_xpc(auth_items_t items, const xpc_object_t data) +{ + bool result = false; + require(data != NULL, done); + require(xpc_get_type(data) == XPC_TYPE_ARRAY, done); + + result = xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) { + + auth_item_t item = auth_item_create_with_xpc(value); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + + return true; + }); + +done: + return result; +} + +auth_items_t auth_items_create_with_xpc(const xpc_object_t data) +{ + auth_items_t items = NULL; + + items = _auth_items_create(true); + require(items != NULL, done); + + _auth_items_parse_xpc(items, data); + +done: + return items; +} + +auth_items_t +auth_items_create_copy(auth_items_t copy) +{ + auth_items_t items = NULL; + + items = _auth_items_create(false); + require(items != NULL, done); + + items->dictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(copy->dictionary), copy->dictionary); + +done: + return items; +} + +size_t +auth_items_get_count(auth_items_t items) +{ + return (size_t)CFDictionaryGetCount(items->dictionary); +} + +AuthorizationItemSet * +auth_items_get_item_set(auth_items_t items) +{ + uint32_t count = (uint32_t)CFDictionaryGetCount(items->dictionary); + if (count) { + size_t size = count * sizeof(AuthorizationItem); + if (items->set.items == NULL) { + items->set.items = calloc(1u, size); + require(items->set.items != NULL, done); + } else { + if (count > items->set.count) { + items->set.items = realloc(items->set.items, size); + require_action(items->set.items != NULL, done, items->set.count = 0); + } + } + items->set.count = count; + CFTypeRef keys[count], values[count]; + CFDictionaryGetKeysAndValues(items->dictionary, keys, values); + for (CFIndex i = 0; i < count; i++) { + auth_item_t item = (auth_item_t)values[i]; + items->set.items[i] = *auth_item_get_auth_item(item); + } + } else { + items->set.count = 0; + } + +done: + return &items->set; +} + +xpc_object_t +auth_items_export_xpc(auth_items_t items) +{ + xpc_object_t array = xpc_array_create(NULL, 0); + + _cf_dictionary_iterate(items->dictionary, ^bool(CFTypeRef key AUTH_UNUSED, CFTypeRef value) { + auth_item_t item = (auth_item_t)value; + xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item); + xpc_array_append_value(array, xpc_data); + xpc_release_safe(xpc_data); + return true; + }); + + return array; +} + +static auth_item_t +_find_item(auth_items_t items, const char * key) +{ + auth_item_t item = NULL; + CFStringRef lookup = NULL; + require(key != NULL, done); + + lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + require(lookup != NULL, done); + + item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup); + +done: + CFReleaseSafe(lookup); + return item; +} + +void +auth_items_set_flags(auth_items_t items, const char *key, uint32_t flags) +{ + auth_item_t item = _find_item(items,key); + if (item) { + item->data.flags |= flags; + } +} + +void +auth_items_clear_flags(auth_items_t items, const char *key, uint32_t flags) +{ + auth_item_t item = _find_item(items,key); + if (item) { + item->data.flags &= ~flags; + } +} + +uint32_t +auth_items_get_flags(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { + return item->data.flags; + } + + return 0; +} + +bool +auth_items_check_flags(auth_items_t items, const char *key, uint32_t flags) +{ + uint32_t current = auth_items_get_flags(items,key); + return flags ? (current & flags) != 0 : current == 0; +} + +void +auth_items_set_key(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (!item) { + item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +bool +auth_items_exist(auth_items_t items, const char *key) +{ + return _find_item(items,key) != NULL; +} + +void +auth_items_remove(auth_items_t items, const char *key) +{ + CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + CFDictionaryRemoveValue(items->dictionary, lookup); + CFReleaseSafe(lookup); +} + +void +auth_items_remove_with_flags(auth_items_t items, uint32_t flags) +{ + auth_items_iterate(items, ^bool(const char *key) { + if (auth_items_check_flags(items, key, flags)) { + auth_items_remove(items,key); + } + return true; + }); +} + +void +auth_items_clear(auth_items_t items) +{ + CFDictionaryRemoveAllValues(items->dictionary); +} + +void +auth_items_copy(auth_items_t items, auth_items_t src) +{ + auth_items_iterate(src, ^bool(const char *key) { + CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup); + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(lookup); + return true; + }); +} + +void +auth_items_copy_xpc(auth_items_t items, const xpc_object_t src) +{ + _auth_items_parse_xpc(items,src); +} + +void +auth_items_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags) +{ + auth_items_iterate(src, ^bool(const char *key) { + if (auth_items_check_flags(src, key, flags)) { + CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup); + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(lookup); + } + return true; + }); +} + +bool +auth_items_iterate(auth_items_t items, auth_items_iterator_t iter) +{ + bool result = false; + CFTypeRef* keys = NULL; + CFTypeRef* values = NULL; + + CFIndex count = CFDictionaryGetCount(items->dictionary); + keys = calloc((size_t)count, sizeof(CFTypeRef)); + require(keys != NULL, done); + + values = calloc((size_t)count, sizeof(CFTypeRef)); + require(values != NULL, done); + + CFDictionaryGetKeysAndValues(items->dictionary, keys, values); + for (CFIndex i = 0; i < count; i++) { + auth_item_t item = (auth_item_t)values[i]; + result = iter(item->data.name); + if (!result) { + break; + } + } + +done: + free_safe(keys); + free_safe(values); + return result; +} + +void +auth_items_set_string(auth_items_t items, const char *key, const char *value) +{ + if (value) { + size_t valLen = strlen(value); + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_STRING && valLen < item->bufLen) { + memcpy(item->data.value, value, valLen+1); // copy null + item->data.valueLength = valLen; + } else { + item = auth_item_create(AI_TYPE_STRING, key, value, valLen, 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } + } +} + +const char * +auth_items_get_string(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type == AI_TYPE_STRING || item->type == AI_TYPE_UNKNOWN)) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i", + item->data.name, item->type, AI_TYPE_STRING); + } +#endif + return auth_item_get_string(item); + } + + return NULL; +} + +void +auth_items_set_data(auth_items_t items, const char *key, const void *value, size_t len) +{ + if (value && len) { + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_DATA && len <= item->bufLen) { + memcpy(item->data.value, value, len); + item->data.valueLength = len; + } else { + item = auth_item_create(AI_TYPE_DATA, key, value, len, 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } + } +} + +const void * +auth_items_get_data(auth_items_t items, const char *key, size_t *len) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i", + item->data.name, item->type, AI_TYPE_DATA); + } +#endif + if (len) { + *len = item->data.valueLength; + } + return item->data.value; + } + + return NULL; +} + +void +auth_items_set_bool(auth_items_t items, const char *key, bool value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_BOOL) { + *(bool*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_BOOL, key, &value, sizeof(bool), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +bool +auth_items_get_bool(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type == AI_TYPE_BOOL || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(bool))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_BOOL, item->data.valueLength, sizeof(bool)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return atoi(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(bool), done); + + return *(bool*)item->data.value; + } + +done: + return false; +} + +void +auth_items_set_int(auth_items_t items, const char *key, int32_t value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_INT) { + *(int32_t*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_INT, key, &value, sizeof(int32_t), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +int32_t +auth_items_get_int(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type ==AI_TYPE_INT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int32_t))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_INT, item->data.valueLength, sizeof(int32_t)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return atoi(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(int32_t), done); + + return *(int32_t*)item->data.value; + } + +done: + return 0; +} + +void +auth_items_set_uint(auth_items_t items, const char *key, uint32_t value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_UINT) { + *(uint32_t*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_UINT, key, &value, sizeof(uint32_t), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +uint32_t +auth_items_get_uint(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type ==AI_TYPE_UINT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint32_t))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_UINT, item->data.valueLength, sizeof(uint32_t)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return (uint32_t)atoi(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(uint32_t), done); + + return *(uint32_t*)item->data.value; + } + +done: + return 0; +} + +void +auth_items_set_int64(auth_items_t items, const char *key, int64_t value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_INT64) { + *(int64_t*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_INT64, key, &value, sizeof(int64_t), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +int64_t +auth_items_get_int64(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type ==AI_TYPE_INT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int64_t))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_INT64, item->data.valueLength, sizeof(int64_t)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return atoll(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(int64_t), done); + + return *(int64_t*)item->data.value; + } + +done: + return 0; +} + +void +auth_items_set_uint64(auth_items_t items, const char *key, uint64_t value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_UINT64) { + *(uint64_t*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_UINT64, key, &value, sizeof(uint64_t), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +uint64_t +auth_items_get_uint64(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type ==AI_TYPE_UINT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint64_t))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_UINT64, item->data.valueLength, sizeof(uint64_t)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return (uint64_t)atoll(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(uint64_t), done); + + return *(uint64_t*)item->data.value; + } + +done: + return 0; +} + +void auth_items_set_double(auth_items_t items, const char *key, double value) +{ + auth_item_t item = _find_item(items,key); + if (item && item->type == AI_TYPE_DOUBLE) { + *(double*)item->data.value = value; + } else { + item = auth_item_create(AI_TYPE_DOUBLE, key, &value, sizeof(double), 0); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } + } +} + +double auth_items_get_double(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { +#if DEBUG + if (!(item->type ==AI_TYPE_DOUBLE || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(double))) { + LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li", + item->data.name, item->type, AI_TYPE_DOUBLE, item->data.valueLength, sizeof(double)); + } +#endif + if (item->type == AI_TYPE_STRING) { + return atof(auth_item_get_string(item)); + } + + require(item->data.value != NULL, done); + require(item->data.valueLength == sizeof(double), done); + + return *(double*)item->data.value; + } + +done: + return 0; +} + +uint32_t auth_items_get_type(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { + return item->type; + } + + return AI_TYPE_UNKNOWN; +} + +size_t auth_items_get_length(auth_items_t items, const char *key) +{ + auth_item_t item = _find_item(items,key); + if (item) { + return item->data.valueLength; + } + + return 0; +} + +void auth_items_set_value(auth_items_t items, const char *key, uint32_t type, uint32_t flags, const void *value, size_t len) +{ + auth_item_t item = auth_item_create(type, key, value, len, flags); + if (item) { + CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); + CFReleaseSafe(item); + } +} + +#pragma mark - +#pragma mark auth_rights_t + +struct _auth_rights_s { + __AUTH_BASE_STRUCT_HEADER__; + + CFMutableArrayRef array; +}; + +static void +_auth_rights_finalize(CFTypeRef value) +{ + auth_rights_t rights = (auth_rights_t)value; + + CFReleaseNull(rights->array); +} + +static Boolean +_auth_rights_equal(CFTypeRef value1, CFTypeRef value2) +{ + auth_rights_t rights1 = (auth_rights_t)value1; + auth_rights_t rights2 = (auth_rights_t)value2; + + return CFEqual(rights1->array, rights2->array); +} + +static CFStringRef +_auth_rights_copy_description(CFTypeRef value) +{ + auth_rights_t rights = (auth_rights_t)value; + return CFCopyDescription(rights->array); +} + +AUTH_TYPE_INSTANCE(auth_rights, + .init = NULL, + .copy = NULL, + .finalize = _auth_rights_finalize, + .equal = _auth_rights_equal, + .hash = NULL, + .copyFormattingDesc = NULL, + .copyDebugDesc = _auth_rights_copy_description + ); + +static CFTypeID auth_rights_get_type_id() +{ + static CFTypeID type_id = _kCFRuntimeNotATypeID; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + type_id = _CFRuntimeRegisterClass(&_auth_type_auth_rights); + }); + + return type_id; +} + +static auth_rights_t +_auth_rights_create() +{ + auth_rights_t rights = NULL; + + rights = (auth_rights_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_rights_get_type_id(), AUTH_CLASS_SIZE(auth_rights), NULL); + require(rights != NULL, done); + + rights->array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + +done: + return rights; +} + +auth_rights_t +auth_rights_create() +{ + auth_rights_t rights = _auth_rights_create(); + require(rights != NULL, done); + +done: + return rights; +} + +auth_rights_t auth_rights_create_with_xpc(const xpc_object_t data) +{ + auth_rights_t rights = _auth_rights_create(); + require(rights != NULL, done); + require(data != NULL, done); + require(xpc_get_type(data) == XPC_TYPE_ARRAY, done); + + xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) { + + auth_item_t item = auth_item_create_with_xpc(value); + if (item) { + CFArrayAppendValue(rights->array, item); + CFReleaseSafe(item); + } + + return true; + }); + +done: + return rights; +} + +xpc_object_t auth_rights_export_xpc(auth_rights_t rights) +{ + xpc_object_t array = xpc_array_create(NULL, 0); + + CFIndex count = CFArrayGetCount(rights->array); + for (CFIndex i = 0; i < count; i++) { + auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i); + xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item); + xpc_array_append_value(array, xpc_data); + xpc_release_safe(xpc_data); + } + + return array; +} + +static auth_item_t +_find_right_item(auth_rights_t rights, const char * key) +{ + auth_item_t item = NULL; + CFStringRef lookup = NULL; + require(key != NULL, done); + + lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + require(lookup != NULL, done); + + CFIndex count = CFArrayGetCount(rights->array); + for (CFIndex i = 0; i < count; i++) { + auth_item_t tmp = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i); + if (tmp && CFEqual(auth_item_get_cf_key(tmp), lookup)) { + item = tmp; + break; + } + } + +done: + CFReleaseSafe(lookup); + return item; +} + +void auth_rights_set_flags(auth_rights_t rights, const char *key, uint32_t flags) +{ + auth_item_t item = _find_right_item(rights,key); + if (item) { + item->data.flags |= flags; + } +} + +void auth_rights_clear_flags(auth_rights_t rights, const char *key, uint32_t flags) +{ + auth_item_t item = _find_right_item(rights,key); + if (item) { + item->data.flags &= ~flags; + } +} + +uint32_t auth_rights_get_flags(auth_rights_t rights, const char *key) +{ + auth_item_t item = _find_right_item(rights,key); + if (item) { + return item->data.flags; + } + + return 0; +} + +bool auth_rights_check_flags(auth_rights_t rights, const char *key, uint32_t flags) +{ + uint32_t current = auth_rights_get_flags(rights,key); + return flags ? (current & flags) != 0 : current == 0; +} + +size_t auth_rights_get_count(auth_rights_t rights) +{ + return (size_t)CFArrayGetCount(rights->array); +} + +void auth_rights_add(auth_rights_t rights, const char *key) +{ + auth_item_t item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0); + if (item) { + CFArrayAppendValue(rights->array, item); + CFReleaseSafe(item); + } +} + +bool auth_rights_exist(auth_rights_t rights, const char *key) +{ + return (_find_right_item(rights,key) != NULL); +} + +void auth_rights_remove(auth_rights_t rights, const char *key) +{ + CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); + CFIndex count = CFArrayGetCount(rights->array); + for (CFIndex i = 0; i < count; i++) { + auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i); + if (CFEqual(auth_item_get_cf_key(item), lookup)) { + CFArrayRemoveValueAtIndex(rights->array, i); + i--; + count--; + } + } + CFReleaseSafe(lookup); +} + +void auth_rights_clear(auth_rights_t rights) +{ + CFArrayRemoveAllValues(rights->array); +} + +bool +auth_rights_iterate(auth_rights_t rights, bool(^iter)(const char * key)) +{ + bool result = false; + + CFIndex count = CFArrayGetCount(rights->array); + for (CFIndex i = 0; i < count; i++) { + auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i); + result = iter(item->data.name); + if (!result) { + break; + } + } + + return result; +}