]> git.saurik.com Git - apple/security.git/blob - keychain/securityd/SFKeychainServer.m
Security-59754.60.13.tar.gz
[apple/security.git] / keychain / securityd / SFKeychainServer.m
1 /*
2 * Copyright (c) 2017 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 #import "SFKeychainServer.h"
25 #import <TargetConditionals.h>
26
27 #if !TARGET_OS_BRIDGE
28 #if __OBJC2__
29
30 #import "SecCDKeychain.h"
31 #import "SecFileLocations.h"
32 #import "debugging.h"
33 #import "CloudKitCategories.h"
34 #import "SecAKSWrappers.h"
35 #include "securityd_client.h"
36 #import "server_entitlement_helpers.h"
37 #import "SecTask.h"
38 #import "keychain/categories/NSError+UsefulConstructors.h"
39 #import "SecEntitlements.h"
40 #import <Security/SecXPCHelper.h>
41 #import <SecurityFoundation/SFKeychain.h>
42 #import <SecurityFoundation/SFCredential_Private.h>
43 #import <SecurityFoundation/SFCredentialStore_Private.h>
44 #import <Foundation/NSKeyedArchiver_Private.h>
45 #import <Foundation/NSXPCConnection_Private.h>
46
47 static NSString* const SFKeychainItemAttributeLocalizedLabel = @"label";
48 static NSString* const SFKeychainItemAttributeLocalizedDescription = @"description";
49
50 static NSString* const SFCredentialAttributeUsername = @"username";
51 static NSString* const SFCredentialAttributePrimaryServiceIdentifier = @"primaryServiceID";
52 static NSString* const SFCredentialAttributeSupplementaryServiceIdentifiers = @"supplementaryServiceIDs";
53 static NSString* const SFCredentialAttributeCreationDate = @"creationDate";
54 static NSString* const SFCredentialAttributeModificationDate = @"modificationDate";
55 static NSString* const SFCredentialAttributeCustom = @"customAttributes";
56 static NSString* const SFCredentialSecretPassword = @"password";
57
58 @interface SFCredential (securityd_only)
59
60 - (instancetype)_initWithUsername:(NSString*)username primaryServiceIdentifier:(SFServiceIdentifier*)primaryServiceIdentifier supplementaryServiceIdentifiers:(nullable NSArray<SFServiceIdentifier*>*)supplementaryServiceIdentifiers;
61
62 @end
63
64 @interface SFKeychainServerConnection ()
65
66 - (instancetype)initWithKeychain:(SecCDKeychain*)keychain xpcConnection:(NSXPCConnection*)connection;
67
68 @end
69
70 @implementation SecCDKeychainItemTypeCredential
71
72 + (instancetype)itemType
73 {
74 static SecCDKeychainItemTypeCredential* itemType = nil;
75 static dispatch_once_t onceToken;
76 dispatch_once(&onceToken, ^{
77 itemType = [[self alloc] _initWithName:@"Credential" version:1 primaryKeys:@[SFCredentialAttributeUsername, SFCredentialAttributePrimaryServiceIdentifier] syncableKeys:nil];
78 });
79
80 return itemType;
81 }
82
83 @end
84
85 @implementation SFKeychainServer {
86 SecCDKeychain* _keychain;
87 }
88
89 - (instancetype)initWithStorageURL:(NSURL*)persistentStoreURL modelURL:(NSURL*)managedObjectURL encryptDatabase:(bool)encryptDatabase
90 {
91 if (self = [super init]) {
92 _keychain = [[SecCDKeychain alloc] initWithStorageURL:persistentStoreURL modelURL:managedObjectURL encryptDatabase:encryptDatabase];
93 }
94
95 return self;
96 }
97
98 - (BOOL)listener:(NSXPCListener*)listener shouldAcceptNewConnection:(NSXPCConnection*)newConnection
99 {
100 NSNumber* keychainDenyEntitlement = [newConnection valueForEntitlement:(__bridge NSString*)kSecEntitlementKeychainDeny];
101 if ([keychainDenyEntitlement isKindOfClass:[NSNumber class]] && keychainDenyEntitlement.boolValue == YES) {
102 secerror("SFKeychainServer: connection denied due to entitlement %@", kSecEntitlementKeychainDeny);
103 return NO;
104 }
105
106 // wait a bit for shared function from SecurityFoundation to get to SDK, then addopt that
107 NSXPCInterface* interface = [NSXPCInterface interfaceWithProtocol:@protocol(SFKeychainServerProtocol)];
108
109 NSSet<Class> *errorClasses = [SecXPCHelper safeErrorClasses];
110
111 [interface setClasses:errorClasses forSelector:@selector(rpcAddCredential:withAccessPolicy:reply:) argumentIndex:1 ofReply:YES];
112 [interface setClasses:errorClasses forSelector:@selector(rpcFetchPasswordCredentialForPersistentIdentifier:reply:) argumentIndex:2 ofReply:YES];
113 [interface setClasses:errorClasses forSelector:@selector(rpcLookupCredentialsForServiceIdentifiers:reply:) argumentIndex:1 ofReply:YES];
114 [interface setClasses:errorClasses forSelector:@selector(rpcRemoveCredentialWithPersistentIdentifier:reply:) argumentIndex:1 ofReply:YES];
115 [interface setClasses:errorClasses forSelector:@selector(rpcReplaceOldCredential:withNewCredential:reply:) argumentIndex:1 ofReply:YES];
116 [interface setClasses:errorClasses forSelector:@selector(rpcReplaceCredential:withNewCredential:reply:) argumentIndex:1 ofReply:YES];
117
118 [interface setClasses:[NSSet setWithObjects:[NSArray class], [SFServiceIdentifier class], nil] forSelector:@selector(rpcLookupCredentialsForServiceIdentifiers:reply:) argumentIndex:0 ofReply:NO];
119 [interface setClasses:[NSSet setWithObjects:[NSArray class], [SFPasswordCredential class], nil] forSelector:@selector(rpcLookupCredentialsForServiceIdentifiers:reply:) argumentIndex:0 ofReply:YES];
120 newConnection.exportedInterface = interface;
121 newConnection.exportedObject = [[SFKeychainServerConnection alloc] initWithKeychain:_keychain xpcConnection:newConnection];
122 [newConnection resume];
123 return YES;
124 }
125
126 - (SecCDKeychain*)_keychain
127 {
128 return _keychain;
129 }
130
131 @end
132
133 @implementation SFKeychainServerConnection {
134 SecCDKeychain* _keychain;
135 NSArray* _clientAccessGroups;
136 }
137
138 @synthesize clientAccessGroups = _clientAccessGroups;
139
140 - (instancetype)initWithKeychain:(SecCDKeychain*)keychain xpcConnection:(NSXPCConnection*)connection
141 {
142 if (self = [super init]) {
143 _keychain = keychain;
144
145 SecTaskRef task = SecTaskCreateWithAuditToken(NULL, connection.auditToken);
146 if (task) {
147 _clientAccessGroups = (__bridge_transfer NSArray*)SecTaskCopyAccessGroups(task);
148 }
149 CFReleaseNull(task);
150 }
151
152 return self;
153 }
154
155 - (keyclass_t)keyclassForAccessPolicy:(SFAccessPolicy*)accessPolicy
156 {
157 if (accessPolicy.accessibility.mode == SFAccessibleAfterFirstUnlock) {
158 if (accessPolicy.sharingPolicy == SFSharingPolicyThisDeviceOnly) {
159 return key_class_cku;
160 }
161 else {
162 return key_class_ck;
163 }
164 }
165 else {
166 if (accessPolicy.sharingPolicy == SFSharingPolicyThisDeviceOnly) {
167 return key_class_aku;
168 }
169 else {
170 return key_class_ak;
171 }
172 }
173 }
174
175 - (void)rpcAddCredential:(SFCredential*)credential withAccessPolicy:(SFAccessPolicy*)accessPolicy reply:(void (^)(NSString* persistentIdentifier, NSError* error))reply
176 {
177 if (![credential isKindOfClass:[SFPasswordCredential class]]) {
178 reply(nil, [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorInvalidParameter userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"attempt to add credential to SFCredentialStore that is not a password credential: %@", credential]}]);
179 return;
180 }
181
182 NSString* accessGroup = accessPolicy.accessGroup;
183 if (!accessGroup) {
184 NSError* error = nil;
185 accessGroup = self.clientAccessGroups.firstObject;
186 if (!accessGroup) {
187 error = [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorMissingAccessGroup userInfo:@{NSLocalizedDescriptionKey : @"no keychain access group found; ensure that your process has the keychain-access-groups entitlement"}];
188 reply(nil, error);
189 return;
190 }
191 }
192
193 SFPasswordCredential* passwordCredential = (SFPasswordCredential*)credential;
194
195 NSError* error = nil;
196 NSData* primaryServiceIdentifierData = [NSKeyedArchiver archivedDataWithRootObject:passwordCredential.primaryServiceIdentifier requiringSecureCoding:YES error:&error];
197 if (!primaryServiceIdentifierData) {
198 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
199 reply(nil, [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorSaveFailed userInfo:@{ NSLocalizedDescriptionKey : @"failed to serialize primary service identifier", NSUnderlyingErrorKey : error }]);
200 });
201 return;
202 }
203
204 NSMutableArray* serializedSupplementaryServiceIdentifiers = [[NSMutableArray alloc] initWithCapacity:passwordCredential.supplementaryServiceIdentifiers.count];
205 for (SFServiceIdentifier* serviceIdentifier in passwordCredential.supplementaryServiceIdentifiers) {
206 NSData* serviceIdentifierData = [NSKeyedArchiver archivedDataWithRootObject:serviceIdentifier requiringSecureCoding:YES error:&error];
207 if (serviceIdentifierData) {
208 [serializedSupplementaryServiceIdentifiers addObject:serviceIdentifierData];
209 }
210 else {
211 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
212 reply(nil, [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorSaveFailed userInfo:@{ NSLocalizedDescriptionKey : @"failed to serialize supplementary service identifier", NSUnderlyingErrorKey : error }]);
213 });
214 return;
215 }
216 }
217
218 NSDictionary* attributes = @{ SFCredentialAttributeUsername : passwordCredential.username,
219 SFCredentialAttributePrimaryServiceIdentifier : primaryServiceIdentifierData,
220 SFCredentialAttributeSupplementaryServiceIdentifiers : serializedSupplementaryServiceIdentifiers,
221 SFCredentialAttributeCreationDate : [NSDate date],
222 SFCredentialAttributeModificationDate : [NSDate date],
223 SFKeychainItemAttributeLocalizedLabel : passwordCredential.localizedLabel,
224 SFKeychainItemAttributeLocalizedDescription : passwordCredential.localizedDescription,
225 SFCredentialAttributeCustom : passwordCredential.customAttributes ?: [NSDictionary dictionary] };
226
227 NSDictionary* secrets = @{ SFCredentialSecretPassword : passwordCredential.password };
228 NSUUID* persistentID = [NSUUID UUID];
229
230 // lookup attributes:
231 // 1. primaryServiceIdentifier (always)
232 // 2. username (always)
233 // 3. label (if present)
234 // 4. description (if present)
235 // 5. each of the service identifiers by type, e.g. "domain"
236 // 6. any custom attributes that fit the requirements (key is string, and value is plist type)
237
238 SecCDKeychainLookupTuple* primaryServiceIdentifierLookup = [SecCDKeychainLookupTuple lookupTupleWithKey:SFCredentialAttributePrimaryServiceIdentifier value:primaryServiceIdentifierData];
239 SecCDKeychainLookupTuple* usernameLookup = [SecCDKeychainLookupTuple lookupTupleWithKey:SFCredentialAttributeUsername value:passwordCredential.username];
240 SecCDKeychainLookupTuple* labelLookup = [SecCDKeychainLookupTuple lookupTupleWithKey:SFKeychainItemAttributeLocalizedLabel value:passwordCredential.localizedLabel];
241 SecCDKeychainLookupTuple* descriptionLookup = [SecCDKeychainLookupTuple lookupTupleWithKey:SFKeychainItemAttributeLocalizedDescription value:passwordCredential.localizedDescription];
242 NSMutableArray* lookupAttributes = [[NSMutableArray alloc] initWithObjects:primaryServiceIdentifierLookup, usernameLookup, nil];
243 if (labelLookup) {
244 [lookupAttributes addObject:labelLookup];
245 }
246 if (descriptionLookup) {
247 [lookupAttributes addObject:descriptionLookup];
248 }
249
250 SFServiceIdentifier* primaryServiceIdentifier = credential.primaryServiceIdentifier;
251 [lookupAttributes addObject:[SecCDKeychainLookupTuple lookupTupleWithKey:primaryServiceIdentifier.lookupKey value:primaryServiceIdentifier.serviceID]];
252 for (SFServiceIdentifier* serviceIdentifier in credential.supplementaryServiceIdentifiers) {
253 [lookupAttributes addObject:[SecCDKeychainLookupTuple lookupTupleWithKey:serviceIdentifier.lookupKey value:serviceIdentifier.serviceID]];
254 }
255
256 [passwordCredential.customAttributes enumerateKeysAndObjectsUsingBlock:^(NSString* customKey, id value, BOOL* stop) {
257 if ([customKey isKindOfClass:[NSString class]]) {
258 SecCDKeychainLookupTuple* lookupTuple = [SecCDKeychainLookupTuple lookupTupleWithKey:customKey value:value];
259 if (lookupTuple) {
260 [lookupAttributes addObject:lookupTuple];
261 }
262 else {
263 // TODO: an error here?
264 }
265 }
266 }];
267
268 SecCDKeychainAccessControlEntity* owner = [SecCDKeychainAccessControlEntity accessControlEntityWithType:SecCDKeychainAccessControlEntityTypeAccessGroup stringRepresentation:accessGroup];
269 keyclass_t keyclass = [self keyclassForAccessPolicy:accessPolicy];
270 SecCDKeychainItem* item = [[SecCDKeychainItem alloc] initItemType:[SecCDKeychainItemTypeCredential itemType] withPersistentID:persistentID attributes:attributes lookupAttributes:lookupAttributes secrets:secrets owner:owner keyclass:keyclass];
271 [_keychain insertItems:@[item] withConnection:self completionHandler:^(bool success, NSError* insertError) {
272 if (success && !insertError) {
273 reply(persistentID.UUIDString, nil);
274 }
275 else {
276 reply(nil, insertError);
277 }
278 }];
279 }
280
281 - (void)rpcFetchPasswordCredentialForPersistentIdentifier:(NSString*)persistentIdentifier reply:(void (^)(SFPasswordCredential* credential, NSString* password, NSError* error))reply
282 {
283 // TODO: negative testing
284 NSUUID* persistentID = [[NSUUID alloc] initWithUUIDString:persistentIdentifier];
285 if (!persistentID) {
286 secerror("SFKeychainServer: attempt to fetch credential with invalid persistent identifier; %@", persistentIdentifier);
287 reply(nil, nil, [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorInvalidPersistentIdentifier userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"invalid persistent identifier: %@", persistentIdentifier]}]);
288 return;
289 }
290
291 [_keychain fetchItemForPersistentID:persistentID withConnection:self completionHandler:^(SecCDKeychainItem* item, NSError* error) {
292 NSError* localError = error;
293 SFPasswordCredential* credential = nil;
294 if (item && !error) {
295 credential = [self passwordCredentialForItem:item error:&localError];
296 }
297
298 if (credential) {
299 reply(credential, credential.password, nil);
300 }
301 else {
302 reply(nil, nil, localError);
303 }
304 }];
305 }
306
307 - (void)rpcLookupCredentialsForServiceIdentifiers:(nullable NSArray<SFServiceIdentifier*>*)serviceIdentifiers reply:(void (^)(NSArray<SFCredential*>* _Nullable results, NSError* _Nullable error))reply
308 {
309 __block NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
310 __block NSError* resultError = nil;
311
312 void (^processFetchedItems)(NSArray*) = ^(NSArray* fetchedItems) {
313 for (SecCDKeychainItemMetadata* item in fetchedItems) {
314 if ([item.itemType isKindOfClass:[SecCDKeychainItemTypeCredential class]]) {
315 SFPasswordCredential* credential = [self passwordCredentialForItemMetadata:item error:&resultError];
316 if (credential) {
317 resultsDict[item.persistentID] = credential;
318 }
319 else {
320 resultsDict = nil; // got an error
321 }
322 }
323 }
324 };
325
326 if (!serviceIdentifiers) {
327 // TODO: lookup everything
328 }
329 else {
330 for (SFServiceIdentifier* serviceIdentifier in serviceIdentifiers) {
331 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
332 // TODO: this is lamé; make fetchItemsWithValue take an array and get rid of the semaphore crap
333 [_keychain fetchItemsWithValue:serviceIdentifier.serviceID forLookupKey:serviceIdentifier.lookupKey ofType:SecCDKeychainLookupValueTypeString withConnection:self completionHandler:^(NSArray<SecCDKeychainItemMetadata*>* items, NSError* error) {
334 if (items && !error) {
335 processFetchedItems(items);
336 }
337 else {
338 resultsDict = nil;
339 resultError = error;
340 }
341
342 dispatch_semaphore_signal(semaphore);
343 }];
344 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
345 }
346 }
347
348 reply(resultsDict.allValues, resultError);
349 }
350
351 - (void)rpcRemoveCredentialWithPersistentIdentifier:(NSString*)persistentIdentifier reply:(void (^)(BOOL success, NSError* _Nullable error))reply
352 {
353 NSUUID* persistentID = [[NSUUID alloc] initWithUUIDString:persistentIdentifier];
354 if (!persistentID) {
355 secerror("SFKeychainServer: attempt to remove credential with invalid persistent identifier; %@", persistentIdentifier);
356 reply(false, [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorInvalidPersistentIdentifier userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"invalid persistent identifier: %@", persistentIdentifier]}]);
357 return;
358 }
359
360 [_keychain deleteItemWithPersistentID:persistentID withConnection:self completionHandler:^(bool success, NSError* error) {
361 reply(success, error);
362 }];
363 }
364
365 - (void)rpcReplaceOldCredential:(SFCredential*)oldCredential withNewCredential:(SFCredential*)newCredential reply:(void (^)(NSString* newPersistentIdentifier, NSError* _Nullable error))reply
366 {
367 // TODO: implement
368 reply(nil, nil);
369 }
370
371 - (SFPasswordCredential*)passwordCredentialForItem:(SecCDKeychainItem*)item error:(NSError**)error
372 {
373 SFPasswordCredential* credential = [self passwordCredentialForItemMetadata:item.metadata error:error];
374 if (credential) {
375 credential.password = item.secrets[SFCredentialSecretPassword];
376 if (!credential.password) {
377 if (error) {
378 *error = [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorSecureDecodeFailed userInfo:@{NSLocalizedDescriptionKey : @"failed to get password for SFCredential"}];
379 }
380 return nil;
381 }
382 }
383
384 return credential;
385 }
386
387 - (SFPasswordCredential*)passwordCredentialForItemMetadata:(SecCDKeychainItemMetadata*)metadata error:(NSError**)error
388 {
389 NSDictionary* attributes = metadata.attributes;
390 NSString* username = attributes[SFCredentialAttributeUsername];
391
392 NSError* localError = nil;
393 SFServiceIdentifier* primaryServiceIdentifier = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFServiceIdentifier class] fromData:attributes[SFCredentialAttributePrimaryServiceIdentifier] error:&localError];
394
395 NSArray* serializedSupplementaryServiceIdentifiers = attributes[SFCredentialAttributeSupplementaryServiceIdentifiers];
396 NSMutableArray* supplementaryServiceIdentifiers = [[NSMutableArray alloc] initWithCapacity:serializedSupplementaryServiceIdentifiers.count];
397 for (NSData* serializedServiceIdentifier in serializedSupplementaryServiceIdentifiers) {
398 if ([serializedServiceIdentifier isKindOfClass:[NSData class]]) {
399 SFServiceIdentifier* serviceIdentifier = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFServiceIdentifier class] fromData:serializedServiceIdentifier error:&localError];
400 if (serviceIdentifier) {
401 [supplementaryServiceIdentifiers addObject:serviceIdentifier];
402 }
403 else {
404 supplementaryServiceIdentifiers = nil;
405 break;
406 }
407 }
408 else {
409 supplementaryServiceIdentifiers = nil;
410 localError = [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorSecureDecodeFailed userInfo:@{NSLocalizedDescriptionKey : @"malformed supplementary service identifiers array in SecCDKeychainItem"}];
411 break;
412 }
413 }
414
415 if (username && primaryServiceIdentifier && supplementaryServiceIdentifiers) {
416 SFPasswordCredential* credential = [[SFPasswordCredential alloc] _initWithUsername:username primaryServiceIdentifier:primaryServiceIdentifier supplementaryServiceIdentifiers:supplementaryServiceIdentifiers];
417 credential.creationDate = attributes[SFCredentialAttributeCreationDate];
418 credential.modificationDate = attributes[SFCredentialAttributeModificationDate];
419 credential.localizedLabel = attributes[SFKeychainItemAttributeLocalizedLabel];
420 credential.localizedDescription = attributes[SFKeychainItemAttributeLocalizedDescription];
421 credential.persistentIdentifier = metadata.persistentID.UUIDString;
422 credential.customAttributes = attributes[SFCredentialAttributeCustom];
423 return credential;
424 }
425 else {
426 if (error) {
427 *error = [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorSecureDecodeFailed userInfo:@{ NSLocalizedDescriptionKey : @"failed to deserialize SFCredential", NSUnderlyingErrorKey : localError }];
428 }
429 return nil;
430 }
431 }
432
433 @end
434
435 #endif // ___OBJC2__
436
437 void SFKeychainServerInitialize(void)
438 {
439 static dispatch_once_t once;
440 static SFKeychainServer* server;
441 static NSXPCListener* listener;
442
443 dispatch_once(&once, ^{
444 @autoreleasepool {
445 NSURL* persistentStoreURL = (__bridge_transfer NSURL*)SecCopyURLForFileInKeychainDirectory((__bridge CFStringRef)@"CDKeychain");
446 NSBundle* resourcesBundle = [NSBundle bundleWithPath:@"/System/Library/Keychain/KeychainResources.bundle"];
447 NSURL* managedObjectModelURL = [resourcesBundle URLForResource:@"KeychainModel" withExtension:@"momd"];
448 server = [[SFKeychainServer alloc] initWithStorageURL:persistentStoreURL modelURL:managedObjectModelURL encryptDatabase:true];
449 listener = [[NSXPCListener alloc] initWithMachServiceName:@(kSFKeychainServerServiceName)];
450 listener.delegate = server;
451 [listener resume];
452 }
453 });
454 }
455
456 #else // !TARGET_OS_BRIDGE
457
458 void SFKeychainServerInitialize(void) {}
459
460 #endif
461