/*- * Copyright (c) 2013 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Portions Copyright (c) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #import #import #import #import "heimcred.h" #import "common.h" #import "heimbase.h" /* * */ static void HeimItemNotify(xpc_object_t object); static void HeimWakeupVersion(void); /* * */ #define HC_INIT() do { _HeimCredInit(); } while(0) #define HC_INIT_ERROR(_error) \ do { \ if (_error) { \ *(_error) = NULL; \ } \ HC_INIT(); \ } while(0) /* * */ static void _HeimCredInit(void) { static dispatch_once_t once; dispatch_once(&once, ^{ _HeimCredInitCommon(); HeimCredCTX.conn = xpc_connection_create("com.apple.GSSCred", HeimCredCTX.queue); xpc_connection_set_event_handler(HeimCredCTX.conn, ^(xpc_object_t object){ HeimItemNotify(object); }); xpc_connection_resume(HeimCredCTX.conn); HeimWakeupVersion(); }); } /* * Let the server know we are awake */ static void HeimWakeupVersion(void) { if (HeimCredCTX.conn == NULL) abort(); xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(request, "command", "wakeup"); xpc_dictionary_set_int64(request, "version", 0); xpc_connection_send_message(HeimCredCTX.conn, request); xpc_release(request); } #if 0 static NSDictionary * heim_send_message_with_reply_sync(NSDictionary *message) { NSDictionary *reply = nil; NSMutableData *request; size_t length; void *ptr; xpc_object_t xrequest; request = [NSMutableData data]; [HeimCredDecoder archiveRootObject:message toData:request]; xrequest = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_data(xrequest, "data", [request mutableBytes], [request length]); xpc_object_t xreply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, xrequest); ptr = xpc_dictionary_get_value(xreply, "data", &length); NSData *data = [NSData dataWithBytes:ptr length:length]; reply = [HeimCredDecoder copyUnarchiveObjectWithData:data]; return reply; } #endif /* * */ static void HeimItemNotify(xpc_object_t object) { CFUUIDRef uuid; if (object == XPC_TYPE_ERROR) return; uuid = HeimCredCopyUUID(object, "uuid"); if (uuid) CFDictionaryRemoveValue(HeimCredCTX.items, uuid); CFRELEASE_NULL(uuid); } /* * */ /* * */ static HeimCredRef HeimCredAddItem(xpc_object_t object) { CFDictionaryRef attributes = HeimCredMessageCopyAttributes(object, "attributes"); if (attributes == NULL) return NULL; CFUUIDRef uuid = CFDictionaryGetValue(attributes, kHEIMAttrUUID); if (uuid == NULL) { if (attributes) CFRelease(attributes); return NULL; } HeimCredRef cred = HeimCredCreateItem(uuid); if (cred == NULL) { if (attributes) CFRelease(attributes); return NULL; } cred->attributes = attributes; dispatch_sync(HeimCredCTX.queue, ^{ CFDictionarySetValue(HeimCredCTX.items, cred->uuid, cred); }); return cred; } /* * */ HeimCredRef HeimCredCreate(CFDictionaryRef attributes, CFErrorRef *error) { __block HeimCredRef cred; HC_INIT_ERROR(error); xpc_object_t xpcattrs = _CFXPCCreateXPCObjectFromCFObject(attributes); if (xpcattrs == NULL) return NULL; xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); heim_assert(request != NULL, "xpc_dictionary_create"); xpc_dictionary_set_string(request, "command", "create"); xpc_dictionary_set_value(request, "attributes", xpcattrs); xpc_release(xpcattrs); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, request); xpc_release(request); if (reply == NULL) return NULL; heim_assert(reply != XPC_ERROR_CONNECTION_INTERRUPTED, "got XPC_ERROR_CONNECTION_INTERRUPTED"); heim_assert(reply != XPC_ERROR_CONNECTION_INVALID, "got XPC_ERROR_CONNECTION_INVALID"); cred = HeimCredAddItem(reply); xpc_release(reply); return cred; } /* * */ CFUUIDRef HeimCredGetUUID(HeimCredRef cred) { return cred->uuid; } /* * */ HeimCredRef HeimCredCopyFromUUID(CFUUIDRef uuid) { __block HeimCredRef cred; HC_INIT(); dispatch_sync(HeimCredCTX.queue, ^{ cred = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, uuid); if (cred == NULL) { cred = HeimCredCreateItem(uuid); CFDictionarySetValue(HeimCredCTX.items, uuid, cred); } else { CFRetain(cred); } }); return cred; } /* * */ bool HeimCredSetAttribute(HeimCredRef cred, CFTypeRef key, CFTypeID value, CFErrorRef *error) { const void *keys[1] = { (void *)key }; const void *values[1] = { (void *)value }; CFDictionaryRef attrs = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (attrs == NULL) return false; bool ret = HeimCredSetAttributes(cred, attrs, error); CFRelease(attrs); return ret; } /* * */ bool HeimCredSetAttributes(HeimCredRef cred, CFDictionaryRef attributes, CFErrorRef *error) { HC_INIT_ERROR(error); xpc_object_t xpcquery = _CFXPCCreateXPCObjectFromCFObject(attributes); if (xpcquery == NULL) return false; xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(request, "command", "setattributes"); HeimCredSetUUID(request, "uuid", cred->uuid); xpc_dictionary_set_value(request, "attributes", xpcquery); xpc_release(xpcquery); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, request); xpc_release(request); if (reply == NULL) return false; dispatch_sync(HeimCredCTX.queue, ^{ CFRELEASE_NULL(cred->attributes); cred->attributes = HeimCredMessageCopyAttributes(reply, "attributes"); }); xpc_release(reply); return true; } CFTypeRef HeimCredCopyAttribute(HeimCredRef cred, CFTypeRef attribute) { CFDictionaryRef attrs = HeimCredCopyAttributes(cred, NULL, NULL); if (attrs == NULL) return NULL; CFTypeRef ref = CFDictionaryGetValue(attrs, attribute); if (ref) CFRetain(ref); CFRelease(attrs); return ref; } /* * */ CFDictionaryRef HeimCredCopyAttributes(HeimCredRef cred, CFSetRef attributes, CFErrorRef *error) { __block CFDictionaryRef attrs; HC_INIT_ERROR(error); dispatch_sync(HeimCredCTX.queue, ^{ if (cred->attributes) CFRetain(cred->attributes); attrs = cred->attributes; }); if (attrs == NULL) { xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(request, "command", "fetch"); HeimCredSetUUID(request, "uuid", cred->uuid); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, request); xpc_release(request); if (reply == NULL) return NULL; dispatch_sync(HeimCredCTX.queue, ^{ CFRELEASE_NULL(cred->attributes); attrs = cred->attributes = HeimCredMessageCopyAttributes(reply, "attributes"); if (attrs) CFRetain(attrs); }); xpc_release(reply); } return attrs; } /* * */ static xpc_object_t SendQueryCommand(const char *command, CFDictionaryRef query) { xpc_object_t xpcquery = _CFXPCCreateXPCObjectFromCFObject(query); if (xpcquery == NULL) return NULL; xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(request, "command", command); xpc_dictionary_set_value(request, "query", xpcquery); xpc_release(xpcquery); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, request); xpc_release(request); return reply; } /* * */ CFArrayRef HeimCredCopyQuery(CFDictionaryRef query) { HC_INIT(); xpc_object_t reply = SendQueryCommand("query", query); if (reply == NULL) return NULL; CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (result == NULL) { xpc_release(reply); return NULL; } xpc_object_t objects = xpc_dictionary_get_value(reply, "items"); if (objects && xpc_get_type(objects) == XPC_TYPE_ARRAY) { xpc_array_apply(objects, ^bool(size_t index, xpc_object_t value) { CFUUIDRef uuid =_CFXPCCreateCFObjectFromXPCObject(value); if (uuid == NULL) return (bool)true; HeimCredRef cred = HeimCredCreateItem(uuid); CFRelease(uuid); if (cred) { CFArrayAppendValue(result, cred); CFRelease(cred); } return (bool)true; }); } xpc_release(reply); return result; } /* * */ bool HeimCredDeleteQuery(CFDictionaryRef query, CFErrorRef *error) { HC_INIT_ERROR(error); xpc_object_t reply = SendQueryCommand("delete", query); if (reply == NULL) return NULL; xpc_release(reply); return false; } static xpc_object_t SendItemCommand(const char *command, CFUUIDRef uuid) { const void *keys[1] = { (void *)kHEIMAttrUUID }; const void *values[1] = { (void *)uuid }; CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); xpc_object_t reply = SendQueryCommand(command, query); CFRelease(query); return reply; } void HeimCredDeleteByUUID(CFUUIDRef uuid) { HC_INIT(); xpc_object_t reply = SendItemCommand("delete", uuid); if (reply) xpc_release(reply); } /* * */ void HeimCredDelete(HeimCredRef cred) { HeimCredDeleteByUUID(cred->uuid); } /* * */ void HeimCredRetainTransient(HeimCredRef cred) { HC_INIT(); xpc_object_t reply = SendItemCommand("retain-transient", cred->uuid); if (reply) xpc_release(reply); } /* * */ void HeimCredReleaseTransient(HeimCredRef cred) { HC_INIT(); xpc_object_t reply = SendItemCommand("release-transient", cred->uuid); if (reply) xpc_release(reply); } bool HeimCredMove(CFUUIDRef from, CFUUIDRef to) { HC_INIT(); xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(request, "command", "move"); HeimCredSetUUID(request, "from", from); HeimCredSetUUID(request, "to", to); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(HeimCredCTX.conn, request); xpc_release(request); xpc_release(reply); return true; } /* * */ CFDictionaryRef HeimCredDoAuth(HeimCredRef cred, CFDictionaryRef input) { HC_INIT(); return NULL; } /* typedef struct HeimAuth_s *HeimAuthRef; HeimAuthRef HeimCreateAuthetication(CFDictionaryRef input); bool HeimAuthStep(HeimAuthRef cred, CFTypeRef input, CFTypeRef *output, CFErrorRef *error); */