]> git.saurik.com Git - apple/security.git/blobdiff - MultiDeviceSimulator/DeviceSimulator/DeviceSimulatorMain.m
Security-58286.200.222.tar.gz
[apple/security.git] / MultiDeviceSimulator / DeviceSimulator / DeviceSimulatorMain.m
diff --git a/MultiDeviceSimulator/DeviceSimulator/DeviceSimulatorMain.m b/MultiDeviceSimulator/DeviceSimulator/DeviceSimulatorMain.m
new file mode 100644 (file)
index 0000000..c6e1ab7
--- /dev/null
@@ -0,0 +1,489 @@
+//
+//  main.m
+//  DeviceSimulator
+//
+//
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSXPCConnection_Private.h>
+#import <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
+#import <objc/runtime.h>
+#import <utilities/debugging.h>
+
+#import <securityd/SOSCloudCircleServer.h>
+#import <Security/SecureObjectSync/SOSPeerInfo.h>
+#import <Security/SecureObjectSync/SOSCloudCircleInternal.h>
+#import <Security/SecureObjectSync/SOSViews.h>
+#import <Security/SecureObjectSync/SOSInternal.h>
+
+#import "DeviceSimulator.h"
+#import "SOSCloudKeychainClient.h"
+#import "MultiDeviceNetworkingProtocol.h"
+#import "SecCFWrappers.h"
+#import "spi.h"
+
+struct SOSCloudTransport MDNTransport;
+
+@class MDNetwork;
+
+NSString *deviceInstance = NULL;
+static NSString *deviceHomeDir = NULL;
+static MDNetwork *deviceNetwork = NULL;
+
+@interface MDNetwork : NSObject<MultiDeviceNetworkingProtocol,MultiDeviceNetworkingCallbackProtocol,NSXPCListenerDelegate>
+@property NSXPCConnection *connection;
+@property NSXPCListener *callbackListener;
+@property dispatch_queue_t flushQueue;
+@property NSMutableDictionary *pendingKeys;
+@property NSSet *registeredKeys;
+- (instancetype)initWithConnection:(NSXPCConnection *)connection;
+@end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wprotocol"
+@implementation MDNetwork
+
+- (instancetype)initWithConnection:(NSXPCConnection *)connection
+{
+    self = [super init];
+    if (self) {
+        self.connection = connection;
+        self.callbackListener = [NSXPCListener anonymousListener];
+        self.callbackListener.delegate = self;
+        [self.callbackListener resume];
+
+        __typeof(self) weakSelf = self;
+        self.connection.invalidationHandler = ^{
+            __typeof(self) strongSelf = weakSelf;
+            [strongSelf.callbackListener invalidate];
+            strongSelf.callbackListener = nil;
+
+            exit(0);
+        };
+
+        self.flushQueue = dispatch_queue_create("MDNetwork.flushqueue", 0);
+        self.pendingKeys = [NSMutableDictionary dictionary];
+        self.registeredKeys = [NSSet set];
+
+        [[self.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
+            NSLog(@"network register callback failed with: %@", error);
+            //abort();
+        }] MDNRegisterCallback:[self.callbackListener endpoint] complete:^void(NSDictionary *values, NSError *error) {
+            ;
+        }];
+
+    }
+    return self;
+}
+
+- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
+{
+    newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingCallbackProtocol)];
+    newConnection.exportedObject = self;
+    [newConnection resume];
+    return YES;
+}
+
+- (void)MDNCItemsChanged:(NSDictionary *)values complete:(MDNComplete)complete
+{
+    NSMutableDictionary *requestedKeys = [NSMutableDictionary dictionary];
+    @synchronized(self.pendingKeys) {
+        secnotice("MDN", "items update: %@ (already pending: %@)", values, self.pendingKeys);
+
+        [self.pendingKeys addEntriesFromDictionary:values];
+        for (NSString *key in self.registeredKeys) {
+            id data = self.pendingKeys[key];
+            if (data) {
+                requestedKeys[key] = data;
+                self.pendingKeys[key] = nil;
+            }
+        }
+    }
+    if (requestedKeys.count) {
+        dispatch_async(self.flushQueue, ^{
+            secnotice("MDN", "engine processing keys: %@", requestedKeys);
+            NSArray *handled = CFBridgingRelease(SOSCCHandleUpdateMessage((__bridge CFDictionaryRef)requestedKeys));
+            /*
+             * Ok, our dear Engine might not have handled all messages.
+             * So put them back unless there are new messages around that
+             * have overwritten the previous message.
+             */
+            for (NSString *key in handled) {
+                requestedKeys[key] = NULL;
+            }
+            if (requestedKeys.count) {
+                @synchronized(self.pendingKeys) {
+                    for (NSString *key in requestedKeys) {
+                        if (self.pendingKeys[key] == nil) {
+                            self.pendingKeys[key] = requestedKeys[key];
+                        }
+                    }
+                }
+            }
+        });
+    }
+    complete(NULL, NULL);
+}
+
+/* Oh, ObjC, you are my friend */
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+    struct objc_method_description desc = protocol_getMethodDescription(@protocol(MultiDeviceNetworkingProtocol), [invocation selector], true, true);
+    if (desc.name == NULL) {
+        [super forwardInvocation:invocation];
+    } else {
+        __block bool gogogo = true;
+        id object = [self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
+            gogogo = false;
+            NSLog(@"network failed with: %@", error);
+            //abort();
+        }];
+        if(gogogo) [invocation invokeWithTarget:object];
+    }
+}
+@end
+#pragma clang diagnostic pop
+
+#define HANDLE_NO_NETWORK(_replyBlock) \
+    if (deviceNetwork == NULL) { \
+        replyBlock((__bridge CFDictionaryRef)@{}, (__bridge CFErrorRef)[NSError errorWithDomain:@"MDNNetwork" code:1 userInfo:NULL]); \
+        return; \
+    }
+
+
+static void
+DSCloudPut(SOSCloudTransportRef transport, CFDictionaryRef valuesToPut, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+        secnotice("MDN", "CloudPut: %@", valuesToPut);
+
+        [deviceNetwork MDNCloudPut:(__bridge NSDictionary *)valuesToPut complete:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(processQueue, ^{
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+            });
+        }];
+    }
+}
+
+static NSString *nKeyAlwaysKeys = @"AlwaysKeys";
+static NSString *nKeyFirstUnlockKeys = @"FirstUnlockKeys";
+static NSString *nKeyUnlockedKeys = @"UnlockedKeys";
+static NSString *nMessageKeyParameter = @"KeyParameter";
+static NSString *nMessageCircle = @"Circle";
+static NSString *nMessageMessage = @"Message";
+
+
+static void
+DSCloudUpdateKeys(SOSCloudTransportRef transport, CFDictionaryRef cfkeys, CFStringRef accountUUID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    /*
+     * Currently doesn't deal with lock state, just smash (HULK!) them all together
+     */
+    @autoreleasepool {
+        NSDictionary *keys = (__bridge NSDictionary *)cfkeys;
+        NSMutableSet *newSet = [NSMutableSet set];
+
+        @synchronized(deviceNetwork.pendingKeys) {
+            for (NSString *type in @[ nMessageKeyParameter, nMessageCircle, nMessageMessage]) {
+                NSDictionary *typeDict = keys[type];
+
+                for (NSString *lockType in @[ nKeyAlwaysKeys, nKeyFirstUnlockKeys, nKeyUnlockedKeys]) {
+                    NSArray *lockArray = typeDict[lockType];
+                    if (lockArray) {
+                        [newSet unionSet:[NSMutableSet setWithArray:lockArray]];
+                    }
+                }
+            }
+            deviceNetwork.registeredKeys = newSet;
+        }
+        /* update engine with stuff */
+        [deviceNetwork MDNCItemsChanged:@{} complete:^(NSDictionary *returnedValues, NSError *error) {
+            if (replyBlock)
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+        }];
+    }
+}
+
+static void
+DSCloudGetDeviceID(SOSCloudTransportRef transport, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+// Debug calls
+static void
+DSCloudGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+static void
+DSCloudGetAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+static void
+DSCloudsynchronize(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+static void
+DSCloudsynchronizeAndWait(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+
+        [deviceNetwork MDNCloudsynchronizeAndWait:@{} complete:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(processQueue, ^{
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+            });
+        }];
+    }
+}
+
+static void
+DSCloudRemoveObjectForKey(SOSCloudTransportRef transport, CFStringRef keyToRemove, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (keyToRemove == NULL) {
+        dispatch_async(processQueue, ^{
+            replyBlock(NULL, NULL);
+        });
+        return;
+    }
+
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+        [deviceNetwork MDNCloudRemoveKeys:@[(__bridge NSString *)keyToRemove] complete:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(processQueue, ^{
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+            });
+        }];
+    }
+}
+
+static void DSCloudremoveKeys(SOSCloudTransportRef transport, CFArrayRef keys, CFStringRef accountUUID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+        [deviceNetwork MDNCloudRemoveKeys:(__bridge NSArray *)keys complete:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(processQueue, ^{
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+            });
+        }];
+    }
+}
+
+static void
+DSCloudclearAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+        [deviceNetwork MDNCloudRemoveKeys:NULL complete:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(processQueue, ^{
+                replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+            });
+        }];
+    }
+}
+
+static bool
+DSCloudhasPendingKey(SOSCloudTransportRef transport, CFStringRef keyName, CFErrorRef* error)
+{
+    bool status = false;
+    @synchronized(deviceNetwork.pendingKeys) {
+        status = deviceNetwork.pendingKeys[(__bridge NSString *)keyName] != nil;
+    }
+    return status;
+}
+
+
+static void
+DSCloudrequestSyncWithPeers(SOSCloudTransportRef transport, CFArrayRef /* CFStringRef */ peers, CFArrayRef /* CFStringRef */ backupPeers, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    CFSetRef sPeers = CFSetCreateCopyOfArrayForCFTypes(peers);
+    CFSetRef sBackupPeers = CFSetCreateCopyOfArrayForCFTypes(backupPeers);
+
+    if (sPeers == NULL || sBackupPeers == NULL) {
+        CFReleaseNull(sPeers);
+        CFReleaseNull(sBackupPeers);
+    } else {
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            CFErrorRef error = NULL;
+            CFSetRef result = SOSCCProcessSyncWithPeers_Server(sPeers, sBackupPeers, &error);
+
+            CFRelease(sPeers);
+            CFRelease(sBackupPeers);
+            CFReleaseNull(result);
+            CFReleaseNull(error);
+        });
+    }
+    if (replyBlock) {
+        dispatch_async(processQueue, ^{
+            replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+        });
+    }
+}
+
+static bool
+DSCloudhasPeerSyncPending(SOSCloudTransportRef transport, CFStringRef peerID, CFErrorRef* error)
+{
+    return false;
+}
+
+static void
+DSCloudrequestEnsurePeerRegistration(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        CFErrorRef eprError = NULL;
+        if (!SOSCCProcessEnsurePeerRegistration_Server(&eprError)) {
+            secnotice("coder", "SOSCCProcessEnsurePeerRegistration failed with: %@", eprError);
+        }
+        CFReleaseNull(eprError);
+        if (replyBlock)
+            replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+    });
+}
+
+static void DSCloudrequestPerfCounters(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+static void DSCloudflush(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    @autoreleasepool {
+        HANDLE_NO_NETWORK(replyBlock);
+
+        [deviceNetwork MDNCloudFlush:^(NSDictionary *returnedValues, NSError *error) {
+            dispatch_async(deviceNetwork.flushQueue, ^{
+                dispatch_async(processQueue, ^{
+                    replyBlock((__bridge CFDictionaryRef)returnedValues, (__bridge CFErrorRef)error);
+                });
+            });
+        }];
+    }
+}
+
+static void DSCloudcounters(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    if (replyBlock)
+        replyBlock((__bridge CFDictionaryRef)@{}, NULL);
+}
+
+@interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
+@end
+
+@implementation ServiceDelegate
+
+- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
+    newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(DeviceSimulatorProtocol)];
+    
+    DeviceSimulator *exportedObject = [DeviceSimulator new];
+    exportedObject.conn  = newConnection;
+    newConnection.exportedObject = exportedObject;
+    
+    [newConnection resume];
+    
+    return YES;
+}
+
+@end
+
+void
+boot_securityd(NSXPCListenerEndpoint *network)
+{
+    secLogDisable();
+    securityd_init((__bridge CFURLRef)[NSURL URLWithString:deviceHomeDir]);
+
+    NSXPCConnection *connection = [[NSXPCConnection alloc] initWithListenerEndpoint:network];
+    connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingProtocol)];
+    [connection resume];
+
+    deviceNetwork = [[MDNetwork alloc] initWithConnection:connection];
+
+}
+
+/*
+ * Make sure each of th peers don't trample on each's others state
+ */
+
+@interface SOSCachedNotification (override)
+@end
+
+@implementation SOSCachedNotification (override)
++ (NSString *)swizzled_notificationName:(const char *)notificationName
+{
+    return [NSString stringWithFormat:@"%@-%@",
+            [SOSCachedNotification swizzled_notificationName:notificationName], deviceInstance];
+}
+
++ (void)load {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        Method orignal = class_getClassMethod(self, @selector(notificationName:));
+        Method swizzled = class_getClassMethod(self, @selector(swizzled_notificationName:));
+        method_exchangeImplementations(orignal, swizzled);
+    });
+}
+@end
+
+
+int main(int argc, const char *argv[])
+{
+    struct sigaction action;
+    memset(&action, 0, sizeof(action));
+    
+    deviceInstance = [[NSXPCListener _UUID] UUIDString];
+
+    NSURL *tempPath = [[NSFileManager defaultManager] temporaryDirectory];
+    deviceHomeDir = [[tempPath path] stringByAppendingPathComponent:deviceInstance];
+
+    [[NSFileManager defaultManager] createDirectoryAtPath:deviceHomeDir
+                              withIntermediateDirectories:NO
+                                               attributes:NULL
+                                                    error:NULL];
+
+    MDNTransport.put = DSCloudPut;
+    MDNTransport.updateKeys = DSCloudUpdateKeys;
+    MDNTransport.getDeviceID = DSCloudGetDeviceID;
+    MDNTransport.get = DSCloudGet;
+    MDNTransport.getAll = DSCloudGetAll;
+    MDNTransport.synchronize = DSCloudsynchronize;
+    MDNTransport.synchronizeAndWait = DSCloudsynchronizeAndWait;
+    MDNTransport.clearAll = DSCloudclearAll;
+    MDNTransport.removeObjectForKey = DSCloudRemoveObjectForKey;
+    MDNTransport.hasPendingKey = DSCloudhasPendingKey;
+    MDNTransport.requestSyncWithPeers = DSCloudrequestSyncWithPeers;
+    MDNTransport.hasPeerSyncPending = DSCloudhasPeerSyncPending;
+    MDNTransport.requestEnsurePeerRegistration = DSCloudrequestEnsurePeerRegistration;
+    MDNTransport.requestPerfCounters = DSCloudrequestPerfCounters;
+    MDNTransport.flush = DSCloudflush;
+    MDNTransport.itemsChangedBlock = CFBridgingRetain(^CFArrayRef(CFDictionaryRef values) {
+        // default change block doesn't handle messages, keep em
+        return CFBridgingRetain(@[]);
+    });
+    MDNTransport.removeKeys = DSCloudremoveKeys;
+    MDNTransport.counters = DSCloudcounters;
+
+    SOSCloudTransportSetDefaultTransport(&MDNTransport);
+
+    // Create the delegate for the service.
+    ServiceDelegate *delegate = [ServiceDelegate new];
+    signal(SIGPIPE, SIG_IGN);
+
+    // Set up the one NSXPCListener for this service. It will handle all incoming connections.
+    NSXPCListener *listener = [NSXPCListener serviceListener];
+    listener.delegate = delegate;
+    
+    // Resuming the serviceListener starts this service. This method does not return.
+    [listener resume];
+    return 0;
+}