]> git.saurik.com Git - apple/security.git/blob - Security/sec/SharedWebCredential/swcagent.m
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SharedWebCredential / swcagent.m
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
91 CFStringRef SWCAGetOperationDescription(enum SWCAXPCOperation op);
92 bool SWCAIsAutofillEnabled(void);
93
94 bool swca_confirm_add(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
95 bool swca_confirm_copy(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
96 bool swca_confirm_update(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
97 bool swca_confirm_delete(CFDictionaryRef attributes, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
98 bool swca_select_item(CFArrayRef items, CFStringRef clientTaskName, CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error);
99
100 CFOptionFlags swca_handle_request(enum SWCAXPCOperation operation, CFStringRef client, CFArrayRef domains);
101 bool swca_process_response(CFOptionFlags response, CFTypeRef *result);
102
103 static CFArrayRef gActiveArray = NULL;
104 static 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
113 static 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
124 static 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 */
152 enum {
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
166 static 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
243 struct __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
255 static 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
272 static 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
282 CFStringRef 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
306 bool 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
317 CFOptionFlags 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
335 check_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
411 out:
412 if (alert_sem_held)
413 dispatch_semaphore_signal(alert_sem);
414 if (notification)
415 CFRelease(notification);
416 return response;
417 }
418
419 bool 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
427 bool 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
435 bool 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
443 bool 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
451 bool 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
459 bool 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
478 entry:
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
542 out:
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
554 static 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
756 static 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
785 int 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: */