]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ipc/server_xpc.m
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / sec / ipc / server_xpc.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 <Foundation/Foundation.h>
25
26 #include <ipc/securityd_client.h>
27 #include <ipc/server_security_helpers.h>
28 #include <ipc/server_endpoint.h>
29 #include <os/transaction_private.h>
30
31 #if TARGET_DARWINOS
32 #undef OCTAGON
33 #undef SECUREOBJECTSYNC
34 #undef SHAREDWEBCREDENTIALS
35 #endif
36
37 #if OCTAGON
38 #import "keychain/categories/NSError+UsefulConstructors.h"
39 #include <CloudKit/CloudKit_Private.h>
40 // If your callbacks might pass back a CK error, you should use the XPCSanitizeError() spi on all branches at this layer.
41 // Otherwise, XPC might crash on the other side if they haven't linked CloudKit.framework.
42 #define XPCSanitizeError CKXPCSuitableError
43 #else
44 // This is a no-op: XPCSanitizeError(error) turns into (error)
45 #define XPCSanitizeError
46 #endif // OCTAGON
47
48 #include <Security/SecEntitlements.h>
49 #include <Security/SecItemPriv.h>
50 #include "keychain/securityd/SecItemServer.h"
51 #include "keychain/securityd/SecItemSchema.h"
52 #include "keychain/securityd/SecItemDb.h"
53
54 #include "keychain/ckks/CKKSViewManager.h"
55
56 #import "keychain/securityd/SecDbBackupManager.h"
57
58 @interface SecOSTransactionHolder : NSObject
59 @property os_transaction_t transaction;
60 - (instancetype)init:(os_transaction_t)transaction;
61 @end
62
63 @implementation SecOSTransactionHolder
64 - (instancetype)init:(os_transaction_t)transaction {
65 if((self = [super init])) {
66 _transaction = transaction;
67 }
68 return self;
69 }
70 @end
71
72 @implementation SecuritydXPCServer (SecuritydXPCProtocol)
73
74 - (void) SecItemAddAndNotifyOnSync:(NSDictionary*) attributes
75 syncCallback:(id<SecuritydXPCCallbackProtocol>) callback
76 complete:(void (^) (NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror))xpcComplete
77 {
78 // The calling client might not handle CK types well. Sanitize!
79 void (^complete)(NSDictionary*, NSArray*, NSError*) = ^(NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror){
80 xpcComplete(opDictResult, opArrayResult, XPCSanitizeError(operror));
81 };
82
83 CFErrorRef cferror = NULL;
84 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
85 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
86 //TODO: ensure cferror can transit xpc
87 complete(NULL, NULL, (__bridge NSError*) cferror);
88 CFReleaseNull(cferror);
89 return;
90 }
91
92 #if OCTAGON
93 // Wait a bit for CKKS initialization in case of daemon start, but don't bail if it isn't up
94 [[CKKSViewManager manager].completedSecCKKSInitialize wait:10];
95 #endif
96
97 if(attributes[(id)kSecAttrDeriveSyncIDFromItemAttributes] ||
98 attributes[(id)kSecAttrPCSPlaintextServiceIdentifier] ||
99 attributes[(id)kSecAttrPCSPlaintextPublicKey] ||
100 attributes[(id)kSecAttrPCSPlaintextPublicIdentity]) {
101
102 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSPlaintextFields]) {
103 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ does not have entitlement %@, but is using SPI anyway"), _client.task, kSecEntitlementPrivateCKKSPlaintextFields);
104 complete(NULL, NULL, (__bridge NSError*) cferror);
105 CFReleaseNull(cferror);
106 return;
107 }
108 }
109
110 if(attributes[(id)kSecDataInetExtraNotes] ||
111 attributes[(id)kSecDataInetExtraHistory] ||
112 attributes[(id)kSecDataInetExtraClientDefined0] ||
113 attributes[(id)kSecDataInetExtraClientDefined1] ||
114 attributes[(id)kSecDataInetExtraClientDefined2] ||
115 attributes[(id)kSecDataInetExtraClientDefined3]) {
116 if(![self clientHasBooleanEntitlement:(__bridge NSString*)kSecEntitlementPrivateInetExpansionFields]) {
117 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ does not have entitlement %@"), _client.task, kSecEntitlementPrivateInetExpansionFields);
118 complete(NULL, NULL, (__bridge NSError*) cferror);
119 CFReleaseNull(cferror);
120 return;
121 }
122 }
123
124 CFTypeRef cfresult = NULL;
125
126 NSMutableDictionary* callbackQuery = [attributes mutableCopy];
127
128 // We probably need to figure out how to call os_transaction_needs_more_time on this transaction, but as this callback passes through C code, it's quite difficult
129 SecOSTransactionHolder* callbackTransaction = [[SecOSTransactionHolder alloc] init:os_transaction_create("com.apple.securityd.SecItemAddAndNotifyOnSync-callback")];
130 callbackQuery[@"f_ckkscallback"] = ^void (bool didSync, CFErrorRef syncerror) {
131 [callback callCallback:didSync error:XPCSanitizeError((__bridge NSError*)syncerror)];
132 callbackTransaction.transaction = nil;
133 };
134
135 _SecItemAdd((__bridge CFDictionaryRef) callbackQuery, &_client, &cfresult, &cferror);
136
137 // SecItemAdd returns Some CF Object, but NSXPC is pretty adamant that everything be a specific NS type. Split it up here:
138 if(!cfresult) {
139 complete(NULL, NULL, (__bridge NSError *)(cferror));
140 } else if( CFGetTypeID(cfresult) == CFDictionaryGetTypeID()) {
141 complete((__bridge NSDictionary *)(cfresult), NULL, (__bridge NSError *)(cferror));
142 } else if( CFGetTypeID(cfresult) == CFArrayGetTypeID()) {
143 complete(NULL, (__bridge NSArray *)cfresult, (__bridge NSError *)(cferror));
144 } else {
145 // TODO: actually error here
146 complete(NULL, NULL, NULL);
147 }
148 CFReleaseNull(cfresult);
149 CFReleaseNull(cferror);
150 }
151
152 - (void)secItemSetCurrentItemAcrossAllDevices:(NSData* _Nonnull)newItemPersistentRef
153 newCurrentItemHash:(NSData* _Nonnull)newItemSHA1
154 accessGroup:(NSString* _Nonnull)accessGroup
155 identifier:(NSString* _Nonnull)identifier
156 viewHint:(NSString* _Nonnull)viewHint
157 oldCurrentItemReference:(NSData* _Nullable)oldCurrentItemPersistentRef
158 oldCurrentItemHash:(NSData* _Nullable)oldItemSHA1
159 complete:(void (^) (NSError* _Nullable operror))xpcComplete
160 {
161 #if OCTAGON
162 // The calling client might not handle CK types well. Sanitize!
163 void (^complete)(NSError*) = ^(NSError* error){
164 xpcComplete(XPCSanitizeError(error));
165 };
166
167 __block CFErrorRef cferror = NULL;
168 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
169 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
170 complete((__bridge NSError*) cferror);
171 CFReleaseNull(cferror);
172 return;
173 }
174
175 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSWriteCurrentItemPointers]) {
176 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ does not have entitlement %@"), _client.task, kSecEntitlementPrivateCKKSWriteCurrentItemPointers);
177 complete((__bridge NSError*) cferror);
178 CFReleaseNull(cferror);
179 return;
180 }
181
182 if (!accessGroupsAllows(self->_client.accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
183 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: client is missing access-group %@: %@"), accessGroup, _client.task);
184 complete((__bridge NSError*)cferror);
185 CFReleaseNull(cferror);
186 return;
187 }
188
189 #if OCTAGON
190 // Wait a bit for CKKS initialization in case of daemon start, and bail it doesn't come up
191 if([[CKKSViewManager manager].completedSecCKKSInitialize wait:10] != 0) {
192 secerror("SecItemSetCurrentItemAcrossAllDevices: CKKSViewManager not initialized?");
193 complete([NSError errorWithDomain:CKKSErrorDomain code:CKKSNotInitialized description:@"CKKS not yet initialized"]);
194 return;
195 }
196 #endif
197
198 CKKSViewManager* manager = [CKKSViewManager manager];
199 if(!manager) {
200 secerror("SecItemSetCurrentItemAcrossAllDevices: no view manager?");
201 complete([NSError errorWithDomain:CKKSErrorDomain
202 code:CKKSNotInitialized
203 description:@"No view manager, cannot forward request"]);
204 return;
205 }
206
207 [manager setCurrentItemForAccessGroup:newItemPersistentRef
208 hash:newItemSHA1
209 accessGroup:accessGroup
210 identifier:identifier
211 viewHint:viewHint
212 replacing:oldCurrentItemPersistentRef
213 hash:oldItemSHA1
214 complete:complete];
215 return;
216 #else // ! OCTAGON
217 xpcComplete([NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemSetCurrentItemAcrossAllDevices not implemented on this platform"}]);
218 #endif // OCTAGON
219 }
220
221 -(void)secItemFetchCurrentItemAcrossAllDevices:(NSString*)accessGroup
222 identifier:(NSString*)identifier
223 viewHint:(NSString*)viewHint
224 fetchCloudValue:(bool)fetchCloudValue
225 complete:(void (^) (NSData* persistentref, NSError* operror))xpcComplete
226 {
227 #if OCTAGON
228 // The calling client might not handle CK types well. Sanitize!
229 void (^complete)(NSData*, NSError*) = ^(NSData* persistentref, NSError* error){
230 xpcComplete(persistentref, XPCSanitizeError(error));
231 };
232
233 CFErrorRef cferror = NULL;
234 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
235 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
236 complete(NULL, (__bridge NSError*) cferror);
237 CFReleaseNull(cferror);
238 return;
239 }
240
241 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSReadCurrentItemPointers]) {
242 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: %@ does not have entitlement %@"), _client.task, kSecEntitlementPrivateCKKSReadCurrentItemPointers);
243 complete(NULL, (__bridge NSError*) cferror);
244 CFReleaseNull(cferror);
245 return;
246 }
247
248 if (!accessGroupsAllows(self->_client.accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
249 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: client is missing access-group %@: %@"), accessGroup, _client.task);
250 complete(NULL, (__bridge NSError*)cferror);
251 CFReleaseNull(cferror);
252 return;
253 }
254
255 // Wait a bit for CKKS initialization in case of daemon start, and bail it doesn't come up
256 if([[CKKSViewManager manager].completedSecCKKSInitialize wait:10] != 0) {
257 secerror("SecItemFetchCurrentItemAcrossAllDevices: CKKSViewManager not initialized?");
258 complete(NULL, [NSError errorWithDomain:CKKSErrorDomain code:CKKSNotInitialized description:@"CKKS not yet initialized"]);
259 return;
260 }
261
262 [[CKKSViewManager manager] getCurrentItemForAccessGroup:accessGroup
263 identifier:identifier
264 viewHint:viewHint
265 fetchCloudValue:fetchCloudValue
266 complete:^(NSString* uuid, NSError* error) {
267 if(error || !uuid) {
268 secnotice("ckkscurrent", "CKKS didn't find a current item for (%@,%@): %@ %@", accessGroup, identifier, uuid, error);
269 complete(NULL, error);
270 return;
271 }
272
273 // Find the persistent ref and return it.
274 secinfo("ckkscurrent", "CKKS believes current item UUID for (%@,%@) is %@. Looking up persistent ref...", accessGroup, identifier, uuid);
275 [self findItemPersistentRefByUUID:uuid
276 extraLoggingString:[NSString stringWithFormat:@"%@,%@", accessGroup, identifier]
277 complete:complete];
278 }];
279 #else // ! OCTAGON
280 xpcComplete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemFetchCurrentItemAcrossAllDevices not implemented on this platform"}]);
281 #endif // OCTAGON
282 }
283
284 -(void)findItemPersistentRefByUUID:(NSString*)uuid
285 extraLoggingString:(NSString*)loggingStr
286 complete:(void (^) (NSData* persistentref, NSError* operror))xpcComplete
287 {
288 // The calling client might not handle CK types well. Sanitize!
289 void (^complete)(NSData*, NSError*) = ^(NSData* persistentref, NSError* error){
290 xpcComplete(persistentref, XPCSanitizeError(error));
291 };
292
293 CFErrorRef cferror = NULL;
294 CFTypeRef result = NULL;
295
296 // Must query per-class, so:
297 const SecDbSchema *newSchema = current_schema();
298 for (const SecDbClass *const *class = newSchema->classes; *class != NULL; class++) {
299 if(!((*class)->itemclass)) {
300 //Don't try to search non-item 'classes'
301 continue;
302 }
303
304 // Now that we're in an item class, reset any errSecItemNotFound errors from the last item class
305 CFReleaseNull(result);
306 CFReleaseNull(cferror);
307
308 _SecItemCopyMatching((__bridge CFDictionaryRef) @{
309 (__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
310 (id)kSecAttrSynchronizable: (id)kSecAttrSynchronizableAny,
311 (id)kSecMatchLimit : (id)kSecMatchLimitOne,
312 (id)kSecAttrUUID: uuid,
313 (id)kSecReturnPersistentRef: @YES,
314 },
315 &self->_client,
316 &result,
317 &cferror);
318
319 if(cferror && CFErrorGetCode(cferror) != errSecItemNotFound) {
320 break;
321 }
322
323 if(result) {
324 // Found the persistent ref! Quit searching.
325 break;
326 }
327 }
328
329 if(result && !cferror) {
330 secinfo("ckkscurrent", "Found current item for (%@: %@)", loggingStr, uuid);
331 } else {
332 secerror("ckkscurrent: No current item for (%@,%@): %@ %@", loggingStr, uuid, result, cferror);
333 }
334
335 complete((__bridge NSData*) result, (__bridge NSError*) cferror);
336 CFReleaseNull(result);
337 CFReleaseNull(cferror);
338 }
339
340 - (void) secItemDigest:(NSString *)itemClass
341 accessGroup:(NSString *)accessGroup
342 complete:(void (^)(NSArray *digest, NSError* error))complete
343 {
344 CFArrayRef accessGroups = self->_client.accessGroups;
345 __block CFErrorRef cferror = NULL;
346 __block CFArrayRef result = NULL;
347
348 if (itemClass == NULL || accessGroup == NULL) {
349 SecError(errSecParam, &cferror, CFSTR("parameter missing: %@"), _client.task);
350 complete(NULL, (__bridge NSError*) cferror);
351 CFReleaseNull(cferror);
352 return;
353 }
354
355 if (![itemClass isEqualToString:@"inet"] && ![itemClass isEqualToString:@"genp"]) {
356 SecError(errSecParam, &cferror, CFSTR("class %@ is not supported: %@"), itemClass, _client.task);
357 complete(NULL, (__bridge NSError*) cferror);
358 CFReleaseNull(cferror);
359 return;
360 }
361
362 if (!accessGroupsAllows(accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
363 SecError(errSecMissingEntitlement, &cferror, CFSTR("Client is missing access-group %@: %@"), accessGroup, _client.task);
364 complete(NULL, (__bridge NSError*) cferror);
365 CFReleaseNull(cferror);
366 return;
367 }
368
369 if (CFArrayContainsValue(accessGroups, CFRangeMake(0, CFArrayGetCount(accessGroups)), CFSTR("*"))) {
370 /* Having the special accessGroup "*" allows access to all accessGroups. */
371 accessGroups = NULL;
372 }
373
374 NSDictionary *attributes = @{
375 (__bridge NSString *)kSecClass : itemClass,
376 (__bridge NSString *)kSecAttrAccessGroup : accessGroup,
377 (__bridge NSString *)kSecAttrSynchronizable : (__bridge NSString *)kSecAttrSynchronizableAny,
378 };
379
380 Query *q = query_create_with_limit((__bridge CFDictionaryRef)attributes, _client.musr, 0, &(_client), &cferror);
381 if (q == NULL) {
382 SecError(errSecParam, &cferror, CFSTR("failed to build query: %@"), _client.task);
383 complete(NULL, (__bridge NSError*) cferror);
384 CFReleaseNull(cferror);
385 return;
386 }
387
388 bool ok = kc_with_dbt(false, &cferror, ^(SecDbConnectionRef dbt) {
389 return (bool)s3dl_copy_digest(dbt, q, &result, accessGroups, &cferror);
390 });
391
392 (void)ok;
393
394 complete((__bridge NSArray *)result, (__bridge NSError *)cferror);
395
396 (void)query_destroy(q, &cferror);
397
398 CFReleaseNull(result);
399 CFReleaseNull(cferror);
400 }
401
402
403 - (void) secKeychainDeleteMultiuser:(NSData *)uuid
404 complete:(void(^)(bool status, NSError* error))complete
405 {
406 __block CFErrorRef cferror = NULL;
407
408 #define SKDMUEntitlement @"com.apple.keychain.multiuser-admin"
409
410 if([self clientHasBooleanEntitlement: SKDMUEntitlement]) {
411 SecError(errSecNotAvailable, &cferror, CFSTR("secKeychainDeleteMultiuser: %@ need entitlement %@"), _client.task, SKDMUEntitlement);
412 complete(false, (__bridge NSError *)cferror);
413 CFReleaseNull(cferror);
414 return;
415 }
416 if ([uuid length] != 16) {
417 SecError(errSecNotAvailable, &cferror, CFSTR("secKeychainDeleteMultiuser: %@ uuid have wrong length: %d"), _client.task, (int)[uuid length]);
418 complete(false, (__bridge NSError *)cferror);
419 CFReleaseNull(cferror);
420 return;
421
422 }
423
424 #if TARGET_OS_IPHONE
425 bool status = kc_with_dbt(true, &cferror, ^(SecDbConnectionRef dbt) {
426 return SecServerDeleteAllForUser(dbt, (__bridge CFDataRef)uuid, false, &cferror);
427 });
428 #else
429 bool status = false;
430 #endif
431
432 complete(status, (__bridge NSError *)cferror);
433 CFReleaseNull(cferror);
434 }
435
436 - (void)secItemVerifyBackupIntegrity:(BOOL)lightweight
437 completion:(void (^)(NSDictionary<NSString*, NSString*>* results, NSError* error))completion
438 {
439 [[SecDbBackupManager manager] verifyBackupIntegrity:lightweight completion:completion];
440 }
441
442
443 - (void)secItemDeleteForAppClipApplicationIdentifier:(NSString*)identifier
444 completion:(void (^)(OSStatus))completion
445 {
446 if (![self clientHasBooleanEntitlement:(__bridge NSString*)kSecEntitlementPrivateAppClipDeletion]) {
447 completion(errSecMissingEntitlement);
448 return;
449 }
450
451 completion(SecServerDeleteForAppClipApplicationIdentifier((__bridge CFStringRef)identifier));
452 }
453
454 @end