2 * Copyright (c) 2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <mach/mach.h>
25 #include <mach/message.h>
28 #include <sys/queue.h>
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>
37 #include <Foundation/Foundation.h>
40 #include <CoreFoundation/CFUserNotification.h>
43 #include <CoreFoundation/CFUserNotificationPriv.h>
47 #include <SpringBoardServices/SpringBoardServices.h>
50 #if TARGET_OS_IPHONE && !TARGET_OS_NANO
52 #include <WebUI/WBUAutoFillData.h>
54 typedef WBSAutoFillDataClasses (*WBUAutoFillGetEnabledDataClasses_f)(void);
59 #include <bsm/libbsm.h>
60 #include <utilities/SecIOFormat.h>
61 #include <utilities/debugging.h>
63 #include <ipc/securityd_client.h>
64 #include "swcagent_client.h"
66 #include <securityd/SecItemServer.h>
67 #include <securityd/SecTrustServer.h>
68 #include <securityd/SecTrustStoreServer.h>
69 #include <securityd/spi.h>
70 #include <Security/SecTask.h>
72 #include <utilities/SecCFWrappers.h>
73 #include <utilities/SecCFError.h>
74 #include <utilities/SecXPCError.h>
76 // TODO: Make this include work on both platforms.
77 #if TARGET_OS_EMBEDDED
78 #include <Security/SecEntitlements.h>
80 /* defines from <Security/SecEntitlements.h> */
81 #define kSecEntitlementAssociatedDomains CFSTR("com.apple.developer.associated-domains")
82 #define kSecEntitlementPrivateAssociatedDomains CFSTR("com.apple.private.associated-domains")
85 #include <Security/SecuritydXPC.h>
88 #include <xpc/private.h>
89 #include <xpc/connection_private.h>
90 #include <AssertMacros.h>
93 // Local function declarations
94 CFStringRef SWCAGetOperationDescription(enum SWCAXPCOperation op);
95 bool SWCAIsAutofillEnabled(void);
97 bool swca_confirm_add(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
98 bool swca_confirm_copy(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
99 bool swca_confirm_update(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
100 bool swca_confirm_delete(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
101 bool swca_select_item(CFArrayRef items, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
103 CFOptionFlags swca_handle_request(enum SWCAXPCOperation operation, CFStringRef client, CFArrayRef domains);
104 bool swca_process_response(CFOptionFlags response, CFTypeRef *result);
106 static CFArrayRef gActiveArray = NULL;
107 static CFDictionaryRef gActiveItem = NULL;
109 #if TARGET_IPHONE_SIMULATOR
110 #define CHECK_ENTITLEMENTS 0
112 #define CHECK_ENTITLEMENTS 0 /* %%% TODO: re-enable when entitlements are available */
115 #if CHECK_ENTITLEMENTS
116 static bool SecTaskGetBooleanValueForEntitlement(SecTaskRef task, CFStringRef entitlement)
118 CFStringRef canModify = (CFStringRef)SecTaskCopyValueForEntitlement(task, entitlement, NULL);
121 CFTypeID canModifyType = CFGetTypeID(canModify);
122 bool ok = (CFBooleanGetTypeID() == canModifyType) && CFBooleanGetValue((CFBooleanRef)canModify);
123 CFRelease(canModify);
127 static CFArrayRef SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task, CFStringRef entitlement)
129 CFArrayRef value = (CFArrayRef)SecTaskCopyValueForEntitlement(task,
132 if (CFGetTypeID(value) == CFArrayGetTypeID()) {
133 CFIndex ix, count = CFArrayGetCount(value);
134 for (ix = 0; ix < count; ++ix) {
135 CFStringRef string = (CFStringRef)CFArrayGetValueAtIndex(value, ix);
136 if (CFGetTypeID(string) != CFStringGetTypeID()) {
151 #endif /* CHECK_ENTITLEMENTS */
154 /* Identify a client */
156 CLIENT_TYPE_BUNDLE_IDENTIFIER,
157 CLIENT_TYPE_EXECUTABLE_PATH,
160 @interface Client : NSObject
161 @property int client_type;
162 @property(retain) NSString *client;
163 @property(retain) NSString *client_name;
164 @property(retain) NSString *path;
165 @property(retain) NSBundle *bundle;
167 @implementation Client
169 static Client *identify_client(pid_t pid)
171 Client *client = [[Client alloc] init];
175 char path_buf[PROC_PIDPATHINFO_SIZE] = "";
179 if (proc_pidpath(pid, path_buf, sizeof(path_buf)) <= 0) {
180 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid);
185 size_t path_len = sizeof(path_buf);
186 if (responsibility_get_responsible_for_pid(pid, NULL, NULL, &path_len, path_buf) != 0) {
187 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid);
192 path_buf[sizeof(path_buf) - 1] = '\0';
194 if (!(client.path = [NSString stringWithUTF8String:path_buf]) ||
195 !(path_url = [NSURL fileURLWithPath:client.path])) {
196 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without path (pid %d)", pid);
202 if ((bundle_url = (NSURL *)_CFBundleCopyBundleURLForExecutableURL((__bridge CFURLRef)path_url)) &&
203 (client.bundle = [NSBundle bundleWithURL:bundle_url]) &&
204 (client.client = [client.bundle bundleIdentifier])) {
205 client.client_type = CLIENT_TYPE_BUNDLE_IDENTIFIER;
206 CFStringRef client_name_cf = NULL;
208 client_name_cf = SBSCopyLocalizedApplicationNameForDisplayIdentifier((__bridge CFStringRef)client.client);
209 client.client_name = (NSString *)client_name_cf;
211 if (!LSCopyDisplayNameForURL((__bridge CFURLRef)bundle_url, &client_name_cf))
212 client.client_name = (__bridge_transfer NSString *)client_name_cf;
215 CFRelease(client_name_cf);
218 asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Refusing client without bundle identifier (%s)", path_buf);
220 [bundle_url release];
223 client.client_type = CLIENT_TYPE_EXECUTABLE_PATH;
224 CFBooleanRef is_app = NULL;
225 CFStringRef client_name_cf;
227 CFURLCopyResourcePropertyForKey((__bridge CFURLRef)bundle_url, _kCFURLIsApplicationKey, &is_app, NULL) &&
228 is_app == kCFBooleanTrue) {
229 if ((client.client = [bundle_url path]) &&
230 !LSCopyDisplayNameForURL((__bridge CFURLRef)bundle_url, &client_name_cf))
231 client.client_name = (__bridge_transfer NSString *)client_name_cf;
233 client.client = client.path;
234 if (!LSCopyDisplayNameForURL((__bridge CFURLRef)path_url, &client_name_cf))
235 client.client_name = (__bridge_transfer NSString *)client_name_cf;
242 [bundle_url release];
252 /* Track whether we've loaded entitlements independently since after the
253 * load, entitlements may legitimately be NULL */
254 Boolean entitlementsLoaded;
255 CFDictionaryRef entitlements;
258 static CFStringRef SecTaskCopyLocalizedDescription(SecTaskRef task)
260 // SecTaskCopyDebugDescription is not sufficient to get the localized client name
262 if (task->pid_self==-1) {
263 audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
265 pid = task->pid_self;
267 Client *client = identify_client(pid);
268 CFStringRef clientTaskName = (__bridge CFStringRef)client.client_name;
269 CFRetainSafe(clientTaskName);
272 return clientTaskName;
275 static CFArrayRef SecTaskCopyAccessGroups(SecTaskRef task) {
276 #if CHECK_ENTITLEMENTS
277 CFArrayRef groups = SecTaskCopyArrayOfStringsForEntitlement(task, kSecEntitlementAssociatedDomains);
279 CFArrayRef groups = SecAccessGroupsGetCurrent();
280 CFRetainSafe(groups);
285 CFStringRef SWCAGetOperationDescription(enum SWCAXPCOperation op)
288 case swca_add_request_id:
289 return CFSTR("swc add");
290 case swca_update_request_id:
291 return CFSTR("swc update");
292 case swca_delete_request_id:
293 return CFSTR("swc delete");
294 case swca_copy_request_id:
295 return CFSTR("swc copy");
296 case swca_select_request_id:
297 return CFSTR("swc select");
298 case swca_copy_pairs_request_id:
299 return CFSTR("swc copy pairs");
300 case swca_set_selection_request_id:
301 return CFSTR("swc set selection");
302 case swca_enabled_request_id:
303 return CFSTR("swc enabled");
305 return CFSTR("Unknown xpc operation");
309 #if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE && !TARGET_OS_NANO
310 static dispatch_once_t sWBUInitializeOnce = 0;
311 static void * sWBULibrary = NULL;
312 static WBUAutoFillGetEnabledDataClasses_f sWBUAutoFillGetEnabledDataClasses_f = NULL;
314 static OSStatus _SecWBUEnsuredInitialized(void);
316 static OSStatus _SecWBUEnsuredInitialized(void)
318 __block OSStatus status = errSecNotAvailable;
320 dispatch_once(&sWBUInitializeOnce, ^{
321 sWBULibrary = dlopen("/System/Library/PrivateFrameworks/WebUI.framework/WebUI", RTLD_LAZY | RTLD_LOCAL);
324 sWBUAutoFillGetEnabledDataClasses_f = (WBUAutoFillGetEnabledDataClasses_f)(uintptr_t) dlsym(sWBULibrary, "WBUAutoFillGetEnabledDataClasses");
328 if (sWBUAutoFillGetEnabledDataClasses_f) {
335 bool SWCAIsAutofillEnabled(void)
337 #if TARGET_IPHONE_SIMULATOR
338 // Assume the setting's on in the simulator: <rdar://problem/17057358> WBUAutoFillGetEnabledDataClasses call failing in the Simulator
340 #elif TARGET_OS_IPHONE && !TARGET_OS_NANO
341 OSStatus status = _SecWBUEnsuredInitialized();
342 if (status) { return false; }
343 WBSAutoFillDataClasses autofill = sWBUAutoFillGetEnabledDataClasses_f();
344 return ((autofill & WBSAutoFillDataClassUsernamesAndPasswords) != 0);
346 //%%% unsupported platform
351 CFOptionFlags swca_handle_request(enum SWCAXPCOperation operation, CFStringRef client, CFArrayRef domains)
353 CFUserNotificationRef notification = NULL;
354 NSMutableDictionary *notification_dictionary = NULL;
355 NSString *security_path = @"/System/Library/Frameworks/Security.framework";
356 NSString *swc_table = @"SharedWebCredentials";
357 NSBundle *security_bundle;
358 NSString *request_key;
359 NSString *request_format;
360 NSString *default_button_key;
361 NSString *alternate_button_key;
362 NSString *other_button_key;
363 NSString *info_message_key;
366 CFOptionFlags response = 0 | kCFUserNotificationCancelResponse;
367 BOOL alert_sem_held = NO;
370 /* If we have previously allowed this operation/domain for this client,
371 * check and don't prompt again.
375 /* Only display one alert at a time. */
376 static dispatch_semaphore_t alert_sem;
377 static dispatch_once_t alert_once;
378 dispatch_once(&alert_once, ^{
379 if (!(alert_sem = dispatch_semaphore_create(1)))
382 if (!alert_sem_held) {
383 alert_sem_held = YES;
384 if (dispatch_semaphore_wait(alert_sem, DISPATCH_TIME_NOW)) {
385 /* Wait for the active alert, then recheck the database in case both alerts are for the same client. */
386 //asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "Delaying prompt for pid %d", pid);
387 dispatch_semaphore_wait(alert_sem, DISPATCH_TIME_FOREVER);
392 #if TARGET_IPHONE_SIMULATOR
393 security_path = [NSString stringWithFormat:@"%s%@", getenv("IPHONE_SIMULATOR_ROOT"), security_path];
395 security_bundle = [NSBundle bundleWithPath:security_path];
396 notification_dictionary = [NSMutableDictionary dictionary];
398 if (1 == [(__bridge NSArray *)domains count]) {
399 domain = (NSString *)[(__bridge NSArray *)domains objectAtIndex:0];
402 case swca_add_request_id:
405 case swca_update_request_id:
408 case swca_delete_request_id:
411 case swca_copy_request_id:
412 op = (domain) ? "COPY" : "COPYALL";
421 request_key = [NSString stringWithFormat:@"SWC_REQUEST_%s", op];
422 request_format = NSLocalizedStringFromTableInBundle(request_key, swc_table, security_bundle, nil);
423 alternate_button_key = (op) ? [NSString stringWithFormat:@"SWC_ALLOW_%s", op] : nil;
424 default_button_key = @"SWC_NEVER";
425 other_button_key = @"SWC_DENY";
426 info_message_key = @"SWC_INFO_MESSAGE";
428 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertHeaderKey] = [NSString stringWithFormat:request_format, client, domain];
429 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertMessageKey] = NSLocalizedStringFromTableInBundle(info_message_key, swc_table, security_bundle, nil);
430 notification_dictionary[(__bridge NSString *)kCFUserNotificationDefaultButtonTitleKey] = NSLocalizedStringFromTableInBundle(default_button_key, swc_table, security_bundle, nil);
431 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlternateButtonTitleKey] = NSLocalizedStringFromTableInBundle(alternate_button_key, swc_table, security_bundle, nil);
433 if (other_button_key) {
434 // notification_dictionary[(__bridge NSString *)kCFUserNotificationOtherButtonTitleKey] = NSLocalizedStringFromTableInBundle(other_button_key, swc_table, security_bundle, nil);
436 notification_dictionary[(__bridge NSString *)kCFUserNotificationLocalizationURLKey] = [security_bundle bundleURL];
439 if (!(notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationStopAlertLevel | kCFUserNotificationNoDefaultButtonFlag, &error, (__bridge CFDictionaryRef)notification_dictionary)) ||
442 if (CFUserNotificationReceiveResponse(notification, 0, &response))
447 dispatch_semaphore_signal(alert_sem);
449 CFRelease(notification);
453 bool swca_process_response(CFOptionFlags response, CFTypeRef *result)
455 int32_t value = (int32_t)(response & 0x3);
456 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
458 return (NULL != number);
461 bool swca_confirm_add(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error)
463 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer);
464 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks);
465 CFOptionFlags response = swca_handle_request(swca_add_request_id, clientTaskName, domains);
466 return swca_process_response(response, result);
469 bool swca_confirm_copy(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error)
471 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer);
472 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks);
473 CFOptionFlags response = swca_handle_request(swca_copy_request_id, clientTaskName, domains);
474 return swca_process_response(response, result);
477 bool swca_confirm_update(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error)
479 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer);
480 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks);
481 CFOptionFlags response = swca_handle_request(swca_update_request_id, clientTaskName, domains);
482 return swca_process_response(response, result);
485 bool swca_confirm_delete(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error)
487 CFStringRef domain = (CFStringRef) CFDictionaryGetValue(attributes, kSecAttrServer);
488 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&domain, 1, &kCFTypeArrayCallBacks);
489 CFOptionFlags response = swca_handle_request(swca_delete_request_id, clientTaskName, domains);
490 return swca_process_response(response, result);
493 bool swca_select_item(CFArrayRef items, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error)
495 CFUserNotificationRef notification = NULL;
496 NSMutableDictionary *notification_dictionary = NULL;
497 NSString *security_path = @"/System/Library/Frameworks/Security.framework";
498 NSString *swc_table = @"SharedWebCredentials";
499 NSBundle *security_bundle;
500 NSString *request_title_format;
501 NSString *info_message_key;
502 NSString *default_button_key;
503 NSString *alternate_button_key;
504 CFOptionFlags response = 0 | kCFUserNotificationCancelResponse;
505 CFIndex item_count = (items) ? CFArrayGetCount(items) : (CFIndex) 0;
506 BOOL alert_sem_held = NO;
508 if (item_count < 1) {
514 /* Only display one alert at a time. */
515 static dispatch_semaphore_t select_alert_sem;
516 static dispatch_once_t select_alert_once;
517 dispatch_once(&select_alert_once, ^{
518 if (!(select_alert_sem = dispatch_semaphore_create(1)))
521 if (!alert_sem_held) {
522 alert_sem_held = YES;
523 if (dispatch_semaphore_wait(select_alert_sem, DISPATCH_TIME_NOW)) {
524 /* Wait for the active alert */
525 dispatch_semaphore_wait(select_alert_sem, DISPATCH_TIME_FOREVER);
531 CFReleaseSafe(gActiveArray);
532 gActiveArray = items;
533 CFReleaseSafe(gActiveItem);
534 gActiveItem = NULL; // selection will be set by remote view controller
535 //gActiveItem = CFArrayGetValueAtIndex(items, 0);
536 //CFRetainSafe(gActiveItem);
538 #if TARGET_IPHONE_SIMULATOR
539 security_path = [NSString stringWithFormat:@"%s%@", getenv("IPHONE_SIMULATOR_ROOT"), security_path];
541 security_bundle = [NSBundle bundleWithPath:security_path];
542 notification_dictionary = [NSMutableDictionary dictionary];
544 request_title_format = NSLocalizedStringFromTableInBundle(@"SWC_ALERT_TITLE", swc_table, security_bundle, nil);
545 default_button_key = @"SWC_ALLOW_USE";
546 alternate_button_key = @"SWC_CANCEL";
547 info_message_key = @"SWC_INFO_MESSAGE";
549 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertHeaderKey] = [NSString stringWithFormat: request_title_format, clientTaskName];
550 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertMessageKey] = NSLocalizedStringFromTableInBundle(info_message_key, swc_table, security_bundle, nil);
551 notification_dictionary[(__bridge NSString *)kCFUserNotificationDefaultButtonTitleKey] = NSLocalizedStringFromTableInBundle(default_button_key, swc_table, security_bundle, nil);
552 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlternateButtonTitleKey] = NSLocalizedStringFromTableInBundle(alternate_button_key, swc_table, security_bundle, nil);
554 notification_dictionary[(__bridge NSString *)kCFUserNotificationLocalizationURLKey] = [security_bundle bundleURL];
555 notification_dictionary[(__bridge NSString *)kCFUserNotificationAlertTopMostKey] = [NSNumber numberWithBool:YES];
557 // additional keys for remote view controller
558 notification_dictionary[(__bridge NSString *)SBUserNotificationDismissOnLock] = [NSNumber numberWithBool:YES];
559 notification_dictionary[(__bridge NSString *)SBUserNotificationDontDismissOnUnlock] = [NSNumber numberWithBool:YES];
560 notification_dictionary[(__bridge NSString *)SBUserNotificationRemoteServiceBundleIdentifierKey] = @"com.apple.SharedWebCredentialViewService";
561 notification_dictionary[(__bridge NSString *)SBUserNotificationRemoteViewControllerClassNameKey] = @"SWCViewController";
564 if (!(notification = CFUserNotificationCreate(NULL, 0, 0, &err, (__bridge CFDictionaryRef)notification_dictionary)) ||
567 if (CFUserNotificationReceiveResponse(notification, 0, &response))
570 //NSLog(@"Selection: %@, Response: %lu", gActiveItem, (unsigned long)response);
571 if (result && response == kCFUserNotificationDefaultResponse) {
572 CFRetainSafe(gActiveItem);
573 *result = gActiveItem;
577 if (alert_sem_held) {
578 dispatch_semaphore_signal(select_alert_sem);
580 CFReleaseSafe(notification);
581 CFReleaseNull(gActiveArray);
582 CFReleaseNull(gActiveItem);
584 return (result && *result);
588 static void swca_xpc_dictionary_handler(const xpc_connection_t connection, xpc_object_t event) {
589 xpc_type_t type = xpc_get_type(event);
590 __block CFErrorRef error = NULL;
591 xpc_object_t xpcError = NULL;
592 xpc_object_t replyMessage = NULL;
593 SecTaskRef clientTask = NULL;
594 CFStringRef clientTaskName = NULL;
595 CFArrayRef accessGroups = NULL;
597 secdebug("swcagent_xpc", "entering");
598 if (type == XPC_TYPE_DICTIONARY) {
599 replyMessage = xpc_dictionary_create_reply(event);
601 uint64_t operation = xpc_dictionary_get_uint64(event, kSecXPCKeyOperation);
602 secdebug("swcagent_xpc", "operation: %@ (%" PRIu64 ")", SWCAGetOperationDescription((enum SWCAXPCOperation)operation), operation);
605 audit_token_t auditToken = {};
606 #if !CHECK_ENTITLEMENTS
607 hasEntitlement = true;
609 // check our caller's private entitlement to invoke swcagent
610 // TODO: on iOS it's enough to have the entitlement; will need to check code requirement on OS X
611 xpc_connection_get_audit_token(connection, &auditToken);
612 clientTask = SecTaskCreateWithAuditToken(kCFAllocatorDefault, auditToken);
613 hasEntitlement = (clientTask && SecTaskGetBooleanValueForEntitlement(clientTask, kSecEntitlementPrivateAssociatedDomains));
614 if (!hasEntitlement) {
615 CFErrorRef entitlementError = NULL;
616 SecError(errSecMissingEntitlement, &entitlementError, CFSTR("%@: %@ lacks entitlement %@"), SOSCCGetOperationDescription((enum SWCAXPCOperation)operation), clientTask, kSecEntitlementPrivateAssociatedDomains);
617 CFReleaseSafe(entitlementError);
619 CFReleaseNull(clientTask);
622 if (hasEntitlement) {
623 // fetch audit token for process which called securityd
625 const uint8_t *bytes = xpc_dictionary_get_data(event, kSecXPCKeyClientToken, &length);
626 if (length == sizeof(audit_token_t)) {
627 memcpy(&auditToken, bytes, sizeof(audit_token_t));
629 secerror("swcagent_xpc - wrong length for client id");
630 hasEntitlement = false;
633 if (hasEntitlement) {
634 // identify original client
635 clientTask = SecTaskCreateWithAuditToken(kCFAllocatorDefault, auditToken);
636 accessGroups = SecTaskCopyAccessGroups(clientTask);
637 clientTaskName = SecTaskCopyLocalizedDescription(clientTask);
638 #if CHECK_ENTITLEMENTS
639 // check for presence of original client's shared credential entitlement
640 hasEntitlement = (clientTask && SecTaskGetBooleanValueForEntitlement(clientTask, kSecEntitlementAssociatedDomains));
641 if (!hasEntitlement) {
642 CFErrorRef entitlementError = NULL;
643 SecError(errSecMissingEntitlement, &entitlementError, CFSTR("%@: %@ lacks entitlement %@"), SOSCCGetOperationDescription((enum SWCAXPCOperation)operation), clientTask, kSecEntitlementAssociatedDomains);
644 CFReleaseSafe(entitlementError);
647 CFReleaseNull(clientTask);
650 if (hasEntitlement) {
653 case swca_add_request_id:
655 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error);
656 //secdebug("ipc", "swcagent: got swca_add_request_id, query: %@", query);
658 CFTypeRef result = NULL;
659 // confirm that we can add this item
660 if (swca_confirm_add(query, clientTaskName, accessGroups, &result, &error) && result) {
661 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
668 case swca_copy_request_id:
670 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error);
671 //secdebug("ipc", "swcagent: got swca_copy_request_id, query: %@", query);
673 CFTypeRef result = NULL;
674 // confirm that we can copy this item
675 if (swca_confirm_copy(query, clientTaskName, accessGroups, &result, &error) && result) {
676 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
683 case swca_update_request_id:
685 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error);
686 //secdebug("ipc", "swcagent: got swca_update_request_id, query: %@", query);
688 CFTypeRef result = NULL;
689 // confirm that we can copy this item
690 if (swca_confirm_update(query, clientTaskName, accessGroups, &result, &error) && result) {
691 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
698 case swca_delete_request_id:
700 CFDictionaryRef query = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error);
701 //secdebug("ipc", "swcagent: got swca_delete_request_id, query: %@", query);
703 CFTypeRef result = NULL;
704 // confirm that we can copy this item
705 if (swca_confirm_delete(query, clientTaskName, accessGroups, &result, &error) && result) {
706 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
713 case swca_select_request_id:
715 CFArrayRef items = SecXPCDictionaryCopyArray(event, kSecXPCKeyQuery, &error);
716 secdebug("ipc", "swcagent: got swca_select_request_id, items: %@", items);
718 CFTypeRef result = NULL;
719 // select a dictionary from an input array of dictionaries
720 if (swca_select_item(items, clientTaskName, accessGroups, &result, &error) && result) {
721 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
728 case swca_copy_pairs_request_id:
730 secdebug("ipc", "swcagent: got swca_copy_pairs_request_id");
731 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, gActiveArray, &error);
734 case swca_set_selection_request_id:
736 CFDictionaryRef dict = SecXPCDictionaryCopyDictionary(event, kSecXPCKeyQuery, &error);
737 secdebug("ipc", "swcagent: got swca_set_selection_request_id, dict: %@", dict);
739 int32_t value = (int32_t) 1;
740 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
741 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, number, &error);
742 CFReleaseSafe(number);
744 CFReleaseSafe(gActiveItem);
748 case swca_enabled_request_id:
750 // return Safari's password autofill enabled status
751 CFTypeRef result = (SWCAIsAutofillEnabled()) ? kCFBooleanTrue : kCFBooleanFalse;
752 SecXPCDictionarySetPList(replyMessage, kSecXPCKeyResult, result, &error);
755 secdebug("ipc", "swcagent: got unsupported request id (%ld)", (long)operation);
761 if(SecErrorGetOSStatus(error) == errSecItemNotFound)
762 secdebug("ipc", "%@ %@ %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), error);
764 secerror("%@ %@ %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), error);
766 xpcError = SecCreateXPCObjectWithCFError(error);
767 xpc_dictionary_set_value(replyMessage, kSecXPCKeyError, xpcError);
768 } else if (replyMessage) {
769 secdebug("ipc", "%@ %@ responding %@", clientTask, SWCAGetOperationDescription((enum SWCAXPCOperation)operation), replyMessage);
772 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, &error, 0, CFSTR("Messages expect to be xpc dictionary, got: %@"), event);
773 secerror("%@: returning error: %@", clientTask, error);
774 xpcError = SecCreateXPCObjectWithCFError(error);
775 replyMessage = xpc_create_reply_with_format(event, "{%string: %value}", kSecXPCKeyError, xpcError);
779 xpc_connection_send_message(connection, replyMessage);
780 xpc_release(replyMessage);
783 xpc_release(xpcError);
785 CFReleaseSafe(error);
786 CFReleaseSafe(accessGroups);
787 CFReleaseSafe(clientTaskName);
790 static void swca_xpc_init()
792 secdebug("swcagent_xpc", "start");
794 xpc_connection_t listener = xpc_connection_create_mach_service(kSWCAXPCServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
796 seccritical("swcagent failed to register xpc listener, exiting");
800 xpc_connection_set_event_handler(listener, ^(xpc_object_t connection) {
801 if (xpc_get_type(connection) == XPC_TYPE_CONNECTION) {
802 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
803 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
804 xpc_retain(connection);
806 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
807 swca_xpc_dictionary_handler(connection, event);
809 xpc_release(connection);
813 xpc_connection_resume(connection);
816 xpc_connection_resume(listener);
819 int main(int argc, char *argv[])
822 char *wait4debugger = getenv("WAIT4DEBUGGER");
823 if (wait4debugger && !strcasecmp("YES", wait4debugger)) {
824 seccritical("SIGSTOPing self, awaiting debugger");
825 kill(getpid(), SIGSTOP);
826 asl_log(NULL, NULL, ASL_LEVEL_CRIT,
827 "Again, for good luck (or bad debuggers)");
828 kill(getpid(), SIGSTOP);
836 /* vi:set ts=4 sw=4 et: */