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