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