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 <mach/mach.h> 25#include <mach/message.h> 26 27#include <stdlib.h> 28#include <sys/queue.h> 29#include <libproc.h> 30 31#include <Security/SecInternal.h> 32#include <Security/SecBasePriv.h> 33#include <Security/SecItemPriv.h> /* for SecErrorGetOSStatus */ 34#include <CoreFoundation/CoreFoundation.h> 35#include <CoreFoundation/CFPriv.h> 36 37#include <Foundation/Foundation.h> 38 39#if TARGET_OS_IPHONE 40#include <CoreFoundation/CFUserNotification.h> 41#endif 42#if !TARGET_OS_IPHONE 43#include <CoreFoundation/CFUserNotificationPriv.h> 44#endif 45 46#if TARGET_OS_IPHONE 47#include <SpringBoardServices/SpringBoardServices.h> 48#endif 49 50#if TARGET_OS_IPHONE 51#include <WebUI/WBUAutoFillData.h> 52#endif 53 54#include <asl.h> 55#include <syslog.h> 56#include <bsm/libbsm.h> 57#include <utilities/SecIOFormat.h> 58#include <utilities/debugging.h> 59 60#include <ipc/securityd_client.h> 61#include "swcagent_client.h" 62 63#include <securityd/SecItemServer.h> 64#include <securityd/SecTrustServer.h> 65#include <securityd/SecTrustStoreServer.h> 66#include <securityd/spi.h> 67#include <Security/SecTask.h> 68 69#include <utilities/SecCFWrappers.h> 70#include <utilities/SecCFError.h> 71#include <utilities/SecXPCError.h> 72 73// TODO: Make this include work on both platforms. 74#if TARGET_OS_EMBEDDED 75#include <Security/SecEntitlements.h> 76#else 77/* defines from <Security/SecEntitlements.h> */ 78#define kSecEntitlementAssociatedDomains CFSTR("com.apple.developer.associated-domains") 79#define kSecEntitlementPrivateAssociatedDomains CFSTR("com.apple.private.associated-domains") 80#endif 81 82#include <Security/SecuritydXPC.h> 83 84#include <xpc/xpc.h> 85#include <xpc/private.h> 86#include <xpc/connection_private.h> 87#include <AssertMacros.h> 88 89 90// Local function declarations 91CFStringRef SWCAGetOperationDescription(enum SWCAXPCOperation op); 92bool SWCAIsAutofillEnabled(void); 93 94bool swca_confirm_add(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error); 95bool swca_confirm_copy(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error); 96bool swca_confirm_update(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error); 97bool swca_confirm_delete(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error); 98bool swca_select_item(CFArrayRef items, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error); 99 100CFOptionFlags swca_handle_request(enum SWCAXPCOperation operation, CFStringRef client, CFArrayRef domains); 101bool swca_process_response(CFOptionFlags response, CFTypeRef *result); 102 103static CFArrayRef gActiveArray = NULL; 104static CFDictionaryRef gActiveItem = NULL; 105 106#if TARGET_IPHONE_SIMULATOR 107#define CHECK_ENTITLEMENTS 0 108#else 109#define CHECK_ENTITLEMENTS 0 /* %%% TODO: re-enable when entitlements are available */ 110#endif 111 112#if CHECK_ENTITLEMENTS 113static bool SecTaskGetBooleanValueForEntitlement(SecTaskRef task, CFStringRef entitlement) 114{ 115 CFStringRef canModify = (CFStringRef)SecTaskCopyValueForEntitlement(task, entitlement, NULL); 116 if (!canModify) 117 return false; 118 CFTypeID canModifyType = CFGetTypeID(canModify); 119 bool ok = (CFBooleanGetTypeID() == canModifyType) && CFBooleanGetValue((CFBooleanRef)canModify); 120 CFRelease(canModify); 121 return ok; 122} 123 124static CFArrayRef SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task, CFStringRef entitlement) 125{ 126 CFArrayRef value = (CFArrayRef)SecTaskCopyValueForEntitlement(task, 127 entitlement, NULL); 128 if (value) { 129 if (CFGetTypeID(value) == CFArrayGetTypeID()) { 130 CFIndex ix, count = CFArrayGetCount(value); 131 for (ix = 0; ix < count; ++ix) { 132 CFStringRef string = (CFStringRef)CFArrayGetValueAtIndex(value, ix); 133 if (CFGetTypeID(string) != CFStringGetTypeID()) { 134 CFRelease(value); 135 value = NULL; 136 break; 137 } 138 } 139 } else { 140 CFRelease(value); 141 value = NULL; 142 } 143 } 144 145 return value; 146} 147 148#endif /* CHECK_ENTITLEMENTS */ 149 150 151/* Identify a client */ 152enum { 153 CLIENT_TYPE_BUNDLE_IDENTIFIER, 154 CLIENT_TYPE_EXECUTABLE_PATH, 155}; 156 157@interface Client : NSObject 158@property int client_type; 159@property(retain) NSString *client; 160@property(retain) NSString *client_name; 161@property(retain) NSString *path; 162@property(retain) NSBundle *bundle; 163@end 164@implementation Client 165@end 166static Client *identify_client(pid_t pid) 167{ 168 Client *client = [[Client alloc] init]; 169 if (!client) 170 return nil; 171 172 char path_buf[PROC_PIDPATHINFO_SIZE] = ""; 173 NSURL *path_url; 174 175#if TARGET_OS_IPHONE 176 if (proc_pidpath(pid, path_buf, sizeof(path_buf)) <= 0) { 177 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid); 178 [client release]; 179 return nil; 180 } 181#else 182 size_t path_len = sizeof(path_buf); 183 if (responsibility_get_responsible_for_pid(pid, NULL, NULL, &path_len, path_buf) != 0) { 184 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid); 185 [client release]; 186 return nil; 187 } 188#endif 189 path_buf[sizeof(path_buf) - 1] = '\0'; 190 191 if (!(client.path = [NSString stringWithUTF8String:path_buf]) || 192 !(path_url = [NSURL fileURLWithPath:client.path])) { 193 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid); 194 [client release]; 195 return nil; 196 } 197 198 NSURL *bundle_url; 199 if ((bundle_url = (NSURL *)_CFBundleCopyBundleURLForExecutableURL((__bridge CFURLRef)path_url)) && 200 (client.bundle = [NSBundle bundleWithURL:bundle_url]) && 201 (client.client = [client.bundle bundleIdentifier])) { 202 client.client_type = CLIENT_TYPE_BUNDLE_IDENTIFIER; 203 CFStringRef client_name_cf = NULL; 204#if TARGET_OS_IPHONE 205 client_name_cf = SBSCopyLocalizedApplicationNameForDisplayIdentifier((__bridge CFStringRef)client.client); 206 client.client_name = (NSString *)client_name_cf; 207#else 208 if (!LSCopyDisplayNameForURL((__bridge CFURLRef)bundle_url, &client_name_cf)) 209 client.client_name = (__bridge_transfer NSString *)client_name_cf; 210#endif 211 if (client_name_cf) 212 CFRelease(client_name_cf); 213 } else { 214#if TARGET_OS_IPHONE 215 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without bundle identifier (%s)", path_buf); 216 [client release]; 217 [bundle_url release]; 218 return nil; 219#else 220 client.client_type = CLIENT_TYPE_EXECUTABLE_PATH; 221 CFBooleanRef is_app = NULL; 222 CFStringRef client_name_cf; 223 if (bundle_url && 224 CFURLCopyResourcePropertyForKey((__bridge CFURLRef)bundle_url, _kCFURLIsApplicationKey, &is_app, NULL) && 225 is_app == kCFBooleanTrue) { 226 if ((client.client = [bundle_url path]) && 227 !LSCopyDisplayNameForURL((__bridge CFURLRef)bundle_url, &client_name_cf)) 228 client.client_name = (__bridge_transfer NSString *)client_name_cf; 229 } else { 230 client.client = client.path; 231 if (!LSCopyDisplayNameForURL((__bridge CFURLRef)path_url, &client_name_cf)) 232 client.client_name = (__bridge_transfer NSString *)client_name_cf; 233 } 234 if (is_app) 235 CFRelease(is_app); 236#endif 237 } 238 239 [bundle_url release]; 240 return client; 241} 242 243struct __SecTask { 244 CFRuntimeBase base; 245 246 pid_t pid_self; 247 audit_token_t token; 248 249 /* Track whether we've loaded entitlements independently since after the 250 * load, entitlements may legitimately be NULL */ 251 Boolean entitlementsLoaded; 252 CFDictionaryRef entitlements; 253}; 254 255static CFStringRef SecTaskCopyLocalizedDescription(SecTaskRef task) 256{ 257 // SecTaskCopyDebugDescription is not sufficient to get the localized client name 258 pid_t pid; 259 if (task->pid_self==-1) { 260 audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); 261 } else { 262 pid = task->pid_self; 263 } 264 Client *client = identify_client(pid); 265 CFStringRef clientTaskName = (__bridge CFStringRef)client.client_name; 266 CFRetainSafe(clientTaskName); 267 [client release]; 268 269 return clientTaskName; 270} 271 272static CFArrayRef SecTaskCopyAccessGroups(SecTaskRef task) { 273#if CHECK_ENTITLEMENTS 274 CFArrayRef groups = SecTaskCopyArrayOfStringsForEntitlement(task, kSecEntitlementAssociatedDomains); 275#else 276 CFArrayRef groups = SecAccessGroupsGetCurrent(); 277 CFRetainSafe(groups); 278#endif 279 return groups; 280} 281 282CFStringRef SWCAGetOperationDescription(enum SWCAXPCOperation op) 283{ 284 switch (op) { 285 case swca_add_request_id: 286 return CFSTR("swc add"); 287 case swca_update_request_id: 288 return CFSTR("swc update"); 289 case swca_delete_request_id: 290 return CFSTR("swc delete"); 291 case swca_copy_request_id: 292 return CFSTR("swc copy"); 293 case swca_select_request_id: 294 return CFSTR("swc select"); 295 case swca_copy_pairs_request_id: 296 return CFSTR("swc copy pairs"); 297 case swca_set_selection_request_id: 298 return CFSTR("swc set selection"); 299 case swca_enabled_request_id: 300 return CFSTR("swc enabled"); 301 default: 302 return CFSTR("Unknown xpc operation"); 303 } 304} 305 306bool SWCAIsAutofillEnabled(void) 307{ 308#if TARGET_IPHONE_SIMULATOR 309 // Assume the setting's on in the simulator: <rdar://problem/17057358> WBUAutoFillGetEnabledDataClasses call failing in the Simulator 310 return true; 311#else 312 WBSAutoFillDataClasses autofill = WBUAutoFillGetEnabledDataClasses(); 313 return ((autofill & WBSAutoFillDataClassUsernamesAndPasswords) != 0); 314#endif 315} 316 317CFOptionFlags swca_handle_request(enum SWCAXPCOperation operation, CFStringRef client, CFArrayRef domains) 318{ 319 CFUserNotificationRef notification = NULL; 320 NSMutableDictionary *notification_dictionary = NULL; 321 NSString *security_path = @"/System/Library/Frameworks/Security.framework"; 322 NSString *swc_table = @"SharedWebCredentials"; 323 NSBundle *security_bundle; 324 NSString *request_key; 325 NSString *request_format; 326 NSString *default_button_key; 327 NSString *alternate_button_key; 328 NSString *other_button_key; 329 NSString *info_message_key; 330 NSString *domain; 331 char *op = NULL; 332 CFOptionFlags response = 0 | kCFUserNotificationCancelResponse; 333 BOOL alert_sem_held = NO; 334 335check_database: 336 /* If we have previously allowed this operation/domain for this client, 337 * check and don't prompt again. 338 */ 339 ; /* %%% TBD */ 340 341 /* Only display one alert at a time. */ 342 static dispatch_semaphore_t alert_sem; 343 static dispatch_once_t alert_once; 344 dispatch_once(&alert_once, ^{ 345 if (!(alert_sem = dispatch_semaphore_create(1))) 346 abort(); 347 }); 348 if (!alert_sem_held) { 349 alert_sem_held = YES; 350 if (dispatch_semaphore_wait(alert_sem, DISPATCH_TIME_NOW)) { 351 /* Wait for the active alert, then recheck the database in case both alerts are for the same client. */ 352 //asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Delaying prompt for pid %d", pid); 353 dispatch_semaphore_wait(alert_sem, DISPATCH_TIME_FOREVER); 354 goto check_database; 355 } 356 } 357 358#if TARGET_IPHONE_SIMULATOR 359 security_path = [NSString stringWithFormat:@"%s%@", getenv("IPHONE_SIMULATOR_ROOT"), security_path]; 360#endif 361 security_bundle = [NSBundle bundleWithPath:security_path]; 362 notification_dictionary = [NSMutableDictionary dictionary]; 363 domain = nil; 364 if (1 == [(__bridge NSArray *)domains count]) { 365 domain = (NSString *)[(__bridge NSArray *)domains objectAtIndex:0]; 366 } 367 switch (operation) { 368 case swca_add_request_id: 369 op = "ADD"; 370 break; 371 case swca_update_request_id: 372 op = "UPDATE"; 373 break; 374 case swca_delete_request_id: 375 op = "DELETE"; 376 break; 377 case swca_copy_request_id: 378 op = (domain) ? "COPY" : "COPYALL"; 379 break; 380 default: 381 op = "USE"; 382 break; 383 } 384 if (!op) { 385 goto out; 386 } 387 request_key = [NSString stringWithFormat:@"SWC_REQUEST_%s", op]; 388 request_format = NSLocalizedStringFromTableInBundle(request_key, swc_table, security_bundle, nil); 389 alternate_button_key = (op) ? [NSString stringWithFormat:@"SWC_ALLOW_%s", op] : nil; 390 default_button_key = @"SWC_NEVER"; 391 other_button_key = @"SWC_DENY"; 392 info_message_key = @"SWC_INFO_MESSAGE"; 393 394 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertHeaderKey] = [NSString stringWithFormat:request_format, client, domain]; 395 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertMessageKey] = NSLocalizedStringFromTableInBundle(info_message_key, swc_table, security_bundle, nil); 396 notification_dictionary[(__bridge NSString *)kCFUserNotificationDefaultButtonTitleKey] = NSLocalizedStringFromTableInBundle(default_button_key, swc_table, security_bundle, nil); 397 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlternateButtonTitleKey] = NSLocalizedStringFromTableInBundle(alternate_button_key, swc_table, security_bundle, nil); 398 399 if (other_button_key) { 400 // notification_dictionary[(__bridge NSString *)kCFUserNotificationOtherButtonTitleKey] = NSLocalizedStringFromTableInBundle(other_button_key, swc_table, security_bundle, nil); 401 } 402 notification_dictionary[(__bridge NSString *)kCFUserNotificationLocalizationURLKey] = [security_bundle bundleURL]; 403 404 SInt32 error; 405 if (!(notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationStopAlertLevel | kCFUserNotificationNoDefaultButtonFlag, &error, (__bridge CFDictionaryRef)notification_dictionary)) || 406 error) 407 goto out; 408 if (CFUserNotificationReceiveResponse(notification, 0, &response)) 409 goto out; 410 411out: 412 if (alert_sem_held) 413 dispatch_semaphore_signal(alert_sem); 414 if (notification) 415 CFRelease(notification); 416 return response; 417} 418 419bool swca_process_response(CFOptionFlags response, CFTypeRef *result) 420{ 421 int32_t value = (int32_t)(response & 0x3); 422 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); 423 *result = number; 424 return (NULL != number); 425} 426 427bool swca_confirm_add(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) 428{ 429 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer); 430 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks); 431 CFOptionFlags response = swca_handle_request(swca_add_request_id, clientTaskName, domains); 432 return swca_process_response(response, result); 433} 434 435bool swca_confirm_copy(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) 436{ 437 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer); 438 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks); 439 CFOptionFlags response = swca_handle_request(swca_copy_request_id, clientTaskName, domains); 440 return swca_process_response(response, result); 441} 442 443bool swca_confirm_update(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) 444{ 445 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer); 446 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks); 447 CFOptionFlags response = swca_handle_request(swca_update_request_id, clientTaskName, domains); 448 return swca_process_response(response, result); 449} 450 451bool swca_confirm_delete(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) 452{ 453 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer); 454 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks); 455 CFOptionFlags response = swca_handle_request(swca_delete_request_id, clientTaskName, domains); 456 return swca_process_response(response, result); 457} 458 459bool swca_select_item(CFArrayRef items, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) 460{ 461 CFUserNotificationRef notification = NULL; 462 NSMutableDictionary *notification_dictionary = NULL; 463 NSString *security_path = @"/System/Library/Frameworks/Security.framework"; 464 NSString *swc_table = @"SharedWebCredentials"; 465 NSBundle *security_bundle; 466 NSString *request_title_format; 467 NSString *info_message_key; 468 NSString *default_button_key; 469 NSString *alternate_button_key; 470 CFOptionFlags response = 0 | kCFUserNotificationCancelResponse; 471 CFIndex item_count = (items) ? CFArrayGetCount(items) : (CFIndex) 0; 472 BOOL alert_sem_held = NO; 473 474 if (item_count < 1) { 475 return false; 476 } 477 478entry: 479 ; 480 /* Only display one alert at a time. */ 481 static dispatch_semaphore_t select_alert_sem; 482 static dispatch_once_t select_alert_once; 483 dispatch_once(&select_alert_once, ^{ 484 if (!(select_alert_sem = dispatch_semaphore_create(1))) 485 abort(); 486 }); 487 if (!alert_sem_held) { 488 alert_sem_held = YES; 489 if (dispatch_semaphore_wait(select_alert_sem, DISPATCH_TIME_NOW)) { 490 /* Wait for the active alert */ 491 dispatch_semaphore_wait(select_alert_sem, DISPATCH_TIME_FOREVER); 492 goto entry; 493 } 494 } 495 496 CFRetainSafe(items); 497 CFReleaseSafe(gActiveArray); 498 gActiveArray = items; 499 CFReleaseSafe(gActiveItem); 500 gActiveItem = NULL; // selection will be set by remote view controller 501 //gActiveItem = CFArrayGetValueAtIndex(items, 0); 502 //CFRetainSafe(gActiveItem); 503 504#if TARGET_IPHONE_SIMULATOR 505 security_path = [NSString stringWithFormat:@"%s%@", getenv("IPHONE_SIMULATOR_ROOT"), security_path]; 506#endif 507 security_bundle = [NSBundle bundleWithPath:security_path]; 508 notification_dictionary = [NSMutableDictionary dictionary]; 509 510 request_title_format = NSLocalizedStringFromTableInBundle(@"SWC_ALERT_TITLE", swc_table, security_bundle, nil); 511 default_button_key = @"SWC_ALLOW_USE"; 512 alternate_button_key = @"SWC_CANCEL"; 513 info_message_key = @"SWC_INFO_MESSAGE"; 514 515 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertHeaderKey] = [NSString stringWithFormat: request_title_format, clientTaskName]; 516 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertMessageKey] = NSLocalizedStringFromTableInBundle(info_message_key, swc_table, security_bundle, nil); 517 notification_dictionary[(__bridge NSString *)kCFUserNotificationDefaultButtonTitleKey] = NSLocalizedStringFromTableInBundle(default_button_key, swc_table, security_bundle, nil); 518 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlternateButtonTitleKey] = NSLocalizedStringFromTableInBundle(alternate_button_key, swc_table, security_bundle, nil); 519 520 notification_dictionary[(__bridge NSString *)kCFUserNotificationLocalizationURLKey] = [security_bundle bundleURL]; 521 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertTopMostKey] = [NSNumber numberWithBool:YES]; 522 523 // additional keys for remote view controller 524 notification_dictionary[(__bridge NSString *)SBUserNotificationDismissOnLock] = [NSNumber numberWithBool:YES]; 525 notification_dictionary[(__bridge NSString *)SBUserNotificationDontDismissOnUnlock] = [NSNumber numberWithBool:YES]; 526 notification_dictionary[(__bridge NSString *)SBUserNotificationRemoteServiceBundleIdentifierKey] = @"com.apple.SharedWebCredentialViewService"; 527 notification_dictionary[(__bridge NSString *)SBUserNotificationRemoteViewControllerClassNameKey] = @"SWCViewController"; 528 529 SInt32 err; 530 if (!(notification = CFUserNotificationCreate(NULL, 0, 0, &err, (__bridge CFDictionaryRef)notification_dictionary)) || 531 err) 532 goto out; 533 if (CFUserNotificationReceiveResponse(notification, 0, &response)) 534 goto out; 535 536 //NSLog(@"Selection: %@, Response: %lu", gActiveItem, (unsigned long)response); 537 if (result && response == kCFUserNotificationDefaultResponse) { 538 CFRetainSafe(gActiveItem); 539 *result = gActiveItem; 540 } 541 542out: 543 if (alert_sem_held) { 544 dispatch_semaphore_signal(select_alert_sem); 545 } 546 CFReleaseSafe(notification); 547 CFReleaseNull(gActiveArray); 548 CFReleaseNull(gActiveItem); 549 550 return (result && *result); 551} 552 553 554static void swca_xpc_dictionary_handler(const xpc_connection_t connection, xpc_object_t event) { 555 xpc_type_t type = xpc_get_type(event); 556 __block CFErrorRef error = NULL; 557 xpc_object_t xpcError = NULL; 558 xpc_object_t replyMessage = NULL; 559 SecTaskRef clientTask = NULL; 560 CFStringRef clientTaskName = NULL; 561 CFArrayRef accessGroups = NULL; 562 563 secdebug("swcagent_xpc", "entering"); 564 if (type == XPC_TYPE_DICTIONARY) { 565 replyMessage = xpc_dictionary_create_reply(event); 566 567 uint64_t operation = xpc_dictionary_get_uint64(event, kSecXPCKeyOperation); 568 secdebug("swcagent_xpc", "operation: %@ (%" PRIu64 ")", SWCAGetOperationDescription((enum SWCAXPCOperation)operation), operation); 569 570 bool hasEntitlement; 571 audit_token_t auditToken = {}; 572#if !CHECK_ENTITLEMENTS 573 hasEntitlement = true; 574#else 575 // check our caller's private entitlement to invoke swcagent 576 // TODO: on iOS it's enough to have the entitlement; will need to check code requirement on OS X 577 xpc_connection_get_audit_token(connection, &auditToken); 578 clientTask = SecTaskCreateWithAuditToken(kCFAllocatorDefault, auditToken); 579 hasEntitlement = (clientTask && SecTaskGetBooleanValueForEntitlement(clientTask, kSecEntitlementPrivateAssociatedDomains)); 580 if (!hasEntitlement) { 581 CFErrorRef entitlementError = NULL; 582 SecError(errSecMissingEntitlement, &entitlementError, CFSTR("%@: %@ lacks entitlement %@"), SOSCCGetOperationDescription((enum SWCAXPCOperation)operation), clientTask, kSecEntitlementPrivateAssociatedDomains); 583 CFReleaseSafe(entitlementError); 584 } 585 CFReleaseNull(clientTask); 586#endif 587 588 if (hasEntitlement) { 589 // fetch audit token for process which called securityd 590 size_t length = 0; 591 const uint8_t *bytes = xpc_dictionary_get_data(event, kSecXPCKeyClientToken, &length); 592 if (length == sizeof(audit_token_t)) { 593 memcpy(&auditToken, bytes, sizeof(audit_token_t)); 594 } else { 595 secerror("swcagent_xpc - wrong length for client id"); 596 hasEntitlement = false; 597 } 598 } 599 if (hasEntitlement) { 600 // identify original client 601 clientTask = SecTaskCreateWithAuditToken(kCFAllocatorDefault, auditToken); 602 accessGroups = SecTaskCopyAccessGroups(clientTask); 603 clientTaskName = SecTaskCopyLocalizedDescription(clientTask); 604#if CHECK_ENTITLEMENTS 605 // check for presence of original client's shared credential entitlement 606 hasEntitlement = (clientTask && SecTaskGetBooleanValueForEntitlement(clientTask, kSecEntitlementAssociatedDomains)); 607 if (!hasEntitlement) { 608 CFErrorRef entitlementError = NULL; 609 SecError(errSecMissingEntitlement, &entitlementError, CFSTR("%@: %@ lacks entitlement %@"), SOSCCGetOperationDescription((enum SWCAXPCOperation)operation), clientTask, kSecEntitlementAssociatedDomains); 610 CFReleaseSafe(entitlementError); 611 } 612#endif 613 CFReleaseNull(clientTask); 614 } 615 616 if (hasEntitlement) { 617 switch (operation) 618 { 619 case swca_add_request_id: 620 { 621 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error); 622 //secdebug("ipc", "swcagent: got swca_add_request_id, query: %@", query); 623 if (query) { 624 CFTypeRef result = NULL; 625 // confirm that we can add this item 626 if (swca_confirm_add(query, clientTaskName, accessGroups, &result, &error) && result) { 627 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 628 CFRelease(result); 629 } 630 CFRelease(query); 631 } 632 break; 633 } 634 case swca_copy_request_id: 635 { 636 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error); 637 //secdebug("ipc", "swcagent: got swca_copy_request_id, query: %@", query); 638 if (query) { 639 CFTypeRef result = NULL; 640 // confirm that we can copy this item 641 if (swca_confirm_copy(query, clientTaskName, accessGroups, &result, &error) && result) { 642 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 643 CFRelease(result); 644 } 645 CFRelease(query); 646 } 647 break; 648 } 649 case swca_update_request_id: 650 { 651 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error); 652 //secdebug("ipc", "swcagent: got swca_update_request_id, query: %@", query); 653 if (query) { 654 CFTypeRef result = NULL; 655 // confirm that we can copy this item 656 if (swca_confirm_update(query, clientTaskName, accessGroups, &result, &error) && result) { 657 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 658 CFRelease(result); 659 } 660 CFRelease(query); 661 } 662 break; 663 } 664 case swca_delete_request_id: 665 { 666 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error); 667 //secdebug("ipc", "swcagent: got swca_delete_request_id, query: %@", query); 668 if (query) { 669 CFTypeRef result = NULL; 670 // confirm that we can copy this item 671 if (swca_confirm_delete(query, clientTaskName, accessGroups, &result, &error) && result) { 672 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 673 CFRelease(result); 674 } 675 CFRelease(query); 676 } 677 break; 678 } 679 case swca_select_request_id: 680 { 681 CFArrayRef items = SecXPCDictionaryCopyArray(event, kSecXPCKeyQuery, &error); 682 secdebug("ipc", "swcagent: got swca_select_request_id, items: %@", items); 683 if (items) { 684 CFTypeRef result = NULL; 685 // select a dictionary from an input array of dictionaries 686 if (swca_select_item(items, clientTaskName, accessGroups, &result, &error) && result) { 687 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 688 CFRelease(result); 689 } 690 CFRelease(items); 691 } 692 break; 693 } 694 case swca_copy_pairs_request_id: 695 { 696 secdebug("ipc", "swcagent: got swca_copy_pairs_request_id"); 697 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, gActiveArray, &error); 698 break; 699 } 700 case swca_set_selection_request_id: 701 { 702 CFDictionaryRef dict = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error); 703 secdebug("ipc", "swcagent: got swca_set_selection_request_id, dict: %@", dict); 704 if (dict) { 705 int32_t value = (int32_t) 1; 706 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); 707 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, number, &error); 708 CFReleaseSafe(number); 709 } 710 CFReleaseSafe(gActiveItem); 711 gActiveItem = dict; 712 break; 713 } 714 case swca_enabled_request_id: 715 { 716 // return Safari's password autofill enabled status 717 CFTypeRef result = (SWCAIsAutofillEnabled()) ? kCFBooleanTrue : kCFBooleanFalse; 718 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error); 719 } 720 default: 721 secdebug("ipc", "swcagent: got unsupported request id (%ld)", (long)operation); 722 break; 723 } 724 } 725 if (error) 726 { 727 if(SecErrorGetOSStatus(error) == errSecItemNotFound) 728 secdebug("ipc", "%@ %@ %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), error); 729 else 730 secerror("%@ %@ %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), error); 731 732 xpcError = SecCreateXPCObjectWithCFError(error); 733 xpc_dictionary_set_value(replyMessage, kSecXPCKeyError, xpcError); 734 } else if (replyMessage) { 735 secdebug("ipc", "%@ %@ responding %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), replyMessage); 736 } 737 } else { 738 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, &error, 0, CFSTR("Messages expect to be xpc dictionary, got: %@"), event); 739 secerror("%@: returning error: %@", clientTask, error); 740 xpcError = SecCreateXPCObjectWithCFError(error); 741 replyMessage = xpc_create_reply_with_format(event, "{%string: %value}", kSecXPCKeyError, xpcError); 742 } 743 744 if (replyMessage) { 745 xpc_connection_send_message(connection, replyMessage); 746 xpc_release(replyMessage); 747 } 748 if (xpcError) { 749 xpc_release(xpcError); 750 } 751 CFReleaseSafe(error); 752 CFReleaseSafe(accessGroups); 753 CFReleaseSafe(clientTaskName); 754} 755 756static void swca_xpc_init() 757{ 758 secdebug("swcagent_xpc", "start"); 759 760 xpc_connection_t listener = xpc_connection_create_mach_service(kSWCAXPCServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); 761 if (!listener) { 762 seccritical("swcagent failed to register xpc listener, exiting"); 763 abort(); 764 } 765 766 xpc_connection_set_event_handler(listener, ^(xpc_object_t connection) { 767 if (xpc_get_type(connection) == XPC_TYPE_CONNECTION) { 768 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { 769 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) { 770 xpc_retain(connection); 771 xpc_retain(event); 772 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 773 swca_xpc_dictionary_handler(connection, event); 774 xpc_release(event); 775 xpc_release(connection); 776 }); 777 } 778 }); 779 xpc_connection_resume(connection); 780 } 781 }); 782 xpc_connection_resume(listener); 783} 784 785int main(int argc, char *argv[]) 786{ 787 @autoreleasepool { 788 char *wait4debugger = getenv("WAIT4DEBUGGER"); 789 if (wait4debugger && !strcasecmp("YES", wait4debugger)) { 790 seccritical("SIGSTOPing self, awaiting debugger"); 791 kill(getpid(), SIGSTOP); 792 asl_log(NULL, NULL, ASL_LEVEL_CRIT, 793 "Again, for good luck (or bad debuggers)"); 794 kill(getpid(), SIGSTOP); 795 } 796 swca_xpc_init(); 797 dispatch_main(); 798 } 799 return 0; 800} 801 802/* vi:set ts=4 sw=4 et: */ 803