]> git.saurik.com Git - apple/security.git/blobdiff - keychain/SecureObjectSync/SOSAccountSync.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSAccountSync.m
diff --git a/keychain/SecureObjectSync/SOSAccountSync.m b/keychain/SecureObjectSync/SOSAccountSync.m
new file mode 100644 (file)
index 0000000..2ee32bf
--- /dev/null
@@ -0,0 +1,324 @@
+
+#include "SOSAccountPriv.h"
+#include "SOSAccount.h"
+
+#include "keychain/SecureObjectSync/SOSKVSKeys.h"
+#include "keychain/SecureObjectSync/SOSTransportCircle.h"
+#include "keychain/SecureObjectSync/SOSTransportCircleKVS.h"
+#include "keychain/SecureObjectSync/SOSTransportMessage.h"
+#include "keychain/SecureObjectSync/SOSKVSKeys.h"
+#include "keychain/SecureObjectSync/SOSTransport.h"
+#include "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
+#include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
+
+#import "keychain/SecureObjectSync/SOSAccountTrust.h"
+#import "keychain/SecureObjectSync/SOSTransport.h"
+#import "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
+#include "keychain/SecureObjectSync/SOSTransportMessage.h"
+#import "keychain/SecureObjectSync/SOSTransportMessageKVS.h"
+#include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
+#include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
+#include "keychain/SecureObjectSync/SOSInternal.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <utilities/SecCFError.h>
+
+// MARK: Engine Logging
+#define LOG_ENGINE_STATE_INTERVAL 20
+#define LOG_CATEGORY "account-sync"
+
+static bool SOSReadyToSync(SOSAccount *account, CFErrorRef *error) {
+    if (![account isInCircle:error]) {
+        secnotice(LOG_CATEGORY, "Not performing requested sync operation: not in circle yet");
+        return false;
+    }
+    return true;
+}
+
+void SOSAccountConsiderLoggingEngineState(SOSAccountTransaction* txn) {
+    static int engineLogCountDown = 0;
+
+    if(engineLogCountDown <= 0) {
+        SOSAccount* acct = txn.account;
+        if(SOSReadyToSync(acct, NULL)) {
+            CFTypeRef engine = [acct.kvs_message_transport SOSTransportMessageGetEngine];
+            SOSEngineLogState((SOSEngineRef)engine);
+            engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
+        }
+    } else {
+        engineLogCountDown--;
+    }
+}
+
+bool SOSAccountInflateTransports(SOSAccount* account, CFStringRef circleName, CFErrorRef *error){
+    bool success = false;
+    
+    if(account.key_transport)
+        SOSUnregisterTransportKeyParameter(account.key_transport);
+    if(account.circle_transport)
+        SOSUnregisterTransportCircle(account.circle_transport);
+    if(account.kvs_message_transport)
+        SOSUnregisterTransportMessage((SOSMessage*)account.kvs_message_transport);
+    
+    account.key_transport = [[CKKeyParameter alloc] initWithAccount:account];
+    account.circle_transport = [[SOSKVSCircleStorageTransport alloc]initWithAccount:account andCircleName:(__bridge NSString *)(circleName)];
+    
+    require_quiet(account.key_transport, fail);
+    require_quiet(account.circle_transport, fail);
+        
+    account.kvs_message_transport = [[SOSMessageKVS alloc] initWithAccount:account andName:(__bridge NSString*)circleName];
+    require_quiet(account.kvs_message_transport, fail);
+    
+    success = true;
+    
+fail:
+    return success;
+}
+
+//
+// MARK: KVS Syncing
+//
+
+static bool SOSAccountSyncWithKVSPeers(SOSAccountTransaction* txn, CFSetRef peerIDs, CFErrorRef *error) {
+    SOSAccount* account = txn.account;
+    CFErrorRef localError = NULL;
+    bool result = false;
+
+    if(SOSReadyToSync(account, error)) {
+        result =[account.kvs_message_transport SOSTransportMessageSyncWithPeers:account.kvs_message_transport p:peerIDs err:&localError];
+    }
+    if (!result) {
+        // Tell account to update SOSEngine with current trusted peers
+        if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
+            secnotice(LOG_CATEGORY, "Arming account to update SOSEngine with current trusted peers");
+            account.engine_peer_state_needs_repair = true;
+        }
+        CFErrorPropagate(localError, error);
+        localError = NULL;
+    }
+    return result;
+    
+}
+
+bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransaction* txn, CFStringRef peerid, CFDataRef message, CFErrorRef *error) {
+    //murfxx protect
+    SOSAccount* account = txn.account;
+    bool result = false;
+    CFErrorRef localError = NULL;
+    CFDictionaryRef encapsulatedMessage = NULL;
+
+    secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerid);
+    secnotice(LOG_CATEGORY, "message: %@", message);
+
+    require_quiet(message, xit);
+    require_quiet(peerid && CFStringGetLength(peerid) <= kSOSPeerIDLengthMax, xit);
+    if(SOSReadyToSync(account, &localError)) {
+        encapsulatedMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerid, message, NULL);
+        result = [account.kvs_message_transport SOSTransportMessageSendMessages:account.kvs_message_transport pm:encapsulatedMessage err:&localError];
+        secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+        SOSAccountConsiderLoggingEngineState(txn);
+    }
+
+xit:
+    CFReleaseNull(encapsulatedMessage);
+    CFErrorPropagate(localError, error);
+    
+    return result;
+}
+
+
+static bool SOSAccountSyncWithKVSPeer(SOSAccountTransaction* txn, CFStringRef peerID, CFErrorRef *error)
+{
+    bool result = false;
+    CFErrorRef localError = NULL;
+    SOSAccount* account = txn.account;
+
+    if(SOSReadyToSync(account, error)) {
+        secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerID);
+        CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFSetAddValue(peerIDs, peerID);
+        result = SOSAccountSyncWithKVSPeers(txn, peerIDs, &localError);
+        secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+
+        CFReleaseNull(peerIDs);
+        CFErrorPropagate(localError, error);
+    }
+
+    return result;
+}
+
+
+CFSetRef SOSAccountSyncWithPeersOverKVS(SOSAccountTransaction* txn, CFSetRef peers) {
+    CFMutableSetRef handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    if(SOSReadyToSync(txn.account, NULL)) {
+        CFSetForEach(peers, ^(const void *value) {
+            CFStringRef peerID = asString(value, NULL);
+            CFErrorRef localError = NULL;
+            if (peerID && SOSAccountSyncWithKVSPeer(txn, peerID, &localError)) {
+                CFSetAddValue(handled, peerID);
+                secnotice(LOG_CATEGORY, "synced with peer: %@", peerID);
+            } else {
+                secnotice(LOG_CATEGORY, "failed to sync with peer: %@ error: %@", peerID, localError);
+            }
+            CFReleaseNull(localError);
+        });
+    }
+
+    return handled;
+}
+
+CF_RETURNS_RETAINED CFMutableSetRef SOSAccountSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peerIDs, CFErrorRef *error) {
+    CFMutableSetRef notMePeers = NULL;
+    CFMutableSetRef handledPeerIDs = NULL;
+    CFMutableSetRef peersForKVS = NULL;
+
+    SOSAccount* account = txn.account;
+    if(!SOSReadyToSync(account, error)) {
+        handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
+        CFReleaseNull(notMePeers);
+        CFReleaseNull(peersForKVS);
+        return handledPeerIDs;
+    }
+
+    handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+
+    SOSPeerInfoRef myPeerInfo = account.peerInfo;
+    if(!myPeerInfo)
+    {
+        CFReleaseNull(notMePeers);
+        CFReleaseNull(peersForKVS);
+        return handledPeerIDs;
+
+    }
+    CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
+
+    notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
+    CFSetRemoveValue(notMePeers, myPeerID);
+    
+    CFSetForEach(notMePeers, ^(const void *value) {
+        CFErrorRef localError = NULL;
+        CFStringRef peerID = asString(value, &localError);
+        SOSPeerInfoRef peerInfo = NULL;
+        SOSCircleRef circle = NULL;
+        SOSAccountTrustClassic *trust = account.trust;
+        circle = trust.trustedCircle;
+        require_quiet(peerID, skip);
+
+        peerInfo = SOSCircleCopyPeerWithID(circle, peerID, NULL);
+        if (peerInfo && SOSCircleHasValidSyncingPeer(circle, peerInfo, account.accountKey, NULL)){
+            CFSetAddValue(peersForKVS, peerID);
+        } else {
+            CFSetAddValue(handledPeerIDs, peerID);
+        }
+
+    skip:
+        CFReleaseNull(peerInfo);
+        if (localError) {
+            secnotice(LOG_CATEGORY, "Skipped peer ID: %@ due to %@", peerID, localError);
+        }
+        CFReleaseNull(localError);
+    });
+
+    CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
+    CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
+    CFReleaseNull(handledKVSPeerIDs);
+
+    SOSAccountConsiderLoggingEngineState(txn);
+
+    CFReleaseNull(notMePeers);
+    CFReleaseNull(peersForKVS);
+    return handledPeerIDs;
+}
+
+CF_RETURNS_RETAINED CFSetRef SOSAccountProcessSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peers, CFSetRef /* CFStringRef */ backupPeers, CFErrorRef *error)
+{
+    CFErrorRef localError = NULL;
+    SOSAccount* account = txn.account;
+
+    if(!SOSReadyToSync(account, error)) {
+        return CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    }
+
+    CFMutableSetRef handled = SOSAccountSyncWithPeers(txn, peers, &localError);
+
+    if (!handled) {
+        secnotice(LOG_CATEGORY, "Peer Sync failed: %@", localError);
+        handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    }
+    CFReleaseNull(localError);
+    
+    CFTypeRef engine = [account.kvs_message_transport SOSTransportMessageGetEngine];
+    CFSetRef engineHandled = SOSEngineSyncWithBackupPeers((SOSEngineRef)engine, backupPeers, false, error);
+    
+    if (engineHandled) {
+        CFSetUnion(handled, engineHandled);
+    } else {
+        secnotice(LOG_CATEGORY, "Engine Backup Sync failed: %@", localError);
+    }
+    CFReleaseNull(localError);
+    CFReleaseNull(engineHandled);
+
+    return handled;
+}
+
+CF_RETURNS_RETAINED CFSetRef SOSAccountCopyBackupPeersAndForceSync(SOSAccountTransaction* txn, CFErrorRef *error)
+{
+    SOSAccount* account = txn.account;
+
+    if(!SOSReadyToSync(account, error)) {
+        return false;
+    }
+
+    SOSEngineRef engine = (SOSEngineRef) [txn.account.kvs_message_transport SOSTransportMessageGetEngine];
+    NSArray* backupPeersArray = (NSArray*) CFBridgingRelease(SOSEngineCopyBackupPeerNames(engine, error));
+    NSSet* backupPeers = [[NSSet alloc] initWithArray: backupPeersArray];
+    return SOSEngineSyncWithBackupPeers(engine, (__bridge CFSetRef) backupPeers, true, error);
+}
+
+bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransaction* txn, CFErrorRef *error)
+{
+    SOSAccount* account = txn.account;
+    SOSAccountTrustClassic *trust = account.trust;
+
+    if(!SOSReadyToSync(account, error)) {
+        return false;
+    }
+
+    NSMutableSet<NSString*>* allSyncingPeers = [NSMutableSet set];
+    SOSCircleRef circle = trust.trustedCircle;
+
+    SOSCircleForEachValidSyncingPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
+        [allSyncingPeers addObject: (__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
+    });
+
+    [txn requestSyncWithPeers:allSyncingPeers];
+
+    return true;
+}
+
+//
+// MARK: Syncing status functions
+//
+bool SOSAccountMessageFromPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
+    bool success = false;
+    SOSAccount* account = txn.account;
+    if(SOSReadyToSync(account, error)) {
+        // This translation belongs inside KVS..way down in CKD, but for now we reach over and do it here.
+        CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport([account kvs_message_transport], (__bridge CFStringRef)(account.peerID), SOSPeerInfoGetPeerID(peer));
+        success = SOSCloudKeychainHasPendingKey(peerMessage, error);
+        CFReleaseNull(peerMessage);
+    }
+    return success;
+}
+
+bool SOSAccountSendToPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
+    bool success = false;
+    SOSAccount* account = txn.account;
+    if(SOSReadyToSync(account, error)) {
+        success = SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer), error);
+    }
+    return success;
+}
+