1/* 2 * Copyright (c) 2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <stdbool.h> 25#include <sys/queue.h> 26#include <syslog.h> 27 28#include <CoreFoundation/CoreFoundation.h> 29#include <Security/SecItem.h> 30#include <Security/SecBasePriv.h> 31#include <Security/SecInternal.h> 32#include <Security/SecuritydXPC.h> 33 34#include <utilities/debugging.h> 35#include <utilities/SecCFError.h> 36#include <utilities/SecXPCError.h> 37#include <utilities/SecCFWrappers.h> 38#include <utilities/SecDispatchRelease.h> 39#include <utilities/SecDb.h> // TODO Fixme this gets us SecError(). 40#include "swcagent_client.h" 41 42#include <xpc/private.h> 43 44 45CFStringRef sSWCAXPCErrorDomain = CFSTR("com.apple.security.swcagent"); 46CFStringRef sSWCASecAttrServer = CFSTR("srvr"); 47 48// 49// MARK: XPC IPC. 50// 51 52static xpc_connection_t swca_create_connection(const char *name) { 53 if (!name) 54 name = kSWCAXPCServiceName; 55 xpc_connection_t connection; 56 connection = xpc_connection_create_mach_service(name, NULL, 0); 57 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { 58 const char *description = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); 59 secnotice("xpc", "got event: %s", description); 60 }); 61 xpc_connection_resume(connection); 62 return connection; 63} 64 65static xpc_connection_t sSWCAConnection; 66 67static xpc_connection_t swca_connection(void) { 68 static dispatch_once_t once; 69 dispatch_once(&once, ^{ 70 sSWCAConnection = swca_create_connection(NULL); 71 }); 72 return sSWCAConnection; 73} 74 75xpc_object_t 76swca_message_with_reply_sync(xpc_object_t message, CFErrorRef *error) 77{ 78 xpc_object_t reply = NULL; 79 xpc_connection_t connection = swca_connection(); 80 81 const int max_tries = 4; // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4. 82 83 unsigned int tries_left = max_tries; 84 do { 85 if (reply) xpc_release(reply); 86 reply = xpc_connection_send_message_with_reply_sync(connection, message); 87 } while (reply == XPC_ERROR_CONNECTION_INTERRUPTED && --tries_left > 0); 88 89 if (xpc_get_type(reply) == XPC_TYPE_ERROR) { 90 CFIndex code = 0; 91 if (reply == XPC_ERROR_CONNECTION_INTERRUPTED || reply == XPC_ERROR_CONNECTION_INVALID) 92 code = kSecXPCErrorConnectionFailed; 93 else if (reply == XPC_ERROR_TERMINATION_IMMINENT) 94 code = kSecXPCErrorUnknown; 95 else 96 code = kSecXPCErrorUnknown; 97 98 char *conn_desc = xpc_copy_description(connection); 99 const char *description = xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION); 100 SecCFCreateErrorWithFormat(code, sSWCAXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description); 101 free(conn_desc); 102 xpc_release(reply); 103 reply = NULL; 104 } 105 106 return reply; 107} 108 109xpc_object_t swca_create_message(enum SWCAXPCOperation op, CFErrorRef* error) 110{ 111 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); 112 if (message) { 113 xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op); 114 } else { 115 SecCFCreateError(kSecXPCErrorConnectionFailed, sSWCAXPCErrorDomain, 116 CFSTR("xpc_dictionary_create returned NULL"), NULL, error); 117 } 118 return message; 119} 120 121// Return true if there is no error in message, return false and set *error if there is. 122bool swca_message_no_error(xpc_object_t message, CFErrorRef *error) { 123 xpc_object_t xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError); 124 if (xpc_error == NULL) 125 return true; 126 127 if (error) { 128 *error = SecCreateCFErrorWithXPCObject(xpc_error); 129 } 130 return false; 131} 132 133// Return int value of message reply (or -1 if error) 134long swca_message_response(xpc_object_t replyMessage, CFErrorRef *error) { 135 int32_t value = -1; 136 CFTypeRef result = NULL; 137 if (!swca_message_no_error(replyMessage, error) || 138 !SecXPCDictionaryCopyPListOptional(replyMessage, kSecXPCKeyResult, &result, error) || 139 !result) { 140 return value; 141 } 142 CFTypeID typeID = CFGetTypeID(result); 143 if (typeID == CFBooleanGetTypeID()) { 144 value = (CFEqual((CFBooleanRef)result, kCFBooleanTrue)) ? 1 : 0; 145 } 146 else if (typeID == CFNumberGetTypeID()) { 147 if (!CFNumberGetValue((CFNumberRef)result, kCFNumberSInt32Type, &value)) { 148 value = -1; 149 } 150 } 151 CFReleaseSafe(result); 152 return value; 153} 154 155bool swca_autofill_enabled(const audit_token_t *auditToken) 156{ 157 bool result = false; 158 CFErrorRef error = NULL; 159 xpc_object_t message = swca_create_message(swca_enabled_request_id, &error); 160 if (message) { 161 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); 162 xpc_object_t reply = swca_message_with_reply_sync(message, &error); 163 if (reply) { 164 long value = swca_message_response(reply, &error); 165 result = (value > 0); 166 xpc_release(reply); 167 } 168 xpc_release(message); 169 } 170 CFReleaseSafe(error); 171 return result; 172} 173 174bool swca_confirm_operation(enum SWCAXPCOperation op, 175 const audit_token_t *auditToken, 176 CFTypeRef query, 177 CFErrorRef *error, 178 void (^add_negative_entry)(CFStringRef fqdn)) 179{ 180 bool result = false; 181 xpc_object_t message = swca_create_message(op, error); 182 if (message) { 183 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); 184 if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error)) { 185 xpc_object_t reply = swca_message_with_reply_sync(message, error); 186 if (reply) { 187 long value = swca_message_response(reply, error); 188 // 189 // possible values (see CFUserNotification.h): 190 // unable to get message response = -1 191 // kCFUserNotificationDefaultResponse = 0 "Don't Allow" (i.e. permanently) 192 // kCFUserNotificationAlternateResponse = 1 "OK" 193 // kCFUserNotificationOtherResponse = 2 (no longer used) 194 // 195 result = (value == 1); 196 if (value == 0 && add_negative_entry) { 197 CFStringRef fqdn = CFDictionaryGetValue(query, sSWCASecAttrServer); 198 add_negative_entry(fqdn); 199 } 200 xpc_release(reply); 201 } 202 } 203 xpc_release(message); 204 } 205 return result; 206} 207 208// Return retained dictionary value of message reply (or NULL if error) 209CFTypeRef swca_message_copy_response(xpc_object_t replyMessage, CFErrorRef *error) { 210 CFTypeRef result = NULL; 211 if (!swca_message_no_error(replyMessage, error) || 212 !SecXPCDictionaryCopyPListOptional(replyMessage, kSecXPCKeyResult, &result, error)) { 213 CFReleaseNull(result); 214 } 215 return result; 216} 217 218CFDictionaryRef swca_copy_selected_dictionary(enum SWCAXPCOperation op, 219 const audit_token_t *auditToken, 220 CFTypeRef items, // array of dictionaries 221 CFErrorRef *error) 222{ 223 CFDictionaryRef result = NULL; 224 xpc_object_t message = swca_create_message(op, error); 225 if (message) { 226 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); 227 if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, items, error)) { 228 xpc_object_t reply = swca_message_with_reply_sync(message, error); 229 if (reply) { 230 result = (CFDictionaryRef) swca_message_copy_response(reply, error); 231 if (!(result && CFGetTypeID(result) == CFDictionaryGetTypeID())) { 232 CFReleaseNull(result); 233 } 234 xpc_release(reply); 235 } 236 } 237 xpc_release(message); 238 } 239 return result; 240} 241 242// Return retained array value of message reply (or NULL if error) 243CFArrayRef swca_copy_pairs(enum SWCAXPCOperation op, 244 const audit_token_t *auditToken, 245 CFErrorRef *error) 246{ 247 CFArrayRef result = NULL; 248 xpc_object_t message = swca_create_message(op, error); 249 if (message) { 250 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); 251 xpc_object_t reply = swca_message_with_reply_sync(message, error); 252 if (reply) { 253 result = (CFArrayRef) swca_message_copy_response(reply, error); 254 if (!(result && CFGetTypeID(result) == CFArrayGetTypeID())) { 255 CFReleaseNull(result); 256 } 257 xpc_release(reply); 258 } 259 xpc_release(message); 260 } 261 return result; 262} 263 264bool swca_set_selection(enum SWCAXPCOperation op, 265 const audit_token_t *auditToken, 266 CFTypeRef dictionary, 267 CFErrorRef *error) 268{ 269 bool result = false; 270 xpc_object_t message = swca_create_message(op, error); 271 if (message) { 272 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t)); 273 if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, dictionary, error)) { 274 xpc_object_t reply = swca_message_with_reply_sync(message, error); 275 if (reply) { 276 long value = swca_message_response(reply, error); 277 if (value != 0) { 278 result = true; 279 }; 280 xpc_release(reply); 281 } 282 } 283 xpc_release(message); 284 } 285 return result; 286} 287 288bool swca_send_sync_and_do(enum SWCAXPCOperation op, CFErrorRef *error, 289 bool (^add_to_message)(xpc_object_t message, CFErrorRef* error), 290 bool (^handle_response)(xpc_object_t response, CFErrorRef* error)) { 291 xpc_object_t message = swca_create_message(op, error); 292 bool ok = false; 293 if (message) { 294 if (!add_to_message || add_to_message(message, error)) { 295 xpc_object_t response = swca_message_with_reply_sync(message, error); 296 if (response) { 297 if (swca_message_no_error(response, error)) { 298 ok = (!handle_response || handle_response(response, error)); 299 } 300 xpc_release(response); 301 } 302 } 303 xpc_release(message); 304 } 305 306 return ok; 307} 308 309 310/* vi:set ts=4 sw=4 et: */ 311