]> git.saurik.com Git - apple/security.git/blob - IDSKeychainSyncingProxy/IDSProxy.m
Security-57740.31.2.tar.gz
[apple/security.git] / IDSKeychainSyncingProxy / IDSProxy.m
1 /*
2 * Copyright (c) 2012-2014 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 //
25 // IDSProxy.m
26 // ids-xpc
27 //
28
29
30 #import <Foundation/NSArray.h>
31 #import <Foundation/Foundation.h>
32
33 #import <Security/SecBasePriv.h>
34 #import <Security/SecItemPriv.h>
35 #import <utilities/debugging.h>
36 #import <notify.h>
37
38 #include <Security/CKBridge/SOSCloudKeychainConstants.h>
39 #include <Security/SecureObjectSync/SOSARCDefines.h>
40 #include <Security/SecureObjectSync/SOSCloudCircle.h>
41 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
42
43 #import <IDS/IDS.h>
44 #import <os/activity.h>
45
46 #include <utilities/SecAKSWrappers.h>
47 #include <utilities/SecCFWrappers.h>
48 #include <utilities/SecCFRelease.h>
49 #include <AssertMacros.h>
50
51 #import "IDSProxy.h"
52 #import "IDSKeychainSyncingProxy+IDSProxyReceiveMessage.h"
53 #import "IDSKeychainSyncingProxy+IDSProxySendMessage.h"
54 #import "IDSKeychainSyncingProxy+IDSProxyThrottle.h"
55 #import "IDSPersistentState.h"
56
57 #define kSecServerKeychainChangedNotification "com.apple.security.keychainchanged"
58 #define kSecServerPeerInfoAvailable "com.apple.security.fpiAvailable"
59
60 #define IDSServiceNameKeychainSync "com.apple.private.alloy.keychainsync"
61 static NSString *kMonitorState = @"MonitorState";
62 static NSString *kExportUnhandledMessages = @"UnhandledMessages";
63 static const char *kStreamName = "com.apple.notifyd.matching";
64
65 NSString *const IDSSendMessageOptionForceEncryptionOffKey = @"IDSSendMessageOptionForceEncryptionOff";
66 static const int64_t kRetryTimerLeeway = (NSEC_PER_MSEC * 250); // 250ms leeway for handling unhandled messages.
67 static const int64_t kMinMessageRetryDelay = (NSEC_PER_SEC * 8);
68
69 CFStringRef kSOSErrorDomain = CFSTR("com.apple.security.sos.error");
70
71 CFIndex kSOSErrorPeerNotFound = 1032;
72 CFIndex SECD_RUN_AS_ROOT_ERROR = 1041;
73
74 #define IDSPROXYSCOPE "IDSProxy"
75
76 @implementation IDSKeychainSyncingProxy
77
78 + (IDSKeychainSyncingProxy *) idsProxy
79 {
80 static IDSKeychainSyncingProxy *idsProxy;
81 if (!idsProxy) {
82 static dispatch_once_t onceToken;
83 dispatch_once(&onceToken, ^{
84 idsProxy = [[self alloc] init];
85 });
86 }
87 return idsProxy;
88 }
89
90 -(NSDictionary*) exportState
91 {
92 return @{ kMonitorState:_monitor,
93 kExportUnhandledMessages:_unhandledMessageBuffer
94 };
95
96 }
97 - (void)persistState
98 {
99 if([_unhandledMessageBuffer count] > 0){
100 [IDSKeychainSyncingProxyPersistentState setUnhandledMessages:[self exportState]];
101 }
102 }
103
104 - (void) importIDSState: (NSMutableDictionary*) state
105 {
106 _unhandledMessageBuffer = state[kExportUnhandledMessages];
107 if(!_unhandledMessageBuffer)
108 _unhandledMessageBuffer = [NSMutableDictionary dictionary];
109
110 _monitor = state[kMonitorState];
111 if(_monitor == nil)
112 _monitor = [NSMutableDictionary dictionary];
113 }
114
115 - (id)init
116 {
117 if (self = [super init])
118 {
119 secnotice("event", "%@ start", self);
120
121 _isIDSInitDone = false;
122 _service = nil;
123 _calloutQueue = dispatch_queue_create("IDSCallout", DISPATCH_QUEUE_SERIAL);
124 _unhandledMessageBuffer = [ [NSMutableDictionary alloc] initWithCapacity: 0];
125 _pingTimers = [ [NSMutableDictionary alloc] initWithCapacity: 0];
126
127 _isSecDRunningAsRoot = false;
128 _doesSecDHavePeer = true;
129
130 secdebug(IDSPROXYSCOPE, "%@ done", self);
131
132 [self doIDSInitialization];
133 if(_isIDSInitDone)
134 [self doSetIDSDeviceID];
135
136
137 // Register for lock state changes
138 xpc_set_event_stream_handler(kStreamName, dispatch_get_main_queue(),
139 ^(xpc_object_t notification){
140 [self streamEvent:notification];
141 });
142
143 _retryTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
144 dispatch_source_set_timer(_retryTimer, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, kRetryTimerLeeway);
145 dispatch_source_set_event_handler(_retryTimer, ^{
146 [self timerFired];
147 });
148 dispatch_resume(_retryTimer);
149 [self importIDSState: [IDSKeychainSyncingProxyPersistentState idsState]];
150
151 int notificationToken;
152 notify_register_dispatch(kSecServerKeychainChangedNotification, &notificationToken, dispatch_get_main_queue(),
153 ^ (int token __unused)
154 {
155 secinfo("backoff", "keychain changed, wiping backoff monitor state");
156 _monitor = [NSMutableDictionary dictionary];
157 });
158 int peerInfo;
159 notify_register_dispatch(kSecServerPeerInfoAvailable, &peerInfo, dispatch_get_main_queue(),
160 ^ (int token __unused)
161 {
162 secinfo("IDS Transport", "secd has a peer info");
163 if(_doesSecDHavePeer == false){
164 _doesSecDHavePeer = true;
165 [self doSetIDSDeviceID];
166 }
167 });
168
169 [self updateUnlockedSinceBoot];
170 [self updateIsLocked];
171 if (!_isLocked)
172 [self keybagDidUnlock];
173
174
175 }
176 return self;
177 }
178
179 - (void)streamEvent:(xpc_object_t)notification
180 {
181 #if (!TARGET_IPHONE_SIMULATOR)
182 const char *notificationName = xpc_dictionary_get_string(notification, "Notification");
183 if (!notificationName) {
184 } else if (strcmp(notificationName, kUserKeybagStateChangeNotification)==0) {
185 return [self keybagStateChange];
186 }
187 const char *eventName = xpc_dictionary_get_string(notification, "XPCEventName");
188 char *desc = xpc_copy_description(notification);
189 secnotice("event", "%@ event: %s name: %s desc: %s", self, eventName, notificationName, desc);
190 if (desc)
191 free((void *)desc);
192 #endif
193 }
194
195 - (void) keybagDidLock
196 {
197 secnotice("IDS Transport", "%@ locking!", self);
198 }
199
200 - (void) keybagDidUnlock
201 {
202 secnotice("IDS Transport", "%@ unlocking!", self);
203 [self handleAllPendingMessage];
204 }
205
206 - (BOOL) updateUnlockedSinceBoot
207 {
208 CFErrorRef aksError = NULL;
209 if (!SecAKSGetHasBeenUnlocked(&_unlockedSinceBoot, &aksError)) {
210 secerror("%@ Got error from SecAKSGetHasBeenUnlocked: %@", self, aksError);
211 CFReleaseSafe(aksError);
212 return NO;
213 }
214 return YES;
215 }
216
217 - (BOOL) updateIsLocked
218 {
219 CFErrorRef aksError = NULL;
220 if (!SecAKSGetIsLocked(&_isLocked, &aksError)) {
221 secerror("%@ Got error querying lock state: %@", self, aksError);
222 CFReleaseSafe(aksError);
223 return NO;
224 }
225 secerror("updateIsLocked: %d", _isLocked);
226 if (!_isLocked)
227 _unlockedSinceBoot = YES;
228 return YES;
229 }
230
231 - (void) keybagStateChange
232 {
233 os_activity_initiate("keybagStateChanged", OS_ACTIVITY_FLAG_DEFAULT, ^{
234 secerror("keybagStateChange! was locked: %d", _isLocked);
235 BOOL wasLocked = _isLocked;
236 if ([self updateIsLocked]) {
237 if (wasLocked == _isLocked)
238 secdebug("IDS Transport", "%@ still %s ignoring", self, _isLocked ? "locked" : "unlocked");
239 else if (_isLocked)
240 [self keybagDidLock];
241 else
242 [self keybagDidUnlock];
243 }
244 });
245 }
246
247
248 - (void)timerFired
249 {
250 if(_unhandledMessageBuffer)
251 secnotice("IDS Transport", "%@ attempting to hand unhandled messages to securityd, here is our message queue: %@", self, _unhandledMessageBuffer);
252
253 if(_isLocked)
254 _retryTimerScheduled = NO;
255 else if([_unhandledMessageBuffer count] == 0)
256 _retryTimerScheduled = NO;
257 else if (_retryTimerScheduled && !_isLocked)
258 [self handleAllPendingMessage];
259 else
260 [[IDSKeychainSyncingProxy idsProxy] scheduleRetryRequestTimer];
261
262 }
263
264 - (void)scheduleRetryRequestTimer
265 {
266 secnotice("IDS Transport", "scheduling unhandled messages timer");
267 dispatch_source_set_timer(_retryTimer, dispatch_time(DISPATCH_TIME_NOW, kMinMessageRetryDelay), DISPATCH_TIME_FOREVER, kRetryTimerLeeway);
268 _retryTimerScheduled = YES;
269 }
270
271 - (void)doIDSInitialization
272 {
273
274 secnotice("IDS Transport", "doIDSInitialization!");
275
276 _service = [[IDSService alloc] initWithService: @IDSServiceNameKeychainSync];
277
278 if( _service == nil ){
279 _isIDSInitDone = false;
280 secerror("Could not create ids service");
281 }
282 else{
283 secnotice("IDS Transport", "IDS Transport Successfully set up IDS!");
284 [_service addDelegate:self queue: dispatch_get_main_queue()];
285
286 _isIDSInitDone = true;
287 if(_isSecDRunningAsRoot == false)
288 [self doSetIDSDeviceID];
289 }
290 }
291
292 - (void) doSetIDSDeviceID
293 {
294 NSInteger code = 0;
295 NSString *errorMessage = nil;
296 __block NSString* deviceID;
297
298 if(!_isIDSInitDone){
299 [self doIDSInitialization];
300 }
301 require_action_quiet(_isSecDRunningAsRoot == false, fail, errorMessage = @"cannot set IDS device ID, secd is running as root"; code = SECD_RUN_AS_ROOT_ERROR;);
302 require_action_quiet(_doesSecDHavePeer == true, fail, errorMessage = @"cannot set IDS deviceID, secd does not have a full peer info for account"; code = kSOSErrorPeerNotFound);
303 require_action_quiet(_isIDSInitDone, fail, errorMessage = @"IDSKeychainSyncingProxy can't set up the IDS service"; code = kSecIDSErrorNotRegistered);
304 require_action_quiet(!_isLocked, fail, errorMessage = @"IDSKeychainSyncingProxy can't set device ID, device is locked"; code = kSecIDSErrorDeviceIsLocked);
305
306 deviceID = IDSCopyLocalDeviceUniqueID();
307 secdebug("IDS Transport", "This is our IDS device ID: %@", deviceID);
308
309 require_action_quiet(deviceID != nil, fail, errorMessage = @"IDSKeychainSyncingProxy could not retrieve device ID from keychain"; code = kSecIDSErrorNoDeviceID);
310
311 if(_inCallout && _isSecDRunningAsRoot == false){
312 _shadowDoSetIDSDeviceID = YES;
313 }
314 else{
315 _setIDSDeviceID = YES;
316 [self calloutWith:^(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *, bool, bool)) {
317 CFErrorRef localError = NULL;
318 bool handledSettingID = false;
319 handledSettingID = SOSCCSetDeviceID((__bridge CFStringRef) deviceID, &localError);
320 if(!handledSettingID && localError != NULL){
321 if(CFErrorGetCode(localError) == SECD_RUN_AS_ROOT_ERROR){
322 secerror("SETTING RUN AS ROOT ERROR: %@", localError);
323 _isSecDRunningAsRoot = true;
324 }
325 else if (CFErrorGetCode(localError) == -536870174 && CFErrorGetDomain(localError) == kSecKernDomain) {
326 secnotice("IDS Transport", "system is locked, cannot set device ID, error: %@", localError);
327 _isLocked = true;
328 }
329 else if (CFErrorGetCode(localError) == kSOSErrorPeerNotFound && CFStringCompare(CFErrorGetDomain(localError), kSOSErrorDomain, 0) == 0){
330 secnotice("IDS Transport","securityd does not have a peer yet , error: %@", localError);
331 _doesSecDHavePeer = false;
332 }
333 }
334 else
335 _setIDSDeviceID = NO;
336
337 CFReleaseNull(localError);
338 dispatch_async(queue, ^{
339 done(nil, NO, YES);
340 });
341 }];
342 }
343 fail:
344 if(errorMessage != nil){
345 secerror("Setting device ID error: %@, code: %ld", errorMessage, (long)code);
346 }
347 }
348
349 - (void) calloutWith: (void(^)(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *handledMessages, bool handledPendingMessage, bool handledSettingDeviceID))) callout
350 {
351 // In IDSKeychainSyncingProxy serial queue
352 dispatch_queue_t idsproxy_queue = dispatch_get_main_queue();
353
354 // dispatch_get_global_queue - well-known global concurrent queue
355 // dispatch_get_main_queue - default queue that is bound to the main thread
356 xpc_transaction_begin();
357 dispatch_async(_calloutQueue, ^{
358 __block NSMutableDictionary *myPending;
359 __block bool myHandlePendingMessage;
360 __block bool myDoSetDeviceID;
361 __block bool wasLocked;
362 dispatch_sync(idsproxy_queue, ^{
363 myPending = [_unhandledMessageBuffer copy];
364 myHandlePendingMessage = _handleAllPendingMessages;
365 myDoSetDeviceID = _setIDSDeviceID;
366 wasLocked = _isLocked;
367
368 _inCallout = YES;
369
370 _shadowHandleAllPendingMessages = NO;
371 });
372
373 callout(myPending, myHandlePendingMessage, myDoSetDeviceID, idsproxy_queue, ^(NSMutableDictionary *handledMessages, bool handledPendingMessage, bool handledSetDeviceID) {
374 secdebug("event", "%@ %s%s before callout handled: %s%s", self, myHandlePendingMessage ? "P" : "p", myDoSetDeviceID ? "D" : "d", handledPendingMessage ? "H" : "h", handledSetDeviceID ? "I" : "i");
375
376 // In IDSKeychainSyncingProxy's serial queue
377 _inCallout = NO;
378
379 // Update setting device id
380 _setIDSDeviceID = ((myDoSetDeviceID && !handledSetDeviceID));
381
382 _shadowDoSetIDSDeviceID = NO;
383
384 xpc_transaction_end();
385 });
386 });
387 }
388
389 - (void) sendKeysCallout: (NSMutableDictionary*(^)(NSMutableDictionary* pending, NSError** error)) handleMessages {
390 [self calloutWith: ^(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *, bool, bool)) {
391 NSError* error = NULL;
392
393 NSMutableDictionary* handled = handleMessages(pending, &error);
394
395 dispatch_async(queue, ^{
396 if (!handled && error) {
397 secerror("%@ did not handle message: %@", self, error);
398 }
399
400 done(handled, NO, NO);
401 });
402 }];
403 }
404
405 NSString* createErrorString(NSString* format, ...)
406 {
407 va_list va;
408 va_start(va, format);
409 NSString* errorString = ([[NSString alloc] initWithFormat:format arguments:va]);
410 va_end(va);
411 return errorString;
412
413 }
414
415 @end