7 #import <Foundation/Foundation.h>
8 #import <Foundation/NSXPCConnection_Private.h>
9 #import <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
10 #import <objc/runtime.h>
11 #import <utilities/debugging.h>
13 #import <securityd/SOSCloudCircleServer.h>
14 #import <Security/SecureObjectSync/SOSPeerInfo.h>
15 #import <Security/SecureObjectSync/SOSCloudCircleInternal.h>
16 #import <Security/SecureObjectSync/SOSViews.h>
17 #import <Security/SecureObjectSync/SOSInternal.h>
19 #import "DeviceSimulator.h"
20 #import "SOSCloudKeychainClient.h"
21 #import "MultiDeviceNetworkingProtocol.h"
22 #import "SecCFWrappers.h"
25 struct SOSCloudTransport MDNTransport;
29 NSString *deviceInstance = NULL;
30 static NSString *deviceHomeDir = NULL;
31 static MDNetwork *deviceNetwork = NULL;
33 @interface MDNetwork : NSObject<MultiDeviceNetworkingProtocol,MultiDeviceNetworkingCallbackProtocol,NSXPCListenerDelegate>
34 @property NSXPCConnection *connection;
35 @property NSXPCListener *callbackListener;
36 @property dispatch_queue_t flushQueue;
37 @property NSMutableDictionary *pendingKeys;
38 @property NSSet *registeredKeys;
39 - (instancetype)initWithConnection:(NSXPCConnection *)connection;
42 #pragma clang diagnostic push
43 #pragma clang diagnostic ignored "-Wprotocol"
44 @implementation MDNetwork
46 - (instancetype)initWithConnection:(NSXPCConnection *)connection
50 self.connection = connection;
51 self.callbackListener = [NSXPCListener anonymousListener];
52 self.callbackListener.delegate = self;
53 [self.callbackListener resume];
55 __typeof(self) weakSelf = self;
56 self.connection.invalidationHandler = ^{
57 __typeof(self) strongSelf = weakSelf;
58 [strongSelf.callbackListener invalidate];
59 strongSelf.callbackListener = nil;
64 self.flushQueue = dispatch_queue_create("MDNetwork.flushqueue", 0);
65 self.pendingKeys = [NSMutableDictionary dictionary];
66 self.registeredKeys = [NSSet set];
68 [[self.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
69 NSLog(@"network register callback failed with: %@", error);
71 }] MDNRegisterCallback:[self.callbackListener endpoint] complete:^void(NSDictionary *values, NSError *error) {
79 - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
81 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingCallbackProtocol)];
82 newConnection.exportedObject = self;
83 [newConnection resume];
87 - (void)MDNCItemsChanged:(NSDictionary *)values complete:(MDNComplete)complete
89 NSMutableDictionary *requestedKeys = [NSMutableDictionary dictionary];
90 @synchronized(self.pendingKeys) {
91 secnotice("MDN", "items update: %@ (already pending: %@)", values, self.pendingKeys);
93 [self.pendingKeys addEntriesFromDictionary:values];
94 for (NSString *key in self.registeredKeys) {
95 id data = self.pendingKeys[key];
97 requestedKeys[key] = data;
98 self.pendingKeys[key] = nil;
102 if (requestedKeys.count) {
103 dispatch_async(self.flushQueue, ^{
104 secnotice("MDN", "engine processing keys: %@", requestedKeys);
105 NSArray *handled = CFBridgingRelease(SOSCCHandleUpdateMessage((__bridge CFDictionaryRef)requestedKeys));
107 * Ok, our dear Engine might not have handled all messages.
108 * So put them back unless there are new messages around that
109 * have overwritten the previous message.
111 for (NSString *key in handled) {
112 requestedKeys[key] = NULL;
114 if (requestedKeys.count) {
115 @synchronized(self.pendingKeys) {
116 for (NSString *key in requestedKeys) {
117 if (self.pendingKeys[key] == nil) {
118 self.pendingKeys[key] = requestedKeys[key];
125 complete(NULL, NULL);
128 /* Oh, ObjC, you are my friend */
129 - (void)forwardInvocation:(NSInvocation *)invocation
131 struct objc_method_description desc = protocol_getMethodDescription(@protocol(MultiDeviceNetworkingProtocol), [invocation selector], true, true);
132 if (desc.name == NULL) {
133 [super forwardInvocation:invocation];
135 __block bool gogogo = true;
136 id object = [self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
138 NSLog(@"network failed with: %@", error);
141 if(gogogo) [invocation invokeWithTarget:object];
145 #pragma clang diagnostic pop
147 #define HANDLE_NO_NETWORK(_replyBlock) \
148 if (deviceNetwork == NULL) { \
149 replyBlock((__bridge CFDictionaryRef)@{}, (__bridge CFErrorRef)[NSError errorWithDomain:@"MDNNetwork" code:1 userInfo:NULL]); \
155 DSCloudPut(SOSCloudTransportRef transport, CFDictionaryRef valuesToPut, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
158 HANDLE_NO_NETWORK(replyBlock);
159 secnotice("MDN", "CloudPut: %@", valuesToPut);
161 [deviceNetwork MDNCloudPut:(__bridge NSDictionary *)valuesToPut complete:^(NSDictionary *returnedValues, NSError *error) {
162 dispatch_async(processQueue, ^{
163 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
169 static NSString *nKeyAlwaysKeys = @"AlwaysKeys";
170 static NSString *nKeyFirstUnlockKeys = @"FirstUnlockKeys";
171 static NSString *nKeyUnlockedKeys = @"UnlockedKeys";
172 static NSString *nMessageKeyParameter = @"KeyParameter";
173 static NSString *nMessageCircle = @"Circle";
174 static NSString *nMessageMessage = @"Message";
178 DSCloudUpdateKeys(SOSCloudTransportRef transport, CFDictionaryRef cfkeys, CFStringRef accountUUID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
181 * Currently doesn't deal with lock state, just smash (HULK!) them all together
184 NSDictionary *keys = (__bridge NSDictionary *)cfkeys;
185 NSMutableSet *newSet = [NSMutableSet set];
187 @synchronized(deviceNetwork.pendingKeys) {
188 for (NSString *type in @[ nMessageKeyParameter, nMessageCircle, nMessageMessage]) {
189 NSDictionary *typeDict = keys[type];
191 for (NSString *lockType in @[ nKeyAlwaysKeys, nKeyFirstUnlockKeys, nKeyUnlockedKeys]) {
192 NSArray *lockArray = typeDict[lockType];
194 [newSet unionSet:[NSMutableSet setWithArray:lockArray]];
198 deviceNetwork.registeredKeys = newSet;
200 /* update engine with stuff */
201 [deviceNetwork MDNCItemsChanged:@{} complete:^(NSDictionary *returnedValues, NSError *error) {
203 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
209 DSCloudGetDeviceID(SOSCloudTransportRef transport, CloudKeychainReplyBlock replyBlock)
212 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
217 DSCloudGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
220 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
224 DSCloudGetAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
227 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
231 DSCloudsynchronize(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
234 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
238 DSCloudsynchronizeAndWait(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
241 HANDLE_NO_NETWORK(replyBlock);
243 [deviceNetwork MDNCloudsynchronizeAndWait:@{} complete:^(NSDictionary *returnedValues, NSError *error) {
244 dispatch_async(processQueue, ^{
245 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
252 DSCloudRemoveObjectForKey(SOSCloudTransportRef transport, CFStringRef keyToRemove, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
254 if (keyToRemove == NULL) {
255 dispatch_async(processQueue, ^{
256 replyBlock(NULL, NULL);
262 HANDLE_NO_NETWORK(replyBlock);
263 [deviceNetwork MDNCloudRemoveKeys:@[(__bridge NSString *)keyToRemove] complete:^(NSDictionary *returnedValues, NSError *error) {
264 dispatch_async(processQueue, ^{
265 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
271 static void DSCloudremoveKeys(SOSCloudTransportRef transport, CFArrayRef keys, CFStringRef accountUUID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
274 HANDLE_NO_NETWORK(replyBlock);
275 [deviceNetwork MDNCloudRemoveKeys:(__bridge NSArray *)keys complete:^(NSDictionary *returnedValues, NSError *error) {
276 dispatch_async(processQueue, ^{
277 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
284 DSCloudclearAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
287 HANDLE_NO_NETWORK(replyBlock);
288 [deviceNetwork MDNCloudRemoveKeys:NULL complete:^(NSDictionary *returnedValues, NSError *error) {
289 dispatch_async(processQueue, ^{
290 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
297 DSCloudhasPendingKey(SOSCloudTransportRef transport, CFStringRef keyName, CFErrorRef* error)
300 @synchronized(deviceNetwork.pendingKeys) {
301 status = deviceNetwork.pendingKeys[(__bridge NSString *)keyName] != nil;
308 DSCloudrequestSyncWithPeers(SOSCloudTransportRef transport, CFArrayRef /* CFStringRef */ peers, CFArrayRef /* CFStringRef */ backupPeers, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
310 CFSetRef sPeers = CFSetCreateCopyOfArrayForCFTypes(peers);
311 CFSetRef sBackupPeers = CFSetCreateCopyOfArrayForCFTypes(backupPeers);
313 if (sPeers == NULL || sBackupPeers == NULL) {
314 CFReleaseNull(sPeers);
315 CFReleaseNull(sBackupPeers);
317 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
318 CFErrorRef error = NULL;
319 CFSetRef result = SOSCCProcessSyncWithPeers_Server(sPeers, sBackupPeers, &error);
322 CFRelease(sBackupPeers);
323 CFReleaseNull(result);
324 CFReleaseNull(error);
328 dispatch_async(processQueue, ^{
329 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
335 DSCloudhasPeerSyncPending(SOSCloudTransportRef transport, CFStringRef peerID, CFErrorRef* error)
341 DSCloudrequestEnsurePeerRegistration(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
343 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
344 CFErrorRef eprError = NULL;
345 if (!SOSCCProcessEnsurePeerRegistration_Server(&eprError)) {
346 secnotice("coder", "SOSCCProcessEnsurePeerRegistration failed with: %@", eprError);
348 CFReleaseNull(eprError);
350 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
354 static void DSCloudrequestPerfCounters(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
357 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
360 static void DSCloudflush(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
363 HANDLE_NO_NETWORK(replyBlock);
365 [deviceNetwork MDNCloudFlush:^(NSDictionary *returnedValues, NSError *error) {
366 dispatch_async(deviceNetwork.flushQueue, ^{
367 dispatch_async(processQueue, ^{
368 replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
375 static void DSCloudcounters(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
378 replyBlock((__bridge CFDictionaryRef)@{}, NULL);
381 @interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
384 @implementation ServiceDelegate
386 - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
387 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(DeviceSimulatorProtocol)];
389 DeviceSimulator *exportedObject = [DeviceSimulator new];
390 exportedObject.conn = newConnection;
391 newConnection.exportedObject = exportedObject;
393 [newConnection resume];
401 boot_securityd(NSXPCListenerEndpoint *network)
404 securityd_init((__bridge CFURLRef)[NSURL URLWithString:deviceHomeDir]);
406 NSXPCConnection *connection = [[NSXPCConnection alloc] initWithListenerEndpoint:network];
407 connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingProtocol)];
410 deviceNetwork = [[MDNetwork alloc] initWithConnection:connection];
415 * Make sure each of th peers don't trample on each's others state
418 @interface SOSCachedNotification (override)
421 @implementation SOSCachedNotification (override)
422 + (NSString *)swizzled_notificationName:(const char *)notificationName
424 return [NSString stringWithFormat:@"%@-%@",
425 [SOSCachedNotification swizzled_notificationName:notificationName], deviceInstance];
429 static dispatch_once_t onceToken;
430 dispatch_once(&onceToken, ^{
431 Method orignal = class_getClassMethod(self, @selector(notificationName:));
432 Method swizzled = class_getClassMethod(self, @selector(swizzled_notificationName:));
433 method_exchangeImplementations(orignal, swizzled);
439 int main(int argc, const char *argv[])
441 struct sigaction action;
442 memset(&action, 0, sizeof(action));
444 deviceInstance = [[NSXPCListener _UUID] UUIDString];
446 NSURL *tempPath = [[NSFileManager defaultManager] temporaryDirectory];
447 deviceHomeDir = [[tempPath path] stringByAppendingPathComponent:deviceInstance];
449 [[NSFileManager defaultManager] createDirectoryAtPath:deviceHomeDir
450 withIntermediateDirectories:NO
454 MDNTransport.put = DSCloudPut;
455 MDNTransport.updateKeys = DSCloudUpdateKeys;
456 MDNTransport.getDeviceID = DSCloudGetDeviceID;
457 MDNTransport.get = DSCloudGet;
458 MDNTransport.getAll = DSCloudGetAll;
459 MDNTransport.synchronize = DSCloudsynchronize;
460 MDNTransport.synchronizeAndWait = DSCloudsynchronizeAndWait;
461 MDNTransport.clearAll = DSCloudclearAll;
462 MDNTransport.removeObjectForKey = DSCloudRemoveObjectForKey;
463 MDNTransport.hasPendingKey = DSCloudhasPendingKey;
464 MDNTransport.requestSyncWithPeers = DSCloudrequestSyncWithPeers;
465 MDNTransport.hasPeerSyncPending = DSCloudhasPeerSyncPending;
466 MDNTransport.requestEnsurePeerRegistration = DSCloudrequestEnsurePeerRegistration;
467 MDNTransport.requestPerfCounters = DSCloudrequestPerfCounters;
468 MDNTransport.flush = DSCloudflush;
469 MDNTransport.itemsChangedBlock = CFBridgingRetain(^CFArrayRef(CFDictionaryRef values) {
470 // default change block doesn't handle messages, keep em
471 return CFBridgingRetain(@[]);
473 MDNTransport.removeKeys = DSCloudremoveKeys;
474 MDNTransport.counters = DSCloudcounters;
476 SOSCloudTransportSetDefaultTransport(&MDNTransport);
478 // Create the delegate for the service.
479 ServiceDelegate *delegate = [ServiceDelegate new];
480 signal(SIGPIPE, SIG_IGN);
482 // Set up the one NSXPCListener for this service. It will handle all incoming connections.
483 NSXPCListener *listener = [NSXPCListener serviceListener];
484 listener.delegate = delegate;
486 // Resuming the serviceListener starts this service. This method does not return.