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