2 #include "SOSAccountPriv.h"
3 #include "SOSAccount.h"
5 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
6 #include "keychain/SecureObjectSync/SOSTransportCircle.h"
7 #include "keychain/SecureObjectSync/SOSTransportCircleKVS.h"
8 #include "keychain/SecureObjectSync/SOSTransportMessage.h"
9 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
10 #include "keychain/SecureObjectSync/SOSTransport.h"
11 #include "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
12 #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
14 #import "keychain/SecureObjectSync/SOSAccountTrust.h"
15 #import "keychain/SecureObjectSync/SOSTransport.h"
16 #import "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
17 #include "keychain/SecureObjectSync/SOSTransportMessage.h"
18 #import "keychain/SecureObjectSync/SOSTransportMessageKVS.h"
19 #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
20 #include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
21 #include "keychain/SecureObjectSync/SOSInternal.h"
23 #include <CoreFoundation/CoreFoundation.h>
25 #include <utilities/SecCFError.h>
27 // MARK: Engine Logging
28 #define LOG_ENGINE_STATE_INTERVAL 20
29 #define LOG_CATEGORY "account-sync"
31 static bool SOSReadyToSync(SOSAccount *account, CFErrorRef *error) {
32 if (![account isInCircle:error]) {
33 secnotice(LOG_CATEGORY, "Not performing requested sync operation: not in circle yet");
39 void SOSAccountConsiderLoggingEngineState(SOSAccountTransaction* txn) {
40 static int engineLogCountDown = 0;
42 if(engineLogCountDown <= 0) {
43 SOSAccount* acct = txn.account;
44 if(SOSReadyToSync(acct, NULL)) {
45 CFTypeRef engine = [acct.kvs_message_transport SOSTransportMessageGetEngine];
46 SOSEngineLogState((SOSEngineRef)engine);
47 engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
54 bool SOSAccountInflateTransports(SOSAccount* account, CFStringRef circleName, CFErrorRef *error){
57 if(account.key_transport)
58 SOSUnregisterTransportKeyParameter(account.key_transport);
59 if(account.circle_transport)
60 SOSUnregisterTransportCircle(account.circle_transport);
61 if(account.kvs_message_transport)
62 SOSUnregisterTransportMessage((SOSMessage*)account.kvs_message_transport);
64 account.key_transport = [[CKKeyParameter alloc] initWithAccount:account];
65 account.circle_transport = [[SOSKVSCircleStorageTransport alloc]initWithAccount:account andCircleName:(__bridge NSString *)(circleName)];
67 require_quiet(account.key_transport, fail);
68 require_quiet(account.circle_transport, fail);
70 account.kvs_message_transport = [[SOSMessageKVS alloc] initWithAccount:account andName:(__bridge NSString*)circleName];
71 require_quiet(account.kvs_message_transport, fail);
83 static bool SOSAccountSyncWithKVSPeers(SOSAccountTransaction* txn, CFSetRef peerIDs, CFErrorRef *error) {
84 SOSAccount* account = txn.account;
85 CFErrorRef localError = NULL;
88 if(SOSReadyToSync(account, error)) {
89 result =[account.kvs_message_transport SOSTransportMessageSyncWithPeers:account.kvs_message_transport p:peerIDs err:&localError];
92 // Tell account to update SOSEngine with current trusted peers
93 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
94 secnotice(LOG_CATEGORY, "Arming account to update SOSEngine with current trusted peers");
95 account.engine_peer_state_needs_repair = true;
97 CFErrorPropagate(localError, error);
104 bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransaction* txn, CFStringRef peerid, CFDataRef message, CFErrorRef *error) {
106 SOSAccount* account = txn.account;
108 CFErrorRef localError = NULL;
109 CFDictionaryRef encapsulatedMessage = NULL;
111 secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerid);
112 secnotice(LOG_CATEGORY, "message: %@", message);
114 require_quiet(message, xit);
115 require_quiet(peerid && CFStringGetLength(peerid) <= kSOSPeerIDLengthMax, xit);
116 if(SOSReadyToSync(account, &localError)) {
117 encapsulatedMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerid, message, NULL);
118 result = [account.kvs_message_transport SOSTransportMessageSendMessages:account.kvs_message_transport pm:encapsulatedMessage err:&localError];
119 secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
120 SOSAccountConsiderLoggingEngineState(txn);
124 CFReleaseNull(encapsulatedMessage);
125 CFErrorPropagate(localError, error);
131 static bool SOSAccountSyncWithKVSPeer(SOSAccountTransaction* txn, CFStringRef peerID, CFErrorRef *error)
134 CFErrorRef localError = NULL;
135 SOSAccount* account = txn.account;
137 if(SOSReadyToSync(account, error)) {
138 secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerID);
139 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
140 CFSetAddValue(peerIDs, peerID);
141 result = SOSAccountSyncWithKVSPeers(txn, peerIDs, &localError);
142 secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
144 CFReleaseNull(peerIDs);
145 CFErrorPropagate(localError, error);
152 CFSetRef SOSAccountSyncWithPeersOverKVS(SOSAccountTransaction* txn, CFSetRef peers) {
153 CFMutableSetRef handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
154 if(SOSReadyToSync(txn.account, NULL)) {
155 CFSetForEach(peers, ^(const void *value) {
156 CFStringRef peerID = asString(value, NULL);
157 CFErrorRef localError = NULL;
158 if (peerID && SOSAccountSyncWithKVSPeer(txn, peerID, &localError)) {
159 CFSetAddValue(handled, peerID);
160 secnotice(LOG_CATEGORY, "synced with peer: %@", peerID);
162 secnotice(LOG_CATEGORY, "failed to sync with peer: %@ error: %@", peerID, localError);
164 CFReleaseNull(localError);
171 CF_RETURNS_RETAINED CFMutableSetRef SOSAccountSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peerIDs, CFErrorRef *error) {
172 CFMutableSetRef notMePeers = NULL;
173 CFMutableSetRef handledPeerIDs = NULL;
174 CFMutableSetRef peersForKVS = NULL;
176 SOSAccount* account = txn.account;
177 if(!SOSReadyToSync(account, error)) {
178 handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
179 CFReleaseNull(notMePeers);
180 CFReleaseNull(peersForKVS);
181 return handledPeerIDs;
184 handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
185 peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
187 SOSPeerInfoRef myPeerInfo = account.peerInfo;
190 CFReleaseNull(notMePeers);
191 CFReleaseNull(peersForKVS);
192 return handledPeerIDs;
195 CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
197 notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
198 CFSetRemoveValue(notMePeers, myPeerID);
200 CFSetForEach(notMePeers, ^(const void *value) {
201 CFErrorRef localError = NULL;
202 CFStringRef peerID = asString(value, &localError);
203 SOSPeerInfoRef peerInfo = NULL;
204 SOSCircleRef circle = NULL;
205 SOSAccountTrustClassic *trust = account.trust;
206 circle = trust.trustedCircle;
207 require_quiet(peerID, skip);
209 peerInfo = SOSCircleCopyPeerWithID(circle, peerID, NULL);
210 if (peerInfo && SOSCircleHasValidSyncingPeer(circle, peerInfo, account.accountKey, NULL)){
211 CFSetAddValue(peersForKVS, peerID);
213 CFSetAddValue(handledPeerIDs, peerID);
217 CFReleaseNull(peerInfo);
219 secnotice(LOG_CATEGORY, "Skipped peer ID: %@ due to %@", peerID, localError);
221 CFReleaseNull(localError);
224 CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
225 CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
226 CFReleaseNull(handledKVSPeerIDs);
228 SOSAccountConsiderLoggingEngineState(txn);
230 CFReleaseNull(notMePeers);
231 CFReleaseNull(peersForKVS);
232 return handledPeerIDs;
235 CF_RETURNS_RETAINED CFSetRef SOSAccountProcessSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peers, CFSetRef /* CFStringRef */ backupPeers, CFErrorRef *error)
237 CFErrorRef localError = NULL;
238 SOSAccount* account = txn.account;
240 if(!SOSReadyToSync(account, error)) {
241 return CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
244 CFMutableSetRef handled = SOSAccountSyncWithPeers(txn, peers, &localError);
247 secnotice(LOG_CATEGORY, "Peer Sync failed: %@", localError);
248 handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
250 CFReleaseNull(localError);
252 CFTypeRef engine = [account.kvs_message_transport SOSTransportMessageGetEngine];
253 CFSetRef engineHandled = SOSEngineSyncWithBackupPeers((SOSEngineRef)engine, backupPeers, false, error);
256 CFSetUnion(handled, engineHandled);
258 secnotice(LOG_CATEGORY, "Engine Backup Sync failed: %@", localError);
260 CFReleaseNull(localError);
261 CFReleaseNull(engineHandled);
266 CF_RETURNS_RETAINED CFSetRef SOSAccountCopyBackupPeersAndForceSync(SOSAccountTransaction* txn, CFErrorRef *error)
268 SOSAccount* account = txn.account;
270 if(!SOSReadyToSync(account, error)) {
274 SOSEngineRef engine = (SOSEngineRef) [txn.account.kvs_message_transport SOSTransportMessageGetEngine];
275 NSArray* backupPeersArray = (NSArray*) CFBridgingRelease(SOSEngineCopyBackupPeerNames(engine, error));
276 NSSet* backupPeers = [[NSSet alloc] initWithArray: backupPeersArray];
277 return SOSEngineSyncWithBackupPeers(engine, (__bridge CFSetRef) backupPeers, true, error);
280 bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransaction* txn, CFErrorRef *error)
282 SOSAccount* account = txn.account;
283 SOSAccountTrustClassic *trust = account.trust;
285 if(!SOSReadyToSync(account, error)) {
289 NSMutableSet<NSString*>* allSyncingPeers = [NSMutableSet set];
290 SOSCircleRef circle = trust.trustedCircle;
292 SOSCircleForEachValidSyncingPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
293 [allSyncingPeers addObject: (__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
296 [txn requestSyncWithPeers:allSyncingPeers];
302 // MARK: Syncing status functions
304 bool SOSAccountMessageFromPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
305 bool success = false;
306 SOSAccount* account = txn.account;
307 if(SOSReadyToSync(account, error)) {
308 // This translation belongs inside KVS..way down in CKD, but for now we reach over and do it here.
309 CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport([account kvs_message_transport], (__bridge CFStringRef)(account.peerID), SOSPeerInfoGetPeerID(peer));
310 success = SOSCloudKeychainHasPendingKey(peerMessage, error);
311 CFReleaseNull(peerMessage);
316 bool SOSAccountSendToPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
317 bool success = false;
318 SOSAccount* account = txn.account;
319 if(SOSReadyToSync(account, error)) {
320 success = SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer), error);