X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/sec/SharedWebCredential/swcagent_client.c diff --git a/Security/sec/SharedWebCredential/swcagent_client.c b/Security/sec/SharedWebCredential/swcagent_client.c new file mode 100644 index 00000000..8b7b4633 --- /dev/null +++ b/Security/sec/SharedWebCredential/swcagent_client.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // TODO Fixme this gets us SecError(). +#include "swcagent_client.h" + +#include + + +CFStringRef sSWCAXPCErrorDomain = CFSTR("com.apple.security.swcagent"); +CFStringRef sSWCASecAttrServer = CFSTR("srvr"); + +// +// MARK: XPC IPC. +// + +static xpc_connection_t swca_create_connection(const char *name) { + if (!name) + name = kSWCAXPCServiceName; + xpc_connection_t connection; + connection = xpc_connection_create_mach_service(name, NULL, 0); + xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { + const char *description = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); + secnotice("xpc", "got event: %s", description); + }); + xpc_connection_resume(connection); + return connection; +} + +static xpc_connection_t sSWCAConnection; + +static xpc_connection_t swca_connection(void) { + static dispatch_once_t once; + dispatch_once(&once, ^{ + sSWCAConnection = swca_create_connection(NULL); + }); + return sSWCAConnection; +} + +xpc_object_t +swca_message_with_reply_sync(xpc_object_t message, CFErrorRef *error) +{ + xpc_object_t reply = NULL; + xpc_connection_t connection = swca_connection(); + + const int max_tries = 4; // Per N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4. + + unsigned int tries_left = max_tries; + do { + if (reply) xpc_release(reply); + reply = xpc_connection_send_message_with_reply_sync(connection, message); + } while (reply == XPC_ERROR_CONNECTION_INTERRUPTED && --tries_left > 0); + + if (xpc_get_type(reply) == XPC_TYPE_ERROR) { + CFIndex code = 0; + if (reply == XPC_ERROR_CONNECTION_INTERRUPTED || reply == XPC_ERROR_CONNECTION_INVALID) + code = kSecXPCErrorConnectionFailed; + else if (reply == XPC_ERROR_TERMINATION_IMMINENT) + code = kSecXPCErrorUnknown; + else + code = kSecXPCErrorUnknown; + + char *conn_desc = xpc_copy_description(connection); + const char *description = xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION); + SecCFCreateErrorWithFormat(code, sSWCAXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description); + free(conn_desc); + xpc_release(reply); + reply = NULL; + } + + return reply; +} + +xpc_object_t swca_create_message(enum SWCAXPCOperation op, CFErrorRef* error) +{ + xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); + if (message) { + xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op); + } else { + SecCFCreateError(kSecXPCErrorConnectionFailed, sSWCAXPCErrorDomain, + CFSTR("xpc_dictionary_create returned NULL"), NULL, error); + } + return message; +} + +// Return true if there is no error in message, return false and set *error if there is. +bool swca_message_no_error(xpc_object_t message, CFErrorRef *error) { + xpc_object_t xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError); + if (xpc_error == NULL) + return true; + + if (error) { + *error = SecCreateCFErrorWithXPCObject(xpc_error); + } + return false; +} + +// Return int value of message reply (or -1 if error) +long swca_message_response(xpc_object_t replyMessage, CFErrorRef *error) { + int32_t value = -1; + CFTypeRef result = NULL; + if (!swca_message_no_error(replyMessage, error) || + !SecXPCDictionaryCopyPListOptional(replyMessage, kSecXPCKeyResult, &result, error) || + !result) { + return value; + } + CFTypeID typeID = CFGetTypeID(result); + if (typeID == CFBooleanGetTypeID()) { + value = (CFEqual((CFBooleanRef)result, kCFBooleanTrue)) ? 1 : 0; + } + else if (typeID == CFNumberGetTypeID()) { + if (!CFNumberGetValue((CFNumberRef)result, kCFNumberSInt32Type, &value)) { + value = -1; + } + } + CFReleaseSafe(result); + return value; +} + +bool swca_autofill_enabled(const audit_token_t *auditToken) +{ + bool result = false; + CFErrorRef error = NULL; + xpc_object_t message = swca_create_message(swca_enabled_request_id, &error); + if (message) { + xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); + xpc_object_t reply = swca_message_with_reply_sync(message, &error); + if (reply) { + long value = swca_message_response(reply, &error); + result = (value > 0); + xpc_release(reply); + } + xpc_release(message); + } + CFReleaseSafe(error); + return result; +} + +bool swca_confirm_operation(enum SWCAXPCOperation op, + const audit_token_t *auditToken, + CFTypeRef query, + CFErrorRef *error, + void (^add_negative_entry)(CFStringRef fqdn)) +{ + bool result = false; + xpc_object_t message = swca_create_message(op, error); + if (message) { + xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); + if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error)) { + xpc_object_t reply = swca_message_with_reply_sync(message, error); + if (reply) { + long value = swca_message_response(reply, error); + // + // possible values (see CFUserNotification.h): + // unable to get message response = -1 + // kCFUserNotificationDefaultResponse = 0 "Don't Allow" (i.e. permanently) + // kCFUserNotificationAlternateResponse = 1 "OK" + // kCFUserNotificationOtherResponse = 2 (no longer used) + // + result = (value == 1); + if (value == 0 && add_negative_entry) { + CFStringRef fqdn = CFDictionaryGetValue(query, sSWCASecAttrServer); + add_negative_entry(fqdn); + } + xpc_release(reply); + } + } + xpc_release(message); + } + return result; +} + +// Return retained dictionary value of message reply (or NULL if error) +CFTypeRef swca_message_copy_response(xpc_object_t replyMessage, CFErrorRef *error) { + CFTypeRef result = NULL; + if (!swca_message_no_error(replyMessage, error) || + !SecXPCDictionaryCopyPListOptional(replyMessage, kSecXPCKeyResult, &result, error)) { + CFReleaseNull(result); + } + return result; +} + +CFDictionaryRef swca_copy_selected_dictionary(enum SWCAXPCOperation op, + const audit_token_t *auditToken, + CFTypeRef items, // array of dictionaries + CFErrorRef *error) +{ + CFDictionaryRef result = NULL; + xpc_object_t message = swca_create_message(op, error); + if (message) { + xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); + if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, items, error)) { + xpc_object_t reply = swca_message_with_reply_sync(message, error); + if (reply) { + result = (CFDictionaryRef) swca_message_copy_response(reply, error); + if (!(result && CFGetTypeID(result) == CFDictionaryGetTypeID())) { + CFReleaseNull(result); + } + xpc_release(reply); + } + } + xpc_release(message); + } + return result; +} + +// Return retained array value of message reply (or NULL if error) +CFArrayRef swca_copy_pairs(enum SWCAXPCOperation op, + const audit_token_t *auditToken, + CFErrorRef *error) +{ + CFArrayRef result = NULL; + xpc_object_t message = swca_create_message(op, error); + if (message) { + xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); + xpc_object_t reply = swca_message_with_reply_sync(message, error); + if (reply) { + result = (CFArrayRef) swca_message_copy_response(reply, error); + if (!(result && CFGetTypeID(result) == CFArrayGetTypeID())) { + CFReleaseNull(result); + } + xpc_release(reply); + } + xpc_release(message); + } + return result; +} + +bool swca_set_selection(enum SWCAXPCOperation op, + const audit_token_t *auditToken, + CFTypeRef dictionary, + CFErrorRef *error) +{ + bool result = false; + xpc_object_t message = swca_create_message(op, error); + if (message) { + xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); + if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, dictionary, error)) { + xpc_object_t reply = swca_message_with_reply_sync(message, error); + if (reply) { + long value = swca_message_response(reply, error); + if (value != 0) { + result = true; + }; + xpc_release(reply); + } + } + xpc_release(message); + } + return result; +} + +bool swca_send_sync_and_do(enum SWCAXPCOperation op, CFErrorRef *error, + bool (^add_to_message)(xpc_object_t message, CFErrorRef* error), + bool (^handle_response)(xpc_object_t response, CFErrorRef* error)) { + xpc_object_t message = swca_create_message(op, error); + bool ok = false; + if (message) { + if (!add_to_message || add_to_message(message, error)) { + xpc_object_t response = swca_message_with_reply_sync(message, error); + if (response) { + if (swca_message_no_error(response, error)) { + ok = (!handle_response || handle_response(response, error)); + } + xpc_release(response); + } + } + xpc_release(message); + } + + return ok; +} + + +/* vi:set ts=4 sw=4 et: */