]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ipc/server_xpc.m
Security-58286.20.16.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
30 #include <Security/SecEntitlements.h>
31 #include <Security/SecItemPriv.h>
32 #include <securityd/SecItemServer.h>
33 #include <securityd/SecItemSchema.h>
34 #include <securityd/SecItemDb.h>
35
36 #include "keychain/ckks/CKKSViewManager.h"
37
38 @implementation SecuritydXPCServer (SecuritydXPCProtocol)
39
40 - (void) SecItemAddAndNotifyOnSync:(NSDictionary*) attributes
41 syncCallback:(id<SecuritydXPCCallbackProtocol>) callback
42 complete:(void (^) (NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror)) complete
43 {
44 CFErrorRef cferror = NULL;
45 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
46 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
47 //TODO: ensure cferror can transit xpc
48 complete(NULL, NULL, (__bridge NSError*) cferror);
49 CFReleaseNull(cferror);
50 return;
51 }
52
53 if(attributes[(id)kSecAttrDeriveSyncIDFromItemAttributes] ||
54 attributes[(id)kSecAttrPCSPlaintextServiceIdentifier] ||
55 attributes[(id)kSecAttrPCSPlaintextPublicKey] ||
56 attributes[(id)kSecAttrPCSPlaintextPublicIdentity]) {
57
58 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSPlaintextFields]) {
59 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ does not have entitlement %@, but is using SPI anyway"), _client.task, kSecEntitlementPrivateCKKSPlaintextFields);
60 complete(NULL, NULL, (__bridge NSError*) cferror);
61 CFReleaseNull(cferror);
62 return;
63 }
64 }
65
66 CFTypeRef cfresult = NULL;
67
68 NSMutableDictionary* callbackQuery = [attributes mutableCopy];
69 callbackQuery[@"f_ckkscallback"] = ^void (bool didSync, CFErrorRef syncerror) {
70 [callback callCallback: didSync error: (__bridge NSError*)syncerror];
71 };
72
73 _SecItemAdd((__bridge CFDictionaryRef) callbackQuery, &_client, &cfresult, &cferror);
74
75 //TODO: ensure cferror can transit xpc
76
77 // SecItemAdd returns Some CF Object, but NSXPC is pretty adamant that everything be a specific NS type. Split it up here:
78 if(!cfresult) {
79 complete(NULL, NULL, (__bridge NSError *)(cferror));
80 } else if( CFGetTypeID(cfresult) == CFDictionaryGetTypeID()) {
81 complete((__bridge NSDictionary *)(cfresult), NULL, (__bridge NSError *)(cferror));
82 } else if( CFGetTypeID(cfresult) == CFArrayGetTypeID()) {
83 complete(NULL, (__bridge NSArray *)cfresult, (__bridge NSError *)(cferror));
84 } else {
85 // TODO: actually error here
86 complete(NULL, NULL, NULL);
87 }
88 CFReleaseNull(cfresult);
89 CFReleaseNull(cferror);
90 }
91
92 - (void)secItemSetCurrentItemAcrossAllDevices:(NSData* _Nonnull)newItemPersistentRef
93 newCurrentItemHash:(NSData* _Nonnull)newItemSHA1
94 accessGroup:(NSString* _Nonnull)accessGroup
95 identifier:(NSString* _Nonnull)identifier
96 viewHint:(NSString* _Nonnull)viewHint
97 oldCurrentItemReference:(NSData* _Nullable)oldCurrentItemPersistentRef
98 oldCurrentItemHash:(NSData* _Nullable)oldItemSHA1
99 complete:(void (^) (NSError* _Nullable operror)) complete
100 {
101 #if OCTAGON
102 __block CFErrorRef cferror = NULL;
103 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
104 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
105 complete((__bridge NSError*) cferror);
106 CFReleaseNull(cferror);
107 return;
108 }
109
110 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSWriteCurrentItemPointers]) {
111 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ does not have entitlement %@"), _client.task, kSecEntitlementPrivateCKKSWriteCurrentItemPointers);
112 complete((__bridge NSError*) cferror);
113 CFReleaseNull(cferror);
114 return;
115 }
116
117 if (!accessGroupsAllows(self->_client.accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
118 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: client is missing access-group %@: %@"), accessGroup, _client.task);
119 complete((__bridge NSError*)cferror);
120 CFReleaseNull(cferror);
121 return;
122 }
123
124 __block SecDbItemRef newItem = NULL;
125 __block SecDbItemRef oldItem = NULL;
126
127 bool ok = kc_with_dbt(false, &cferror, ^bool (SecDbConnectionRef dbt) {
128 Query *q = query_create_with_limit( (__bridge CFDictionaryRef) @{
129 (__bridge NSString *)kSecValuePersistentRef : newItemPersistentRef,
130 (__bridge NSString *)kSecAttrAccessGroup : accessGroup,
131 },
132 NULL,
133 1,
134 &cferror);
135 if(cferror) {
136 secerror("couldn't create query: %@", cferror);
137 return false;
138 }
139
140 if(!SecDbItemQuery(q, NULL, dbt, &cferror, ^(SecDbItemRef item, bool *stop) {
141 newItem = CFRetainSafe(item);
142 })) {
143 query_destroy(q, NULL);
144 return false;
145 }
146
147 if(!query_destroy(q, &cferror)) {
148 return false;
149 };
150
151 if(oldCurrentItemPersistentRef) {
152 q = query_create_with_limit( (__bridge CFDictionaryRef) @{
153 (__bridge NSString *)kSecValuePersistentRef : oldCurrentItemPersistentRef,
154 (__bridge NSString *)kSecAttrAccessGroup : accessGroup,
155 },
156 NULL,
157 1,
158 &cferror);
159 if(cferror) {
160 secerror("couldn't create query: %@", cferror);
161 return false;
162 }
163
164 if(!SecDbItemQuery(q, NULL, dbt, &cferror, ^(SecDbItemRef item, bool *stop) {
165 oldItem = CFRetainSafe(item);
166 })) {
167 query_destroy(q, NULL);
168 return false;
169 }
170
171 if(!query_destroy(q, &cferror)) {
172 return false;
173 };
174 }
175
176 CKKSViewManager* manager = [CKKSViewManager manager];
177 if(!manager) {
178 secerror("SecItemSetCurrentItemAcrossAllDevices: no view manager?");
179 cferror = (CFErrorRef) CFBridgingRetain([NSError errorWithDomain:@"securityd" code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"No view manager, cannot forward request"}]);
180 return false;
181 }
182 [manager setCurrentItemForAccessGroup:newItem
183 hash:newItemSHA1
184 accessGroup:accessGroup
185 identifier:identifier
186 viewHint:viewHint
187 replacing:oldItem
188 hash:oldItemSHA1
189 complete:complete];
190 return true;
191 });
192
193 CFReleaseNull(newItem);
194 CFReleaseNull(oldItem);
195
196 if(!ok) {
197 secnotice("ckks", "SecItemSetCurrentItemAcrossAllDevices failed due to: %@", cferror);
198 complete((__bridge NSError*) cferror);
199 }
200 CFReleaseNull(cferror);
201 #else // ! OCTAGON
202 complete([NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemSetCurrentItemAcrossAllDevices not implemented on this platform"}]);
203 #endif // OCTAGON
204 }
205
206 -(void)secItemFetchCurrentItemAcrossAllDevices:(NSString*)accessGroup
207 identifier:(NSString*)identifier
208 viewHint:(NSString*)viewHint
209 fetchCloudValue:(bool)fetchCloudValue
210 complete:(void (^) (NSData* persistentref, NSError* operror)) complete
211 {
212 #if OCTAGON
213 CFErrorRef cferror = NULL;
214 if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
215 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
216 complete(NULL, (__bridge NSError*) cferror);
217 CFReleaseNull(cferror);
218 return;
219 }
220
221 if(![self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementPrivateCKKSReadCurrentItemPointers]) {
222 SecError(errSecNotAvailable, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: %@ does not have entitlement %@"), _client.task, kSecEntitlementPrivateCKKSReadCurrentItemPointers);
223 complete(NULL, (__bridge NSError*) cferror);
224 CFReleaseNull(cferror);
225 return;
226 }
227
228 if (!accessGroupsAllows(self->_client.accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
229 SecError(errSecMissingEntitlement, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: client is missing access-group %@: %@"), accessGroup, _client.task);
230 complete(NULL, (__bridge NSError*)cferror);
231 CFReleaseNull(cferror);
232 return;
233 }
234
235 [[CKKSViewManager manager] getCurrentItemForAccessGroup:accessGroup
236 identifier:identifier
237 viewHint:viewHint
238 fetchCloudValue:fetchCloudValue
239 complete:^(NSString* uuid, NSError* error) {
240 if(error || !uuid) {
241 complete(NULL, error);
242 return;
243 }
244
245 // Find the persisent ref and return it.
246 [self findItemPersistentRefByUUID:uuid complete:complete];
247 }];
248 #else // ! OCTAGON
249 complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemFetchCurrentItemAcrossAllDevices not implemented on this platform"}]);
250 #endif // OCTAGON
251 }
252
253 -(void)findItemPersistentRefByUUID:(NSString*)uuid
254 complete:(void (^) (NSData* persistentref, NSError* operror)) complete
255 {
256 CFErrorRef cferror = NULL;
257 CFTypeRef result = NULL;
258
259 // Must query per-class, so:
260 const SecDbSchema *newSchema = current_schema();
261 for (const SecDbClass *const *class = newSchema->classes; *class != NULL; class++) {
262 CFReleaseNull(result);
263 CFReleaseNull(cferror);
264
265 if(!((*class)->itemclass)) {
266 //Don't try to search non-item 'classes'
267 continue;
268 }
269
270 _SecItemCopyMatching((__bridge CFDictionaryRef) @{
271 (__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
272 (id)kSecAttrSynchronizable: (id)kSecAttrSynchronizableAny,
273 (id)kSecMatchLimit : (id)kSecMatchLimitOne,
274 (id)kSecAttrUUID: uuid,
275 (id)kSecReturnPersistentRef: @YES,
276 },
277 &self->_client,
278 &result,
279 &cferror);
280
281 if(cferror && CFErrorGetCode(cferror) != errSecItemNotFound) {
282 break;
283 }
284
285 if(result) {
286 // Found the persistent ref! Quit searching.
287 break;
288 }
289 }
290
291 complete((__bridge NSData*) result, (__bridge NSError*) cferror);
292 CFReleaseNull(result);
293 CFReleaseNull(cferror);
294 }
295
296 - (void) secItemDigest:(NSString *)itemClass
297 accessGroup:(NSString *)accessGroup
298 complete:(void (^)(NSArray *digest, NSError* error))complete
299 {
300 CFArrayRef accessGroups = self->_client.accessGroups;
301 __block CFErrorRef cferror = NULL;
302 __block CFArrayRef result = NULL;
303
304 if (itemClass == NULL || accessGroup == NULL) {
305 SecError(errSecParam, &cferror, CFSTR("parameter missing: %@"), _client.task);
306 complete(NULL, (__bridge NSError*) cferror);
307 CFReleaseNull(cferror);
308 return;
309 }
310
311 if (![itemClass isEqualToString:@"inet"] && ![itemClass isEqualToString:@"genp"]) {
312 SecError(errSecParam, &cferror, CFSTR("class %@ is not supported: %@"), itemClass, _client.task);
313 complete(NULL, (__bridge NSError*) cferror);
314 CFReleaseNull(cferror);
315 return;
316 }
317
318 if (!accessGroupsAllows(accessGroups, (__bridge CFStringRef)accessGroup, &_client)) {
319 SecError(errSecMissingEntitlement, &cferror, CFSTR("Client is missing access-group %@: %@"), accessGroup, _client.task);
320 complete(NULL, (__bridge NSError*) cferror);
321 CFReleaseNull(cferror);
322 return;
323 }
324
325 if (CFArrayContainsValue(accessGroups, CFRangeMake(0, CFArrayGetCount(accessGroups)), CFSTR("*"))) {
326 /* Having the special accessGroup "*" allows access to all accessGroups. */
327 accessGroups = NULL;
328 }
329
330 NSDictionary *attributes = @{
331 (__bridge NSString *)kSecClass : itemClass,
332 (__bridge NSString *)kSecAttrAccessGroup : accessGroup,
333 (__bridge NSString *)kSecAttrSynchronizable : (__bridge NSString *)kSecAttrSynchronizableAny,
334 };
335
336 Query *q = query_create_with_limit((__bridge CFDictionaryRef)attributes, _client.musr, 0, &cferror);
337 if (q == NULL) {
338 SecError(errSecParam, &cferror, CFSTR("failed to build query: %@"), _client.task);
339 complete(NULL, (__bridge NSError*) cferror);
340 CFReleaseNull(cferror);
341 return;
342 }
343
344 bool ok = kc_with_dbt(false, &cferror, ^(SecDbConnectionRef dbt) {
345 return (bool)s3dl_copy_digest(dbt, q, &result, accessGroups, &cferror);
346 });
347
348 (void)ok;
349
350 complete((__bridge NSArray *)result, (__bridge NSError *)cferror);
351
352 (void)query_destroy(q, &cferror);
353
354 CFReleaseNull(result);
355 CFReleaseNull(cferror);
356 }
357
358
359 @end