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