]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.c
CommitLineData
5c19dc3a
A
1/*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5/*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10#include "SOSAccountPriv.h"
11#include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
12#include <Security/SecureObjectSync/SOSTransportCircle.h>
13#include <Security/SecureObjectSync/SOSTransportMessage.h>
14#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
15#include <Security/SecureObjectSync/SOSKVSKeys.h>
16#include <Security/SecureObjectSync/SOSTransport.h>
17#include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
18#include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
19#include <Security/SecureObjectSync/SOSEngine.h>
20#include <Security/SecureObjectSync/SOSPeerCoder.h>
21#include <Security/SecureObjectSync/SOSInternal.h>
22#include <Security/SecureObjectSync/SOSRing.h>
23#include <Security/SecureObjectSync/SOSRingUtils.h>
24#include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
25#include <Security/SecureObjectSync/SOSPeerInfoV2.h>
fa7225c8 26#include <Security/SecureObjectSync/SOSAccountTransaction.h>
5c19dc3a
A
27#include <Security/SecItemInternal.h>
28#include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
29#include <SOSCircle/Regressions/SOSRegressionUtilities.h>
30
fa7225c8
A
31#include <utilities/SecCFWrappers.h>
32
5c19dc3a
A
33CFGiblisWithCompareFor(SOSAccount);
34
35const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
fa7225c8 36const CFStringRef SOSTransportMessageTypeIDSV2 = CFSTR("IDS2.0");
5c19dc3a
A
37const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
38const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
e0e0d90e 39const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
5c19dc3a 40const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
fa7225c8
A
41const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
42const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
43
5c19dc3a 44
5c19dc3a
A
45#define DATE_LENGTH 25
46const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
47
5c19dc3a
A
48bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
49{
50 bool result = false;
51 CFStringRef circle_name = NULL;
52
53 require_quiet(a, xit);
54 require_quiet(a->factory, xit);
55
56 circle_name = SOSDataSourceFactoryCopyName(a->factory);
57 require(circle_name, xit);
58
59 SOSAccountEnsureCircle(a, circle_name, NULL);
60
61 result = true;
62
63xit:
64 // We don't own name, so don't release it.
65 CFReleaseNull(circle_name);
66 return result;
67}
68
69
70SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
71 CFDictionaryRef gestalt,
72 SOSDataSourceFactoryRef factory) {
73 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
74
75 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
76
5c19dc3a
A
77 a->gestalt = CFRetainSafe(gestalt);
78
79 a->trusted_circle = NULL;
5c19dc3a
A
80 a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
81 a->my_identity = NULL;
82 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
83
84 a->factory = factory; // We adopt the factory. kthanksbai.
fa7225c8
A
85
86 a->isListeningForSync = false;
5c19dc3a
A
87
88 a->_user_private = NULL;
89 a->_password_tmp = NULL;
90 a->user_private_timer = NULL;
fa7225c8 91 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
5c19dc3a
A
92
93 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
fa7225c8 94 a->waitForInitialSync_blocks = NULL;
5c19dc3a
A
95 a->departure_code = kSOSNeverAppliedToCircle;
96
97 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
98 a->circle_transport = NULL;
99 a->kvs_message_transport = NULL;
100 a->ids_message_transport = NULL;
101 a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
fa7225c8
A
102 SOSAccountAddRingDictionary(a);
103
104 a->saveBlock = NULL;
105 a->circle_rings_retirements_need_attention = false;
106 a->engine_peer_state_needs_repair = false;
107 a->key_interests_need_updating = false;
108 a->deviceID = NULL;
5c19dc3a
A
109
110 return a;
111}
112
fa7225c8
A
113//
114// MARK: Transactional
115//
116
117void SOSAccountWithTransaction_Locked(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
118 SOSAccountTransactionRef at = SOSAccountTransactionCreate(account);
119 action(account, at);
120 SOSAccountTransactionFinish(at);
121 CFReleaseNull(at);
122}
123
124
125
126void SOSAccountWithTransaction(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
127 dispatch_block_t with_transaction = ^{
128 SOSAccountWithTransaction_Locked(account, action);
129 };
130
131 if (sync) {
132 dispatch_sync(SOSAccountGetQueue(account), with_transaction);
133 } else {
134 dispatch_async(SOSAccountGetQueue(account), with_transaction);
135 }
136}
137
138void SOSAccountWithTransactionSync(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
139 SOSAccountWithTransaction(account, true, action);
140}
141
142void SOSAccountWithTransactionAsync(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
143 SOSAccountWithTransaction(account, false, action);
144}
145
146//
147// MARK: Save Block
148//
149
150void SOSAccountSetSaveBlock(SOSAccountRef account, SOSAccountSaveBlock saveBlock) {
151 CFAssignRetained(account->saveBlock, Block_copy(saveBlock));
152}
153
154void SOSAccountFlattenToSaveBlock(SOSAccountRef account) {
155 if (account->saveBlock) {
156 CFErrorRef localError = NULL;
157 CFDataRef saveData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &localError);
158
159 (account->saveBlock)(saveData, localError);
160
161 CFReleaseNull(saveData);
162 CFReleaseNull(localError);
163 }
164}
165
166//
167// MARK: Security Properties
168//
169
5c19dc3a
A
170SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
171 SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
172 bool updateCircle = false;
173 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
174 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
175 retval = SOSFullPeerInfoUpdateSecurityProperty(account->my_identity, actionCode, property, error);
176
177 if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
178 updateCircle = true;
179 } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
180 updateCircle = true;
181 } else if(actionCode == kSOSCCSecurityPropertyPending) {
182 updateCircle = true;
183 }
184
185 if (updateCircle) {
186 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
187 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
188 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
189 });
190 }
191
192errOut:
193 return retval;
194}
195
196SOSSecurityPropertyResultCode SOSAccountSecurityPropertyStatus(SOSAccountRef account, CFStringRef property, CFErrorRef *error) {
197 SOSSecurityPropertyResultCode retval = kSOSCCGeneralViewError;
198 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
199 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
200 retval = SOSFullPeerInfoSecurityPropertyStatus(account->my_identity, property, error);
201errOut:
202 return retval;
203}
204
205bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
206{
207 if (CFEqualSafe(new_gestalt, account->gestalt))
208 return false;
209
210 if (account->trusted_circle && account->my_identity
211 && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
212 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
fa7225c8 213 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
5c19dc3a
A
214 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
215 });
216 }
217
218 CFRetainAssign(account->gestalt, new_gestalt);
219 return true;
220}
221
fa7225c8 222static bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
5c19dc3a
A
223 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
224 //send new DSID over account changed
225 SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
5c19dc3a
A
226 return true;
227}
228
fa7225c8
A
229void SOSAccountAssertDSID(SOSAccountRef account, CFStringRef dsid) {
230 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
231 if(accountDSID == NULL) {
232 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
233
234 SOSAccountUpdateDSID(account, dsid);
235 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
236 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
237
238 //DSID has changed, blast the account!
239 SOSAccountSetToNew(account);
240
241 //update DSID to the new DSID
242 SOSAccountUpdateDSID(account, dsid);
243 } else {
244 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
245 }
246}
247
822b670c 248bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews, CFSetRef excludedViews) {
5c19dc3a 249 if (account->trusted_circle && account->my_identity) {
822b670c 250 if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
5c19dc3a
A
251 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
252 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
253 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
254 });
255 }
256 }
257
258 return true;
259}
260
fa7225c8
A
261static bool SOSAccountValueSetContainsValue(SOSAccountRef account, CFStringRef key, CFTypeRef value) {
262 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
263 return foundSet && CFSetContainsValue(foundSet, value);
264}
265
266static void SOSAccountValueUnionWith(SOSAccountRef account, CFStringRef key, CFSetRef valuesToUnion) {
267 CFMutableSetRef unionedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, valuesToUnion);
268 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
269 if (foundSet) {
270 CFSetUnion(unionedSet, foundSet);
271 }
272 SOSAccountSetValue(account, key, unionedSet, NULL);
273 CFReleaseNull(unionedSet);
274}
275
276static void SOSAccountValueSubtractFrom(SOSAccountRef account, CFStringRef key, CFSetRef valuesToSubtract) {
277 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
278 if (foundSet) {
279 CFMutableSetRef subtractedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, foundSet);
280 CFSetSubtract(subtractedSet, valuesToSubtract);
281 SOSAccountSetValue(account, key, subtractedSet, NULL);
282 CFReleaseNull(subtractedSet);
283 }
284}
285
286void SOSAccountPendEnableViewSet(SOSAccountRef account, CFSetRef enabledViews)
287{
288 if(CFSetGetValue(enabledViews, kSOSViewKeychainV0) != NULL) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
289
290 SOSAccountValueUnionWith(account, kSOSPendingEnableViewsToBeSetKey, enabledViews);
291 SOSAccountValueSubtractFrom(account, kSOSPendingDisableViewsToBeSetKey, enabledViews);
292}
293
294
295void SOSAccountPendDisableViewSet(SOSAccountRef account, CFSetRef disabledViews)
296{
297 SOSAccountValueUnionWith(account, kSOSPendingDisableViewsToBeSetKey, disabledViews);
298 SOSAccountValueSubtractFrom(account, kSOSPendingEnableViewsToBeSetKey, disabledViews);
299}
300
301static SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccountRef account, SOSViewActionCode actionCode) {
302 SOSViewResultCode retval = kSOSCCGeneralViewError;
303 // The V0 view switches on and off all on it's own, we allow people the delusion
304 // of control and status if it's what we're stuck at., otherwise error.
305 if (SOSAccountSyncingV0(account)) {
306 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
307 retval = kSOSCCViewMember;
308 } else {
309 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
310 retval = kSOSCCViewNotMember;
311 }
312errOut:
313 return retval;
314}
315
316
5c19dc3a
A
317SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
318 SOSViewResultCode retval = kSOSCCGeneralViewError;
319 SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
fa7225c8
A
320 bool alreadyInSync = SOSAccountHasCompletedInitialSync(account);
321
5c19dc3a
A
322 bool updateCircle = false;
323 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
324 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
325 require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
326 currentStatus = SOSAccountViewStatus(account, viewname, error);
327 require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
328
329 if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
fa7225c8 330 retval = SOSAccountVirtualV0Behavior(account, actionCode);
5c19dc3a
A
331 } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
332 // Subviews of V0 syncing can't be turned off if V0 is on.
333 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
334 retval = kSOSCCViewMember;
335 } else {
fa7225c8
A
336 CFMutableSetRef pendingSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
337 CFSetAddValue(pendingSet, viewname);
338
5c19dc3a 339 if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
fa7225c8
A
340 if(alreadyInSync) {
341 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
342 if(retval == kSOSCCViewMember) updateCircle = true;
343 } else {
344 SOSAccountPendEnableViewSet(account, pendingSet);
345 retval = kSOSCCViewMember;
346 updateCircle = false;
347 }
5c19dc3a 348 } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
fa7225c8
A
349 if(alreadyInSync) {
350 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
351 if(retval == kSOSCCViewNotMember) updateCircle = true;
352 } else {
353 SOSAccountPendDisableViewSet(account, pendingSet);
354 retval = kSOSCCViewNotMember;
355 updateCircle = false;
356 }
5c19dc3a
A
357 } else {
358 retval = currentStatus;
359 }
fa7225c8
A
360
361 CFReleaseNull(pendingSet);
5c19dc3a
A
362
363 if (updateCircle) {
364 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
365 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
366 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
367 });
368 }
369 }
370
371errOut:
372 return retval;
373}
374
375SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewname, CFErrorRef *error) {
376 SOSViewResultCode retval = kSOSCCGeneralViewError;
377 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
378 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
e0e0d90e 379
fa7225c8
A
380 if (SOSAccountValueSetContainsValue(account, kSOSPendingEnableViewsToBeSetKey, viewname)) {
381 retval = kSOSCCViewMember;
382 } else if (SOSAccountValueSetContainsValue(account, kSOSPendingDisableViewsToBeSetKey, viewname)) {
383 retval = kSOSCCViewNotMember;
384 } else {
385 retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
386 }
e0e0d90e
A
387
388 // If that doesn't say we're a member and this view is a V0 subview, and we're syncing V0 views we are a member
389 if (retval != kSOSCCViewMember) {
390 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
391 && SOSAccountSyncingV0(account)) {
392 retval = kSOSCCViewMember;
393 }
394 }
395
396 // If we're only an applicant we report pending if we would be a view member
397 if (retval == kSOSCCViewMember) {
398 bool isApplicant = SOSCircleHasApplicant(account->trusted_circle, SOSAccountGetMyPeerInfo(account), error);
399 if (isApplicant) {
400 retval = kSOSCCViewPending;
401 }
402 }
403
5c19dc3a
A
404errOut:
405 return retval;
406}
407
408static void dumpViewSet(CFStringRef label, CFSetRef views) {
409 if(views) {
fa7225c8
A
410 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
411 secnotice("circleChange", "%@ list: %@", label, description);
412 });
5c19dc3a
A
413 } else {
414 secnotice("circleChange", "No %@ list provided.", label);
415 }
416}
417
fa7225c8
A
418static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account, CFMutableSetRef viewSet, SOSViewActionCode actionCode) {
419 bool retval = true;
420 if(viewSet && CFSetContainsValue(viewSet, kSOSViewKeychainV0)) {
421 retval = SOSAccountVirtualV0Behavior(account, actionCode) != kSOSCCGeneralViewError;
422 CFSetRemoveValue(viewSet, kSOSViewKeychainV0);
423 }
424 return retval;
425}
426
427bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef origEnabledViews, CFSetRef origDisabledViews) {
428 bool retval = false;
5c19dc3a 429 bool updateCircle = false;
fa7225c8
A
430 SOSPeerInfoRef pi = NULL;
431 CFMutableSetRef enabledViews = NULL;
432 CFMutableSetRef disabledViews = NULL;
433 if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
434 if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
5c19dc3a
A
435 dumpViewSet(CFSTR("Enabled"), enabledViews);
436 dumpViewSet(CFSTR("Disabled"), disabledViews);
437
438 require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
5c19dc3a 439
fa7225c8 440 // Make sure we have a peerInfo capable of supporting views.
5c19dc3a 441 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
fa7225c8
A
442 require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
443 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
444
445 pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
5c19dc3a
A
446
447 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
448
5c19dc3a 449 if(!SOSPeerInfoVersionIsCurrent(pi)) {
fa7225c8
A
450 CFErrorRef updateFailure = NULL;
451 require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
452 (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
453 secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
454 updateCircle = true;
5c19dc3a
A
455 }
456
fa7225c8
A
457 CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
458 secnotice("viewChange", "Enabling %@", description);
459 });
5c19dc3a 460
fa7225c8
A
461 CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
462 secnotice("viewChange", "Disabling %@", description);
463 });
5c19dc3a 464
fa7225c8
A
465 require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
466 require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
467
468 if(SOSAccountHasCompletedInitialSync(account)) {
469 if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
470 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
471 retval = true;
472 } else {
473 //hold on to the views and enable them later
474 if(enabledViews) SOSAccountPendEnableViewSet(account, enabledViews);
475 if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
476 retval = true;
5c19dc3a
A
477 }
478
fa7225c8
A
479 if(updateCircle) {
480 /* UPDATE FULLPEERINFO VIEWS */
481 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
482
483 require_quiet(SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
484 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
485 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
486 }), errOut);
487
488 // Make sure we update the engine
489 account->circle_rings_retirements_need_attention = true;
490 }
491
5c19dc3a 492errOut:
fa7225c8
A
493 CFReleaseNull(enabledViews);
494 CFReleaseNull(disabledViews);
495 CFReleaseNull(pi);
496 return retval;
5c19dc3a
A
497}
498
499
500SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
501 CFDictionaryRef gestalt,
502 SOSDataSourceFactoryRef factory) {
503 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
504
5c19dc3a
A
505 SOSAccountEnsureFactoryCircles(a);
506
fa7225c8
A
507 a->key_interests_need_updating = true;
508
5c19dc3a
A
509 return a;
510}
511
512static void SOSAccountDestroy(CFTypeRef aObj) {
513 SOSAccountRef a = (SOSAccountRef) aObj;
514
515 // We don't own the factory, merely have a reference to the singleton
516 // Don't free it.
517 // a->factory
518
fa7225c8 519 SOSAccountCancelSyncChecking(a);
5c19dc3a
A
520
521 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
522
523 if (engine)
524 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
525
526 dispatch_sync(a->queue, ^{
527 CFReleaseNull(a->gestalt);
528
529 CFReleaseNull(a->my_identity);
530 CFReleaseNull(a->trusted_circle);
5c19dc3a
A
531 CFReleaseNull(a->backups);
532 CFReleaseNull(a->retirees);
533
534 a->user_public_trusted = false;
535 CFReleaseNull(a->user_public);
536 CFReleaseNull(a->user_key_parameters);
537
538 SOSAccountPurgePrivateCredential(a);
539 CFReleaseNull(a->previous_public);
540 CFReleaseNull(a->_user_private);
541 CFReleaseNull(a->_password_tmp);
542
543 a->departure_code = kSOSNeverAppliedToCircle;
544 CFReleaseNull(a->kvs_message_transport);
545 CFReleaseNull(a->ids_message_transport);
546 CFReleaseNull(a->key_transport);
547 CFReleaseNull(a->circle_transport);
548 dispatch_release(a->queue);
5c19dc3a
A
549
550 dispatch_release(a->user_private_timer);
551 CFReleaseNull(a->change_blocks);
e3d460c9 552 CFReleaseNull(a->waitForInitialSync_blocks);
5c19dc3a
A
553 CFReleaseNull(a->expansion);
554
fa7225c8
A
555 CFReleaseNull(a->saveBlock);
556 CFReleaseNull(a->deviceID);
5c19dc3a
A
557 });
558}
559
560static OSStatus do_delete(CFDictionaryRef query) {
561 OSStatus result;
562
563 result = SecItemDelete(query);
564 if (result) {
565 secerror("SecItemDelete: %d", (int)result);
566 }
567 return result;
568}
569
570static int
571do_keychain_delete_aks_bags()
572{
573 OSStatus result;
574 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
575 kSecClass, kSecClassGenericPassword,
576 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
577 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
578 kSecAttrService, CFSTR("SecureBackupService"),
579 kSecAttrSynchronizable, kCFBooleanTrue,
580 kSecUseTombstones, kCFBooleanFalse,
581 NULL);
582
583 result = do_delete(item);
584 CFReleaseSafe(item);
585
586 return result;
587}
588
589static int
590do_keychain_delete_identities()
591{
592 OSStatus result;
593 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
594 kSecClass, kSecClassKey,
595 kSecAttrSynchronizable, kCFBooleanTrue,
596 kSecUseTombstones, kCFBooleanFalse,
597 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
598 NULL);
599
600 result = do_delete(item);
601 CFReleaseSafe(item);
602
603 return result;
604}
605
606static int
607do_keychain_delete_lakitu()
608{
609 OSStatus result;
610 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
611 kSecClass, kSecClassGenericPassword,
612 kSecAttrSynchronizable, kCFBooleanTrue,
613 kSecUseTombstones, kCFBooleanFalse,
614 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
615 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
616 kSecAttrService, CFSTR("EscrowService"),
617 NULL);
618
619 result = do_delete(item);
620 CFReleaseSafe(item);
621
622 return result;
623}
624
625static int
626do_keychain_delete_sbd()
627{
628 OSStatus result;
629 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
630 kSecClass, kSecClassGenericPassword,
631 kSecAttrSynchronizable, kCFBooleanTrue,
632 kSecUseTombstones, kCFBooleanFalse,
633 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
634 NULL);
635
636 result = do_delete(item);
637 CFReleaseSafe(item);
638
639 return result;
640}
641
fa7225c8
A
642void static SOSAccountResetKeyInterests(SOSAccountRef a) {
643 CFDictionaryRef emptyDictionary = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
644 SOSCloudKeychainUpdateKeys(emptyDictionary, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error) {
645 if (error) {
646 secerror("Error updating keys: %@", error);
647 }
648 });
649 CFReleaseNull(emptyDictionary);
650}
651
5c19dc3a
A
652void SOSAccountSetToNew(SOSAccountRef a) {
653 secnotice("accountChange", "Setting Account to New");
654 int result = 0;
655
656 CFReleaseNull(a->my_identity);
657 CFReleaseNull(a->trusted_circle);
5c19dc3a
A
658 CFReleaseNull(a->backups);
659 CFReleaseNull(a->retirees);
660
661 CFReleaseNull(a->user_key_parameters);
662 CFReleaseNull(a->user_public);
663 CFReleaseNull(a->previous_public);
664 CFReleaseNull(a->_user_private);
665 CFReleaseNull(a->_password_tmp);
666
667 CFReleaseNull(a->key_transport);
668 CFReleaseNull(a->circle_transport);
669 CFReleaseNull(a->kvs_message_transport);
670 CFReleaseNull(a->ids_message_transport);
671 CFReleaseNull(a->expansion);
fa7225c8 672 CFReleaseNull(a->deviceID);
5c19dc3a
A
673
674 /* remove all syncable items */
fa7225c8 675 result = do_keychain_delete_aks_bags(); (void) result;
5c19dc3a
A
676 secdebug("set to new", "result for deleting aks bags: %d", result);
677
fa7225c8 678 result = do_keychain_delete_identities(); (void) result;
5c19dc3a
A
679 secdebug("set to new", "result for deleting identities: %d", result);
680
fa7225c8 681 result = do_keychain_delete_lakitu(); (void) result;
5c19dc3a
A
682 secdebug("set to new", "result for deleting lakitu: %d", result);
683
fa7225c8 684 result = do_keychain_delete_sbd(); (void) result;
5c19dc3a
A
685 secdebug("set to new", "result for deleting sbd: %d", result);
686
687 a->user_public_trusted = false;
688 a->departure_code = kSOSNeverAppliedToCircle;
fa7225c8
A
689
690 if (a->user_private_timer) {
691 dispatch_source_cancel(a->user_private_timer);
692 dispatch_release(a->user_private_timer);
693 a->user_private_timer = NULL;
694 xpc_transaction_end();
695
696 }
697 if (a->lock_notification_token != NOTIFY_TOKEN_INVALID) {
698 notify_cancel(a->lock_notification_token);
699 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
700 }
5c19dc3a
A
701
702 // keeping gestalt;
703 // keeping factory;
704 // Live Notification
705 // change_blocks;
706 // update_interest_block;
707 // update_block;
708
709 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
710 a->circle_transport = NULL;
711 a->kvs_message_transport = NULL;
712 a->ids_message_transport = NULL;
713
5c19dc3a
A
714 a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
715
716 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
717 a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
fa7225c8 718 SOSAccountAddRingDictionary(a);
5c19dc3a
A
719
720 SOSAccountEnsureFactoryCircles(a); // Does rings too
e0e0d90e 721
fa7225c8
A
722 // Reset our key interests since we are new, we need to hear about everything:
723 SOSAccountResetKeyInterests(a);
724
725 a->key_interests_need_updating = true;
5c19dc3a
A
726}
727
fa7225c8
A
728bool SOSAccountIsNew(SOSAccountRef account, CFErrorRef *error){
729 bool result = false;
730 require_quiet(account->user_public_trusted == false, exit);
731 require_quiet(account->departure_code == kSOSNeverAppliedToCircle, exit);
732 require_quiet(account->user_private_timer == NULL, exit);
733 require_quiet(account->lock_notification_token == NOTIFY_TOKEN_INVALID, exit);
734 require_quiet (CFDictionaryGetCount(account->backups) == 0, exit);
735 require_quiet(CFSetGetCount(account->retirees) == 0, exit);
736
737 result = true;
738exit:
739 return result;
740}
5c19dc3a
A
741
742static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
743 SOSAccountRef a = (SOSAccountRef) aObj;
744
745 CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
746
fa7225c8
A
747 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: %c%c%c%c%c G: %@ Me: %@ C: %@ >"), a,
748 a->user_public ? 'P' : 'p',
749 a->user_public_trusted ? 'T' : 't',
750 a->isListeningForSync ? 'L' : 'l',
751 SOSAccountHasCompletedInitialSync(a) ? 'C' : 'c',
752 SOSAccountHasCompletedRequiredBackupSync(a) ? 'B' : 'b',
753 gestaltDescription, a->my_identity, a->trusted_circle);
5c19dc3a
A
754
755 CFReleaseNull(gestaltDescription);
756
757 return result;
758}
759
760CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
761
762 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
763
764 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
765
766 CFReleaseNull(gestaltDescription);
767
768 return result;
769}
770
771static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
772{
773 SOSAccountRef laccount = (SOSAccountRef) lhs;
774 SOSAccountRef raccount = (SOSAccountRef) rhs;
775
776 return CFEqualSafe(laccount->gestalt, raccount->gestalt)
777 && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
fa7225c8 778 && CFEqualSafe(laccount->expansion, raccount->expansion)
5c19dc3a
A
779 && CFEqualSafe(laccount->my_identity, raccount->my_identity);
780}
781
782dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
783 return account->queue;
784}
785
786void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
787 account->user_public_trusted = true;
788}
789
790SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
791{
792 return CFRetainSafe(account->my_identity);
793}
794
795static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
796 bool ok = false;
797 __block CFErrorRef error = NULL;
fa7225c8 798
5c19dc3a
A
799 if (!SOSAccountHasPublicKey(account, &error)) {
800 CFReleaseSafe(error);
801 return false;
802 }
803
804 bool hasID = true;
805
806 require_action_quiet(account->my_identity, xit,
807 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
808
fa7225c8 809 SOSTransportMessageIDSGetIDSDeviceID(account);
5c19dc3a
A
810
811 require_action_quiet(account->trusted_circle, xit,
812 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
813
814 require_action_quiet(hasID, xit,
815 SOSCreateError(kSOSErrorBadFormat, CFSTR("Missing IDS device ID"), NULL, &error));
816 ok = SOSCircleHasPeerWithID(account->trusted_circle,
817 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity)), &error);
818xit:
819 if (!ok) {
820 secerror("sync with device failure: %@", error);
821 }
822 CFReleaseSafe(error);
823 return ok;
824}
825
e0e0d90e 826static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef peerID) {
5c19dc3a
A
827 SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
828 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
829
830 return myPeerID && CFEqualSafe(myPeerID, peerID);
831}
832
fa7225c8 833bool SOSAccountSendIKSPSyncList(SOSAccountRef account, CFErrorRef *error){
5c19dc3a 834 bool result = true;
5c19dc3a 835 __block CFErrorRef localError = NULL;
fa7225c8
A
836 __block CFMutableArrayRef ids = NULL;
837 SOSCircleRef circle = NULL;
838
839 require_action_quiet(SOSAccountIsInCircle(account, NULL), xit,
840 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"),
841 NULL, &localError));
842
843 circle = SOSAccountGetCircle(account, error);
844 ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
845
5c19dc3a
A
846 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
847 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
848 NULL, &localError));
849
850 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
e0e0d90e 851 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
fa7225c8
A
852 if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer) &&
853 SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
854 SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue);
855 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
856 if(deviceID != NULL){
857 CFArrayAppendValue(ids, deviceID);
e3d460c9 858 }
fa7225c8
A
859 CFReleaseNull(deviceID);
860 }
861 }
862 });
863 require_quiet(CFArrayGetCount(ids) != 0, xit);
864 secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids);
865
866 SOSCloudKeychainGetIDSDeviceAvailability(ids, SOSAccountGetMyPeerID(account), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
867 bool success = (sync_error == NULL);
868 if(!success)
869 secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error);
870 });
871xit:
872 if(error && *error != NULL)
873 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
874
875 if(localError)
876 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
877
878 CFReleaseNull(ids);
879 CFReleaseNull(localError);
880
881 return result;
882}
883
884bool SOSAccountSyncWithAllKVSPeers(SOSAccountRef account, CFErrorRef *error)
885{
886 __block bool result = true;
887
888 if(SOSAccountIsInCircle(account, NULL)) {
889 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
890 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
891 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
892 if(deviceID == NULL || !SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
893 result = SOSAccountSyncWithKVSPeer(account, SOSPeerInfoGetPeerID(peer), error);
894 if(result){
895 secnotice("KVS Transport", "synced with peer: %@", SOSPeerInfoGetPeerID(peer));
896 }
897 else{
898 secnotice("KVS Transport", "failed to sync with peer: %@", SOSPeerInfoGetPeerID(peer));
899 }
900 }
901 CFReleaseNull(deviceID);
5c19dc3a 902 }
fa7225c8
A
903 });
904 }
905 secnotice("sync", "SOSAccountSyncWithAllKVSPeers returns: %d", result);
906 return true;
907}
908
909static CFMutableArrayRef SOSAccountCopyPeerIDsForDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef* error) {
910 CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
911
912 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
913 CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
914 if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
915 CFArrayAppendValue(peerIDs, SOSPeerInfoGetPeerID(peer));
5c19dc3a 916 }
fa7225c8 917 CFReleaseNull(peerDeviceID);
5c19dc3a 918 });
fa7225c8
A
919
920 if (peerIDs == NULL || CFArrayGetCount(peerIDs) == 0) {
921 CFReleaseNull(peerIDs);
922 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer with DSID: %@"), deviceID);
5c19dc3a
A
923 }
924
fa7225c8
A
925 return peerIDs;
926}
927
928static bool SOSAccountSyncWithKVSPeers(SOSAccountRef account, CFArrayRef peerIDs, CFErrorRef *error) {
929 CFDictionaryRef circleToPeerIDs = NULL;
930
931 CFErrorRef localError = NULL;
932 bool result = false;
933 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
934 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
935 NULL, &localError));
936
937 circleToPeerIDs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
938 SOSCircleGetName(account->trusted_circle), peerIDs,
939 NULL);
940 result = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
5c19dc3a 941
fa7225c8
A
942 SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
943 result &= SOSEngineSyncWithPeers(engine, &localError);
5c19dc3a
A
944
945 if (result)
946 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
947
948xit:
949 CFReleaseNull(circleToPeerIDs);
fa7225c8
A
950 if (!result) {
951 // Tell account to update SOSEngine with current trusted peers
952 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
953 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
954 account->engine_peer_state_needs_repair = true;
955 }
956 CFErrorPropagate(localError, error);
957 localError = NULL;
958 }
959 return result;
960
961}
5c19dc3a 962
fa7225c8
A
963bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error) {
964 bool result = false;
965 CFErrorRef localError = NULL;
966
967 secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID);
968
969 CFArrayRef peerIDs = SOSAccountCopyPeerIDsForDSID(account, deviceID, &localError);
970 require_quiet(peerIDs, xit);
971
972 CFStringArrayPerfromWithDescription(peerIDs, ^(CFStringRef peerIDList) {
973 secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList);
974 });
975
976 result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
977 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
978
979xit:
980 CFReleaseNull(peerIDs);
981 CFErrorPropagate(localError, error);
982
983 return result;
984}
985
986bool SOSAccountSyncWithKVSPeer(SOSAccountRef account, CFStringRef peerID, CFErrorRef *error)
987{
988 bool result = false;
989 CFErrorRef localError = NULL;
990
991 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID);
992
993 CFArrayRef peerIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault, peerID, NULL);
994
995 result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
996 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
997
998 CFReleaseNull(peerIDs);
999 CFErrorPropagate(localError, error);
1000
1001 return result;
1002}
1003
1004#define LOG_ENGINE_STATE_INTERVAL 20
1005
1006bool SOSAccountSyncWithIDSPeer(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error)
1007{
1008 CFErrorRef localError = NULL;
1009 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1010 CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1011 static int engineLogCountDown = 0;
1012 bool result = true;
1013
1014 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
1015 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
1016 NULL, &localError));
1017 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
1018 CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
1019 if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
1020 CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
1021 }
1022 CFReleaseNull(peerDeviceID);
1023 });
1024
1025 require_action_quiet(CFArrayGetCount(ids), xit, SOSCreateError(kSOSErrorNoCircle, CFSTR("Cannot find peer in circle"),
1026 NULL, &localError));
1027
1028 secnotice("IDS Transport","Syncing with IDS capable peer: %@", ids);
1029 CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(account->trusted_circle), ids);
1030 result = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToPeerIDs, &localError);
1031 secnotice("IDS Transport", "IDS Sync result: %d", result);
1032
1033 SOSEngineRef engine = SOSTransportMessageGetEngine(account->ids_message_transport);
1034 result &= SOSEngineSyncWithPeers(engine, &localError);
1035
1036 if (result)
1037 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
1038
1039 if(engineLogCountDown <= 0) {
1040 SOSEngineLogState(engine);
1041 engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
1042 } else {
1043 engineLogCountDown--;
1044 }
1045
1046xit:
1047 CFReleaseNull(circleToPeerIDs);
1048
5c19dc3a 1049 if (!result) {
fa7225c8 1050 secdebug("Account", "Could not sync with peer %@, error: %@", deviceID, localError);
e3d460c9
A
1051 // Tell account to update SOSEngine with current trusted peers
1052 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
1053 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
fa7225c8 1054 account->engine_peer_state_needs_repair = true;
e3d460c9 1055 }
5c19dc3a
A
1056 CFErrorPropagate(localError, error);
1057 localError = NULL;
1058 }
fa7225c8 1059 CFReleaseNull(ids);
5c19dc3a 1060 CFReleaseSafe(localError);
fa7225c8 1061
5c19dc3a
A
1062 return result;
1063}
1064
fa7225c8
A
1065
1066
5c19dc3a
A
1067bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
1068 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
1069{
1070 bool success = true;
1071
1072 SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1073 require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
1074 require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
1075
1076 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
1077
1078 CFStringRef circle_name = SOSCircleGetName(circle);
1079
1080 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1081 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
1082
1083 CFErrorRef localError = NULL;
1084 if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
1085 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
1086 }
1087
1088 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
1089 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
1090 }
1091
1092 CFReleaseNull(localError);
1093
1094 if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
1095 if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
1096 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
1097 }
1098 }
1099 CFReleaseNull(localError);
1100 CFReleaseNull(circleToPeerIDs);
1101
1102xit:
1103 return success;
1104}
1105
1106bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
1107 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
1108
1109 __block bool success = true;
1110
1111 CFSetForEach(account->retirees, ^(const void *value) {
1112 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
1113
1114 if (retiree) {
1115 // Remove the entry if it's not a retired peer or if it's retirment ticket has expired AND he's no longer in the circle.
1116 if (!SOSPeerInfoIsRetirementTicket(retiree) ||
1117 (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
1118 CFSetAddValue(retirees_to_remove, retiree);
1119 };
1120 }
1121 });
1122
1123 CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1124 CFSetForEach(retirees_to_remove, ^(const void *value) {
1125 CFArrayAppendValue(retirees_to_cleanup, value);
1126 CFSetRemoveValue(account->retirees, value);
1127 });
1128
1129 CFReleaseNull(retirees_to_remove);
1130
1131 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1132 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
1133 NULL);
1134
1135 CFReleaseNull(retirees_to_cleanup);
1136
1137 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
1138
1139 CFReleaseNull(retirements_to_remove);
1140
1141 return success;
1142}
1143
1144bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
1145 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1146 CFSetSetValue(account->retirees, peer);
1147 CFErrorRef cleanupError = NULL;
1148 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
1149 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
1150 }
1151 CFReleaseSafe(cleanupError);
1152 });
1153 return true;
1154}
1155
1156SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
1157 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
fa7225c8
A
1158 SOSFullPeerInfoRef meFull = SOSAccountGetMyFullPeerInfo(account);
1159 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(meFull);
1160 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
1161
5c19dc3a 1162 if(!new_circle) return NULL;
fa7225c8 1163 __block bool workDone = false;
5c19dc3a
A
1164 if (account->retirees) {
1165 CFSetForEach(account->retirees, ^(const void* value) {
1166 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
1167 if (isSOSPeerInfo(pi)) {
1168 SOSCircleUpdatePeerInfo(new_circle, pi);
fa7225c8 1169 workDone = true;
5c19dc3a
A
1170 }
1171 });
1172 }
1173
fa7225c8
A
1174 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
1175 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
1176
1177 if(iAmApplicant) {
1178 if(userPrivKey) {
1179 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
1180 if(!SOSCircleResetToOffering(new_circle, userPrivKey, meFull, error) ||
1181 !SOSAccountAddiCloudIdentity(account, new_circle, userPrivKey, error)) {
1182 CFReleaseNull(new_circle);
1183 return NULL;
1184 }
1185 } else {
1186 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
1187 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
1188 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
1189 // handleUpdateCircle to return false.
1190 CFReleaseNull(new_circle);
1191 return NULL;
1192 }
1193 } else {
1194 // This case is when we aren't an applicant and the circle is retirement-empty.
1195 secnotice("resetToEmpty", "Reset to empty with last retirement");
1196 SOSCircleResetToEmpty(new_circle, NULL);
1197 }
5c19dc3a
A
1198 }
1199
1200 return new_circle;
1201}
1202
1203//
1204// MARK: Circle Membership change notificaion
1205//
1206
1207void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1208 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
1209 CFArrayAppendValue(a->change_blocks, copy);
1210 CFReleaseNull(copy);
1211}
1212
1213void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1214 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
1215}
1216
1217void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
1218 if (!changeBlock) return;
1219
1220 CFRetainSafe(ds_name);
1221 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
1222 CFSetRef added_peers, CFSetRef removed_peers,
1223 CFSetRef added_applicants, CFSetRef removed_applicants) {
1224
1225 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
1226 return;
1227
1228 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
1229 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
1230
1231 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1232 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1233 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1234
1235 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
1236 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
1237 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
1238 });
1239
1240 CFSetForEach(added_peers, ^(const void *value) {
1241 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
1242 });
1243
1244 CFSetForEach(removed_peers, ^(const void *value) {
1245 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
1246 });
1247 }
1248
1249 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
1250 changeBlock(peer_ids, added_ids, removed_ids);
1251
1252 CFReleaseSafe(peer_ids);
1253 CFReleaseSafe(added_ids);
1254 CFReleaseSafe(removed_ids);
1255 };
1256
1257 CFRetainSafe(changeBlock);
1258 SOSAccountAddChangeBlock(a, block_to_register);
1259
1260 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
1261 if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
1262 block_to_register(a->trusted_circle, empty, empty, empty, empty);
1263 }
1264 CFReleaseSafe(empty);
1265}
1266
1267void SOSAccountPurgeIdentity(SOSAccountRef account) {
1268 if (account->my_identity) {
1269 // Purge private key but don't return error if we can't.
1270 CFErrorRef purgeError = NULL;
1271 if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
1272 secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
1273 }
1274 CFReleaseNull(purgeError);
1275
1276 CFReleaseNull(account->my_identity);
1277 }
1278}
1279
1280bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1281 SOSFullPeerInfoRef fpi = account->my_identity;
1282 if(!fpi) return false;
1283
1284 CFErrorRef localError = NULL;
1285
1286 bool retval = false;
1287
1288 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1289 if (!retire_peer) {
1290 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1291 } else {
1292 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1293 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1294 // Remove our application if we have one.
1295 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1296 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1297 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1298 CFErrorRef cleanupError = NULL;
1299 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
1300 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1301 }
1302 CFReleaseSafe(cleanupError);
1303 }
1304 }
1305
1306 // Store the retirement record locally.
1307 CFSetAddValue(account->retirees, retire_peer);
1308
1309 // Write retirement to Transport
1310 CFErrorRef postError = NULL;
1311 if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
1312 secwarning("Couldn't post retirement (%@)", postError);
1313 }
1314 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
1315 secwarning("Couldn't flush retirement data (%@)", postError);
1316 }
1317 CFReleaseNull(postError);
1318 }
1319
1320 SOSAccountPurgeIdentity(account);
1321
1322 retval = true;
1323
1324 CFReleaseNull(localError);
1325 CFReleaseNull(retire_peer);
1326 return retval;
1327}
1328
1329bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
1330 SOSFullPeerInfoRef fpi = account->my_identity;
1331 if(!fpi) return false;
1332 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1333 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1334
1335 CFErrorRef localError = NULL;
1336
1337 bool retval = false;
1338 bool writeRing = false;
1339 bool writePeerInfo = false;
1340
1341 if(SOSRingHasPeerID(ring, peerID)) {
1342 writePeerInfo = true;
1343 }
1344
1345#if 0
1346 // this was circle behavior - at some point
1347 if(SOSRingHasApplicant(ring, peerID)) {
1348 writeRing = true;
1349 }
1350#endif
1351
1352 if(writePeerInfo || writeRing) {
1353 SOSRingWithdraw(ring, NULL, fpi, error);
1354 }
1355
1356 // Write leave thing to Transport
1357 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
1358 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
1359
1360 if (writeRing) {
1361 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1362
1363 if (ring_data) {
1364 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
1365 }
1366 CFReleaseNull(ring_data);
1367 }
1368 retval = true;
1369 CFReleaseNull(localError);
1370 return retval;
1371}
1372
1373bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
1374 bool result = false;
1375 SOSTransportCircleRef transport = account->circle_transport;
1376 if (transport) {
1377 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
1378 }
1379 return result;
1380}
1381
1382/*
1383 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1384 local value that has been overwritten by a distant value. If there is no
1385 conflict between the local and the distant values when doing the initial
1386 sync (e.g. if the cloud has no data stored or the client has not stored
1387 any data yet), you'll never see that notification.
1388
1389 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1390 with server but initial round trip with server does not imply
1391 NSUbiquitousKeyValueStoreInitialSyncChange.
1392 */
1393
1394
1395//
1396// MARK: Status summary
1397//
1398
1399static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
1400 if (!circle)
1401 return kSOSCCNotInCircle;
1402
1403 if (circle && SOSCircleCountPeers(circle) == 0)
1404 return kSOSCCCircleAbsent;
1405
1406 if (this_peer) {
1407
1408 if(SOSPeerInfoIsRetirementTicket(this_peer))
1409 return kSOSCCNotInCircle;
1410
1411 if (SOSCircleHasPeer(circle, this_peer, NULL))
1412 return kSOSCCInCircle;
1413
1414 if (SOSCircleHasApplicant(circle, this_peer, NULL))
1415 return kSOSCCRequestPending;
1416 }
1417
1418 return kSOSCCNotInCircle;
1419}
1420
fa7225c8
A
1421CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1422 switch(status) {
1423 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1424 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1425 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1426 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1427 case kSOSCCError: return CFSTR("kSOSCCError");
1428 }
1429 return CFSTR("kSOSCCError");
1430}
1431
5c19dc3a
A
1432bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
1433 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
1434
fa7225c8 1435 if (result != kSOSCCInCircle) {
5c19dc3a
A
1436 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
1437 return false;
1438 }
1439
1440 return true;
1441}
1442
1443SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
1444 if (!SOSAccountHasPublicKey(account, error)) {
1445 return kSOSCCError;
1446 }
1447
1448 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
1449}
1450
1451//
1452// MARK: Account Reset Circles
1453//
1454
fa7225c8 1455// This needs to be called within a SOSAccountModifyCircle() block
5c19dc3a 1456
fa7225c8
A
1457bool SOSAccountAddiCloudIdentity(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
1458 bool result = false;
1459 SOSFullPeerInfoRef cloud_identity = NULL;
1460 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
1461 require_quiet(cloud_peer, err_out);
1462 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
1463 CFReleaseNull(cloud_peer);
1464 require_quiet(cloud_identity, err_out);
1465 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, error), err_out);
1466 require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), error), err_out);
1467 result = true;
1468err_out:
1469 return result;
1470}
1471
1472bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1473 bool retval = false;
1474 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1475
1476 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1477 if(SOSPeerInfoIsCloudIdentity(peer)) {
1478 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1479 if(!icfpi) {
1480 CFSetAddValue(iCloud2Remove, peer);
1481 }
1482 CFReleaseNull(icfpi);
1483 }
1484 });
1485
1486 if(CFSetGetCount(iCloud2Remove) > 0) {
1487 retval = true;
1488 SOSCircleRemovePeers(circle, privKey, account->my_identity, iCloud2Remove, error);
1489 }
1490 CFReleaseNull(iCloud2Remove);
1491 return retval;
1492}
1493
1494static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn, SecKeyRef user_key, CFErrorRef *error) {
1495 SOSAccountRef account = aTxn->account;
1496 bool result = false;
1497
1498 require(SOSAccountHasCircle(account, error), fail);
5c19dc3a
A
1499 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1500
e0e0d90e 1501 (void) SOSAccountResetAllRings(account, error);
fa7225c8 1502
5c19dc3a
A
1503 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1504 bool result = false;
1505 SOSFullPeerInfoRef cloud_identity = NULL;
1506 CFErrorRef localError = NULL;
1507
1508 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1509
5c19dc3a 1510 account->departure_code = kSOSNeverLeftCircle;
e0e0d90e 1511 require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
fa7225c8
A
1512
1513 require_quiet(SOSAccountAddiCloudIdentity(account, circle, user_key, error), err_out);
5c19dc3a
A
1514 result = true;
1515 SOSAccountPublishCloudParameters(account, NULL);
1516
1517 err_out:
1518 if (result == false)
1519 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1520 if (localError && error && *error == NULL) {
1521 *error = localError;
1522 localError = NULL;
1523 }
1524 CFReleaseNull(localError);
1525 CFReleaseNull(cloud_identity);
1526 return result;
1527 });
1528
fa7225c8
A
1529 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1530 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1531
5c19dc3a
A
1532 result = true;
1533
1534fail:
1535 return result;
1536}
1537
1538
fa7225c8
A
1539bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1540 SOSAccountRef account = aTxn->account;
5c19dc3a
A
1541 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1542 if (!user_key)
1543 return false;
1544
1545 CFReleaseNull(account->my_identity);
fa7225c8 1546 secnotice("resetToOffering", "Resetting circle to offering by request from client");
5c19dc3a 1547
fa7225c8 1548 return user_key && SOSAccountResetCircleToOffering(aTxn, user_key, error);
5c19dc3a
A
1549}
1550
1551bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1552 if (!SOSAccountHasPublicKey(account, error))
1553 return false;
e0e0d90e
A
1554 __block bool result = true;
1555
1556 result &= SOSAccountResetAllRings(account, error);
5c19dc3a
A
1557
1558 CFReleaseNull(account->my_identity);
1559
5c19dc3a 1560 account->departure_code = kSOSWithdrewMembership;
fa7225c8 1561 secnotice("resetToEmpty", "Reset Circle to empty by client request");
5c19dc3a
A
1562 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1563 result = SOSCircleResetToEmpty(circle, error);
1564 return result;
1565 });
1566
1567 if (!result) {
1568 secerror("error: %@", error ? *error : NULL);
1569 }
5c19dc3a
A
1570 return result;
1571}
e3d460c9
A
1572//
1573// MARK: start backups
1574//
5c19dc3a 1575
fa7225c8 1576bool SOSAccountEnsureInBackupRings(SOSAccountRef account) {
e3d460c9
A
1577 __block bool result = false;
1578 __block CFErrorRef error = NULL;
fa7225c8 1579 secnotice("backup", "Ensuring in rings");
e3d460c9 1580
fa7225c8 1581 CFDataRef backupKey = NULL;
5c19dc3a 1582
fa7225c8 1583 require_action_quiet(account->backup_key, exit, result = true);
5c19dc3a 1584
fa7225c8 1585 backupKey = SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey);
5c19dc3a 1586
fa7225c8
A
1587 require_action_quiet(!CFEqualSafe(backupKey, account->backup_key), exit, result = true); // If we're already set up, we're done.
1588 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1589 return SOSFullPeerInfoUpdateBackupKey(fpi, account->backup_key, error);
1590 }), exit);
5c19dc3a 1591
fa7225c8 1592 require_quiet(account->backup_key, exit); // If it went null, we're done now.
5c19dc3a 1593
fa7225c8 1594 require_quiet(SOSBSKBIsGoodBackupPublic(account->backup_key, &error), exit);
5c19dc3a 1595
fa7225c8
A
1596 // It's a good key, we're going with it. Stop backing up the old way.
1597 CFErrorRef localError = NULL;
1598 if (!SOSDeleteV0Keybag(&localError)) {
1599 secerror("Failed to delete v0 keybag: %@", localError);
5c19dc3a 1600 }
fa7225c8 1601 CFReleaseNull(localError);
e3d460c9 1602
fa7225c8 1603 result = true;
5c19dc3a 1604
fa7225c8
A
1605 // Setup backups the new way.
1606 SOSAccountForEachBackupView(account, ^(const void *value) {
1607 CFStringRef viewName = (CFStringRef)value;
1608 result &= SOSAccountNewBKSBForView(account, viewName, &error);
5c19dc3a
A
1609 });
1610
fa7225c8
A
1611exit:
1612 if (!result) {
1613 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
5c19dc3a 1614 }
fa7225c8
A
1615 CFReleaseNull(backupKey);
1616 return result;
5c19dc3a
A
1617}
1618
1619//
1620// MARK: Joining
1621//
1622
fa7225c8 1623static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn, SecKeyRef user_key,
5c19dc3a 1624 bool use_cloud_peer, CFErrorRef* error) {
fa7225c8
A
1625 SOSAccountRef account = aTxn->account;
1626
5c19dc3a
A
1627 __block bool result = false;
1628 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1629
1630 require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1631 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1632
1633 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1634
fa7225c8
A
1635 if (SOSCircleCountPeers(account->trusted_circle) == 0) {
1636 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1637 // this also clears initial sync data
1638 result = SOSAccountResetCircleToOffering(aTxn, user_key, error);
5c19dc3a
A
1639 } else {
1640 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
5c19dc3a 1641
fa7225c8
A
1642 if (use_cloud_peer) {
1643 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1644 }
1645
5c19dc3a 1646 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
e0e0d90e
A
1647 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1648 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
5c19dc3a
A
1649 account->departure_code = kSOSNeverLeftCircle;
1650 if(result && cloud_full_peer) {
1651 CFErrorRef localError = NULL;
1652 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1653 require_quiet(cloudid, finish);
1654 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1655 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
fa7225c8 1656
5c19dc3a
A
1657 finish:
1658 if (localError){
1659 secerror("Failed to join with cloud identity: %@", localError);
1660 CFReleaseNull(localError);
1661 }
1662 }
1663 return result;
1664 });
fa7225c8
A
1665
1666 if (use_cloud_peer) {
1667 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1668 }
5c19dc3a
A
1669 }
1670
1671fail:
1672 CFReleaseNull(cloud_full_peer);
1673 return result;
1674}
1675
fa7225c8
A
1676static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn, bool use_cloud_identity, CFErrorRef* error) {
1677 SOSAccountRef account = aTxn->account;
5c19dc3a
A
1678 bool success = false;
1679
1680 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1681 require_quiet(user_key, done); // Fail if we don't get one.
1682
1683 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1684
1685 if (account->my_identity != NULL) {
1686 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1687 success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
1688 require_quiet(!success, done);
1689
1690 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1691
1692 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1693 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1694
1695 CFReleaseNull(account->my_identity);
1696 myPeer = NULL;
1697 }
1698 }
1699
fa7225c8 1700 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
5c19dc3a
A
1701
1702 require_quiet(success, done);
e0e0d90e 1703
5c19dc3a
A
1704 account->departure_code = kSOSNeverLeftCircle;
1705
1706done:
1707 return success;
1708}
1709
fa7225c8
A
1710bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1711 return SOSAccountJoinCircles_internal(aTxn, false, error);
5c19dc3a
A
1712}
1713
1714CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1715 CFStringRef result = NULL;
1716
1717 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1718
1719 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1720
1721fail:
1722 return result;
1723}
1724
1725bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
1726 bool result = true;
fa7225c8
A
1727
1728 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1729 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1730 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1731
1732 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
5c19dc3a 1733
fa7225c8
A
1734 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1735 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDSV2, error);
1736 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanFalse, error);
1737 SOSFullPeerInfoUpdateTransportFragmentationPreference(account->my_identity, kCFBooleanTrue, error);
5c19dc3a 1738
fa7225c8
A
1739 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1740 });
5c19dc3a 1741 }
fa7225c8
A
1742 else
1743 result = false;
1744
5c19dc3a
A
1745 SOSCCSyncWithAllPeers();
1746
1747fail:
fa7225c8
A
1748 CFReleaseNull(account->deviceID);
1749 account->deviceID = CFRetainSafe(IDS);
5c19dc3a
A
1750 return result;
1751}
1752
5c19dc3a
A
1753bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1754 bool result = true;
fa7225c8
A
1755 //construct message dictionary, circle -> peerID -> message
1756
1757 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1758 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1759
1760 char *messageCharStar;
1761 asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
1762 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1763
1764 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, messageString, kIDSMessageToSendKey, CFSTR("send IDS test message"), NULL);
1765
1766 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1767 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
e0e0d90e 1768 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
fa7225c8
A
1769 });
1770
1771 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1772 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1773
1774 CFReleaseNull(mutableDictionary);
1775 CFReleaseNull(peerToMessage);
1776 CFReleaseNull(circleToPeerMessages);
1777 CFReleaseNull(messageString);
1778 free(messageCharStar);
5c19dc3a
A
1779 return result;
1780}
1781
1782bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
e0e0d90e
A
1783 bool result = false;
1784 //construct message dictionary, circle -> peerID -> message
1785
1786 if(account->ids_message_transport == NULL)
1787 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1788
1789 require_quiet(account->ids_message_transport, fail);
1790 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1791 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1792
1793 char *messageCharStar;
1794 asprintf(&messageCharStar, "%d", kIDSStartPingTestMessage);
fa7225c8
A
1795 CFStringRef operationToString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1796 CFStringRef messageToSend = CFSTR("send IDS test message");
e0e0d90e 1797
fa7225c8
A
1798 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationToString, kIDSMessageToSendKey, messageToSend, NULL);
1799
e0e0d90e
A
1800
1801 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1802 if(CFStringCompare(SOSAccountGetMyPeerID(account), SOSPeerInfoGetPeerID(peer), 0) != 0)
1803 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1804 });
1805
1806 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1807 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1808
1809 CFReleaseNull(mutableDictionary);
1810 CFReleaseNull(peerToMessage);
1811 CFReleaseNull(circleToPeerMessages);
fa7225c8 1812 CFReleaseNull(operationToString);
e0e0d90e
A
1813 free(messageCharStar);
1814fail:
5c19dc3a
A
1815 return result;
1816}
1817
1818bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
1819 bool result = true;
fa7225c8
A
1820
1821 __block bool success = true;
1822 __block CFErrorRef localError = NULL;
1823 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1824 dispatch_retain(wait_for); // Both this scope and the block own it
1825
1826 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1827 success = (sync_error == NULL);
1828 if (!success) {
1829 CFRetainAssign(localError, sync_error);
1830 }
5c19dc3a 1831
fa7225c8 1832 dispatch_semaphore_signal(wait_for);
5c19dc3a 1833 dispatch_release(wait_for);
fa7225c8
A
1834 });
1835
1836 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1837 dispatch_release(wait_for);
1838
1839 if(!success && localError != NULL && error != NULL){
1840 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
1841 *error = localError;
1842 result = false;
1843 }
1844 else{
1845 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
5c19dc3a
A
1846 }
1847 return result;
1848}
1849
fa7225c8
A
1850bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1851 return SOSAccountJoinCircles_internal(aTxn, true, error);
5c19dc3a
A
1852}
1853
1854
1855bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1856{
1857 bool result = true;
1858
fa7225c8 1859 secnotice("leaveCircle", "Leaving circle by client request");
5c19dc3a
A
1860 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1861 return sosAccountLeaveCircle(account, circle, error);
1862 });
1863
1864 account->departure_code = kSOSWithdrewMembership;
1865
1866 return result;
1867}
1868
e0e0d90e
A
1869bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CFErrorRef* error)
1870{
1871 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1872 if (!user_key)
1873 return false;
1874
1875 bool result = true;
1876
1877 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1878
1879 bool leaveCircle = CFSetContainsValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1880
1881 CFSetRemoveValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1882
1883 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1884 bool success = false;
1885
e3d460c9
A
1886 if(CFSetGetCount(peersToRemove) != 0) {
1887 require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
1888 success = SOSAccountGenerationSignatureUpdate(account, error);
1889 } else success = true;
e0e0d90e 1890
e3d460c9 1891 if (success && leaveCircle) {
fa7225c8 1892 secnotice("leaveCircle", "Leaving circle by client request");
e0e0d90e 1893 success = sosAccountLeaveCircle(account, circle, error);
e0e0d90e
A
1894 }
1895
1896 done:
1897 return success;
1898
1899 });
1900
1901 return result;
1902}
1903
1904
5c19dc3a
A
1905bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
1906 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1907 dispatch_group_t group = dispatch_group_create();
1908 __block bool result = false;
1909 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1910 // Add a task to the group
1911 dispatch_group_async(group, queue, ^{
1912 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
fa7225c8 1913 secnotice("leaveCircle", "Leaving circle by client request");
5c19dc3a
A
1914 return sosAccountLeaveCircle(account, circle, error);
1915 });
1916 });
1917 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1918 dispatch_group_wait(group, milestone);
1919
1920 account->departure_code = kSOSWithdrewMembership;
1921
1922 dispatch_release(group);
1923 return result;
1924}
1925
1926
1927//
1928// MARK: Application
1929//
1930
1931static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
1932 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1933 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1934 CFErrorRef peer_error = NULL;
1935 if (account->trusted_circle && me &&
1936 SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
1937 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
1938 __block bool modified = false;
1939 CFArrayForEach(peer_infos, ^(const void *value) {
1940 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1941 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1942 if (action(circle, account->my_identity, peer)) {
1943 modified = true;
1944 }
1945 }
1946 });
1947 return modified;
1948 });
1949 }
1950 if (peer_error)
1951 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1952 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1953}
1954
1955bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1956 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1957 if (!user_key)
1958 return false;
1959
1960 __block bool success = true;
1961 __block int64_t num_peers = 0;
1962
1963 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1964 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1965 if (!accepted)
1966 success = false;
1967 else
1968 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1969 return accepted;
1970 });
1971
1972 return success;
1973}
1974
1975bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1976 __block bool success = true;
1977 __block int64_t num_peers = 0;
1978
1979 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1980 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1981 if (!rejected)
1982 success = false;
1983 else
1984 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1985 return rejected;
1986 });
1987
1988 return success;
1989}
1990
1991
1992CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1993 return CFSTR("We're compatible, go away");
1994}
1995
1996enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1997 return account->departure_code;
1998}
1999
2000void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
2001 account->departure_code = reason;
2002}
2003
2004
2005CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
2006 CFArrayRef result = NULL;
2007 CFNumberRef generation = NULL;
2008
2009 require_quiet(SOSAccountHasPublicKey(account, error), fail);
2010 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
2011
2012 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
2013 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
2014
2015fail:
2016 return result;
2017}
2018
2019bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
2020 if (!SOSAccountHasPublicKey(account, error))
2021 return NULL;
2022
2023 return account->user_public_trusted;
2024}
2025
2026bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
2027 // TODO: this result is never set or used
2028 bool result = true;
2029
2030 secnotice("updates", "Ensuring peer registration.");
2031
2032 require_quiet(account->trusted_circle, done);
2033 require_quiet(account->my_identity, done);
fa7225c8
A
2034 require_quiet(account->user_public_trusted, done);
2035
5c19dc3a
A
2036 // If we are not in the circle, there is no point in setting up peers
2037 require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
2038
2039 // This code only uses the SOSFullPeerInfoRef for two things:
2040 // - Finding out if this device is in the trusted circle
2041 // - Using the peerID for this device to see if the current peer is "me"
fa7225c8 2042 // - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder
5c19dc3a
A
2043
2044 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
2045
fa7225c8 2046 SOSCircleForEachValidSyncingPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
5c19dc3a
A
2047 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
2048 CFErrorRef localError = NULL;
2049 SOSTransportMessageRef messageTransport = NULL;
2050
fa7225c8 2051 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
5c19dc3a 2052
fa7225c8 2053 SOSEngineInitializePeerCoder(messageTransport->engine, account->my_identity, peer, &localError);
5c19dc3a
A
2054 if (localError)
2055 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
2056 CFReleaseSafe(localError);
2057 }
2058 });
2059
2060 //Initialize our device ID
e3d460c9
A
2061 SOSTransportMessageIDSGetIDSDeviceID(account);
2062
5c19dc3a
A
2063
2064done:
2065 return result;
2066}
2067
2068static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
2069 if (!account->expansion) {
2070 account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
2071 }
2072
2073 return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
2074}
2075
fa7225c8
A
2076//
2077// Value manipulation
2078//
2079
2080bool SOSAccountClearValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
5c19dc3a
A
2081 bool success = SOSAccountEnsureExpansion(account, error);
2082 require_quiet(success, errOut);
2083
2084 CFDictionaryRemoveValue(account->expansion, key);
2085errOut:
2086 return success;
2087}
2088
fa7225c8
A
2089bool SOSAccountSetValue(SOSAccountRef account, CFStringRef key, CFTypeRef value, CFErrorRef *error) {
2090 if (value == NULL) return SOSAccountClearValue(account, key, error);
2091
5c19dc3a
A
2092 bool success = SOSAccountEnsureExpansion(account, error);
2093 require_quiet(success, errOut);
2094
2095 CFDictionarySetValue(account->expansion, key, value);
2096errOut:
2097 return success;
2098}
2099
fa7225c8 2100CFTypeRef SOSAccountGetValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
5c19dc3a
A
2101 if (!account->expansion) {
2102 return NULL;
2103 }
2104 return CFDictionaryGetValue(account->expansion, key);
2105}
2106
e0e0d90e
A
2107bool SOSAccountAddEscrowRecords(SOSAccountRef account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
2108 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
2109 CFMutableDictionaryRef escrowCopied = NULL;
2110 bool success = false;
2111
2112 if(isDictionary(escrowRecords) && escrowRecords != NULL)
2113 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
2114 else
2115 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2116
2117 CFDictionaryAddValue(escrowCopied, dsid, record);
2118 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
2119
2120 if(*error == NULL)
2121 success = true;
2122
2123 CFReleaseNull(escrowCopied);
2124
2125 return success;
2126
2127}
2128
2129bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
2130 bool success = false;
2131
2132 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
2133 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
2134
2135 return success;
2136}
2137
2138bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
2139{
2140 CFMutableDictionaryRef circleToPeerMessages = NULL;
fa7225c8 2141 CFStringRef operationTypeAsString = NULL;
e0e0d90e
A
2142 CFMutableDictionaryRef mutableDictionary = NULL;
2143 CFMutableSetRef peers = NULL;
2144 CFMutableDictionaryRef peerList = NULL;
2145 char* message = NULL;
2146 bool result = false;
2147 if(account->ids_message_transport == NULL)
2148 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
2149
2150 require_quiet(account->ids_message_transport, fail);
2151 circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2152
2153 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
fa7225c8 2154
e0e0d90e 2155 asprintf(&message, "%d", kIDSPeerAvailability);
fa7225c8 2156 operationTypeAsString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
e0e0d90e 2157
fa7225c8 2158 mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationTypeAsString, kIDSMessageToSendKey, CFSTR("checking peers"), NULL);
e0e0d90e
A
2159
2160 //make sure there are peers in the circle
2161 peers = SOSCircleCopyPeers(account->trusted_circle, kCFAllocatorDefault);
2162 require_quiet(CFSetGetCount(peers) > 0, fail);
2163 CFReleaseNull(peers);
2164
2165 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2166 SOSCircleRef circle = account->trusted_circle;
2167
2168 //check each peer to make sure they have the right view set enabled
2169 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
2170 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
2171 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account))){
2172 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
2173 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
2174 if(CFEqualSafe(intersectSets, mySubSet)){
2175 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
2176 if(deviceID != NULL)
2177 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), mutableDictionary);
2178 CFReleaseNull(deviceID);
2179 }
2180 CFReleaseNull(peerViews);
2181 CFReleaseNull(intersectSets);
2182 }
2183 });
e3d460c9 2184
e0e0d90e
A
2185 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
2186 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerList);
2187 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
fa7225c8 2188
e0e0d90e
A
2189fail:
2190 CFReleaseNull(mutableDictionary);
fa7225c8 2191 CFReleaseNull(operationTypeAsString);
e0e0d90e
A
2192 CFReleaseNull(peerList);
2193 CFReleaseNull(circleToPeerMessages);
2194 CFReleaseNull(peers);
2195 free(message);
2196 return result;
2197}
2198
2199
fa7225c8 2200void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
e0e0d90e
A
2201 if (!SOSAccountIsInCircle(account, NULL))
2202 return;
2203
2204 SOSAccountModifyCircle(account, NULL, ^bool (SOSCircleRef circle) {
2205 __block bool updated = false;
2206 CFSetForEach(account->retirees, ^(CFTypeRef element){
2207 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
2208
2209 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
2210 updated = true;
2211 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
2212 CFErrorRef cleanupError = NULL;
2213 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
2214 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
2215 CFReleaseSafe(cleanupError);
2216 }
2217 });
2218 return updated;
2219 });
2220}
2221
e0e0d90e 2222
fa7225c8
A
2223static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error) {
2224 size_t total_payload = 0;
2225
2226 CFDataRef publicBytes = NULL;
2227 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
2228
2229 if (result != errSecSuccess) {
2230 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
2231 return 0;
5c19dc3a 2232 }
fa7225c8
A
2233
2234 require_quiet(accumulate_size(&total_payload, der_sizeof_number(gencount, error)), errOut);
2235 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(publicBytes, error)), errOut);
2236 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(signature, error)), errOut);
2237 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
e3d460c9 2238
fa7225c8
A
2239errOut:
2240 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
2241 return 0;
2242}
5c19dc3a 2243
fa7225c8
A
2244static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
2245 CFDataRef publicBytes = NULL;
2246
2247 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
2248
2249 if (result != errSecSuccess) {
2250 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
2251 return NULL;
2252 }
2253
2254
2255 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2256 der_encode_number(gencount, error, der,
2257 der_encode_data_or_null(publicBytes, error, der,
2258 der_encode_data_or_null(signature, error, der, der_end))));
2259 return der_end;
5c19dc3a
A
2260}
2261
fa7225c8
A
2262static CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFAllocatorRef allocator, CFErrorRef *error)
2263{
2264 return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
2265 return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
2266 });
2267}
2268
2269struct piggyBackBlob {
2270 SOSGenCountRef gencount;
2271 SecKeyRef pubKey;
2272 CFDataRef signature;
2273};
2274
2275static struct piggyBackBlob *SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator, CFErrorRef *error,
2276 const uint8_t** der_p, const uint8_t *der_end) {
2277 const uint8_t *sequence_end;
2278 struct piggyBackBlob *retval = NULL;
2279 SOSGenCountRef gencount = NULL;
2280 CFDataRef signature = NULL;
2281 CFDataRef publicBytes = NULL;
2282
2283 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
2284 require_action_quiet(sequence_end != NULL, errOut,
2285 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
2286 *der_p = der_decode_number(allocator, 0, &gencount, error, *der_p, sequence_end);
2287 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, der_end);
2288 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, der_end);
2289 require_action_quiet(*der_p && *der_p == der_end, errOut,
2290 SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
2291 retval = malloc(sizeof(struct piggyBackBlob));
2292 retval->gencount = gencount;
2293 retval->signature = signature;
2294 retval->pubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
2295
2296errOut:
2297 if(!retval) {
2298 CFReleaseNull(gencount);
2299 CFReleaseNull(publicBytes);
2300 CFReleaseNull(signature);
2301 }
2302 return retval;
2303}
2304
2305static struct piggyBackBlob *SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator, CFDataRef blobData, CFErrorRef *error)
2306{
2307 size_t size = CFDataGetLength(blobData);
2308 const uint8_t *der = CFDataGetBytePtr(blobData);
2309 struct piggyBackBlob *inflated = SOSPiggyBackBlobCreateFromDER(allocator, error, &der, der + size);
2310 return inflated;
2311}
2312
2313
2314
2315SOSPeerInfoRef SOSAccountCopyApplication(SOSAccountRef account, CFErrorRef* error) {
2316 SOSPeerInfoRef applicant = NULL;
2317 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2318 if(!userKey) return false;
2319 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), errOut);
2320 require(SOSFullPeerInfoPromoteToApplication(account->my_identity, userKey, error), errOut);
2321 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, (SOSFullPeerInfoGetPeerInfo(account->my_identity)), error);
2322errOut:
2323 return applicant;
2324}
2325
2326
2327CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccountRef account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2328 SOSGenCountRef gencount = NULL;
2329 CFDataRef signature = NULL;
2330 SecKeyRef ourKey = NULL;
2331
2332 CFDataRef pbblob = NULL;
2333
2334 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2335 require_quiet(userKey, errOut);
2336
2337 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2338 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2339
2340 {
2341 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
2342 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2343 require_quiet(ourKey, errOut);
2344 }
2345
2346 SOSCircleRef currentCircle = SOSAccountGetCircle(account, error);
2347 require_quiet(currentCircle, errOut);
2348
2349 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2350 require_quiet(prunedCircle, errOut);
2351 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2352
2353 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2354
2355 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2356 require_quiet(signature, errOut);
2357
2358 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, kCFAllocatorDefault, error);
2359
2360errOut:
2361 CFReleaseNull(gencount);
2362 CFReleaseNull(signature);
2363 CFReleaseNull(ourKey);
2364
2365 return pbblob;
2366}
2367
2368bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account, CFDataRef joiningBlob, CFErrorRef *error) {
2369 bool retval = false;
2370 SecKeyRef userKey = NULL;
2371 struct piggyBackBlob *pbb = NULL;
2372
2373 userKey = SOSAccountGetPrivateCredential(account, error);
2374 require_quiet(userKey, errOut);
2375 pbb = SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault, joiningBlob, error);
2376 require_quiet(pbb, errOut);
2377
2378 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2379
2380 retval = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef copyOfCurrent) {
2381 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2382 pbb->gencount,
2383 pbb->pubKey,
2384 pbb->signature,
2385 account->my_identity, error);;
2386
2387 });
2388
2389errOut:
2390 if(pbb) {
2391 CFReleaseNull(pbb->gencount);
2392 CFReleaseNull(pbb->pubKey);
2393 CFReleaseNull(pbb->signature);
2394 free(pbb);
2395 }
2396 return retval;
2397}
2398
2399static char boolToChars(bool val, char truechar, char falsechar) {
2400 return val? truechar: falsechar;
2401}
2402
2403#define ACCOUNTLOGSTATE "accountLogState"
2404void SOSAccountLogState(SOSAccountRef account) {
2405 bool hasPubKey = account->user_public != NULL;
2406 bool pubTrusted = account->user_public_trusted;
2407 bool hasPriv = account->_user_private != NULL;
2408 SOSCCStatus stat = SOSAccountGetCircleStatus(account, NULL);
2409 CFStringRef userPubKeyID = (account->user_public) ? SOSCopyIDOfKeyWithLength(account->user_public, 8, NULL):
2410 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2411
2412 secnotice(ACCOUNTLOGSTATE, "Start");
2413
2414 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2415 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2416 userPubKeyID,
2417 SOSAccountGetSOSCCStatusString(stat)
2418 );
2419 CFReleaseNull(userPubKeyID);
2420 if(account->trusted_circle) SOSCircleLogState(ACCOUNTLOGSTATE, account->trusted_circle, account->user_public, SOSAccountGetMyPeerID(account));
2421 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2422}
2423
2424void SOSAccountLogViewState(SOSAccountRef account) {
2425 bool isInCircle = SOSAccountIsInCircle(account, NULL);
2426 require_quiet(isInCircle, imOut);
2427 SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
2428 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2429 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2430
2431 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2432 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2433 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2434 boolToChars(isInitialComplete, 'I', 'i'),
2435 boolToChars(isBackupComplete, 'B', 'b'),
2436 description);
2437 });
2438 CFReleaseNull(views);
2439 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2440 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2441 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2442 });
2443 CFReleaseNull(unsyncedViews);
2444
2445imOut:
2446 secnotice(ACCOUNTLOGSTATE, "Finish");
2447
2448 return;
2449}