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