2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
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>
26 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
27 #include <Security/SecItemInternal.h>
28 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
29 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
31 #include <utilities/SecCFWrappers.h>
33 CFGiblisWithCompareFor(SOSAccount
);
35 const CFStringRef SOSTransportMessageTypeIDS
= CFSTR("IDS");
36 const CFStringRef SOSTransportMessageTypeIDSV2
= CFSTR("IDS2.0");
37 const CFStringRef SOSTransportMessageTypeKVS
= CFSTR("KVS");
38 const CFStringRef kSOSDSIDKey
= CFSTR("AccountDSID");
39 const CFStringRef kSOSEscrowRecord
= CFSTR("EscrowRecord");
40 const CFStringRef kSOSUnsyncedViewsKey
= CFSTR("unsynced");
41 const CFStringRef kSOSPendingEnableViewsToBeSetKey
= CFSTR("pendingEnableViews");
42 const CFStringRef kSOSPendingDisableViewsToBeSetKey
= CFSTR("pendingDisableViews");
45 #define DATE_LENGTH 25
46 const CFStringRef kSOSAccountDebugScope
= CFSTR("Scope");
48 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
)
51 CFStringRef circle_name
= NULL
;
53 require_quiet(a
, xit
);
54 require_quiet(a
->factory
, xit
);
56 circle_name
= SOSDataSourceFactoryCopyName(a
->factory
);
57 require(circle_name
, xit
);
59 SOSAccountEnsureCircle(a
, circle_name
, NULL
);
64 // We don't own name, so don't release it.
65 CFReleaseNull(circle_name
);
70 SOSAccountRef
SOSAccountCreateBasic(CFAllocatorRef allocator
,
71 CFDictionaryRef gestalt
,
72 SOSDataSourceFactoryRef factory
) {
73 SOSAccountRef a
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
);
75 a
->queue
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
);
77 a
->gestalt
= CFRetainSafe(gestalt
);
79 a
->trusted_circle
= NULL
;
80 a
->backups
= CFDictionaryCreateMutableForCFTypes(allocator
);
81 a
->my_identity
= NULL
;
82 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
84 a
->factory
= factory
; // We adopt the factory. kthanksbai.
86 a
->isListeningForSync
= false;
88 a
->_user_private
= NULL
;
89 a
->_password_tmp
= NULL
;
90 a
->user_private_timer
= NULL
;
91 a
->lock_notification_token
= NOTIFY_TOKEN_INVALID
;
93 a
->change_blocks
= CFArrayCreateMutableForCFTypes(allocator
);
94 a
->waitForInitialSync_blocks
= NULL
;
95 a
->departure_code
= kSOSNeverAppliedToCircle
;
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
);
102 SOSAccountAddRingDictionary(a
);
105 a
->circle_rings_retirements_need_attention
= false;
106 a
->engine_peer_state_needs_repair
= false;
107 a
->key_interests_need_updating
= false;
114 // MARK: Transactional
117 void SOSAccountWithTransaction_Locked(SOSAccountRef account
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) {
118 SOSAccountTransactionRef at
= SOSAccountTransactionCreate(account
);
120 SOSAccountTransactionFinish(at
);
126 void SOSAccountWithTransaction(SOSAccountRef account
, bool sync
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) {
127 dispatch_block_t with_transaction
= ^{
128 SOSAccountWithTransaction_Locked(account
, action
);
132 dispatch_sync(SOSAccountGetQueue(account
), with_transaction
);
134 dispatch_async(SOSAccountGetQueue(account
), with_transaction
);
138 void SOSAccountWithTransactionSync(SOSAccountRef account
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) {
139 SOSAccountWithTransaction(account
, true, action
);
142 void SOSAccountWithTransactionAsync(SOSAccountRef account
, bool sync
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) {
143 SOSAccountWithTransaction(account
, false, action
);
150 void SOSAccountSetSaveBlock(SOSAccountRef account
, SOSAccountSaveBlock saveBlock
) {
151 CFAssignRetained(account
->saveBlock
, Block_copy(saveBlock
));
154 void SOSAccountFlattenToSaveBlock(SOSAccountRef account
) {
155 if (account
->saveBlock
) {
156 CFErrorRef localError
= NULL
;
157 CFDataRef saveData
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &localError
);
159 (account
->saveBlock
)(saveData
, localError
);
161 CFReleaseNull(saveData
);
162 CFReleaseNull(localError
);
167 // MARK: Security Properties
170 SOSSecurityPropertyResultCode
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
);
177 if(actionCode
== kSOSCCSecurityPropertyEnable
&& retval
== kSOSCCSecurityPropertyValid
) {
179 } else if(actionCode
== kSOSCCSecurityPropertyDisable
&& retval
== kSOSCCSecurityPropertyNotValid
) {
181 } else if(actionCode
== kSOSCCSecurityPropertyPending
) {
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
));
196 SOSSecurityPropertyResultCode
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
);
205 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
)
207 if (CFEqualSafe(new_gestalt
, account
->gestalt
))
210 if (account
->trusted_circle
&& account
->my_identity
211 && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) {
212 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
213 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
214 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
218 CFRetainAssign(account
->gestalt
, new_gestalt
);
222 static bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){
223 SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
);
224 //send new DSID over account changed
225 SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
);
229 void 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
);
234 SOSAccountUpdateDSID(account
, dsid
);
235 } else if(CFStringCompare(dsid
, accountDSID
, 0) != kCFCompareEqualTo
) {
236 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID
, dsid
);
238 //DSID has changed, blast the account!
239 SOSAccountSetToNew(account
);
241 //update DSID to the new DSID
242 SOSAccountUpdateDSID(account
, dsid
);
244 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID
, dsid
);
248 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
, CFSetRef excludedViews
) {
249 if (account
->trusted_circle
&& account
->my_identity
) {
250 if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
, excludedViews
)) {
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
));
261 static bool SOSAccountValueSetContainsValue(SOSAccountRef account
, CFStringRef key
, CFTypeRef value
) {
262 CFSetRef foundSet
= asSet(SOSAccountGetValue(account
, key
, NULL
), NULL
);
263 return foundSet
&& CFSetContainsValue(foundSet
, value
);
266 static void SOSAccountValueUnionWith(SOSAccountRef account
, CFStringRef key
, CFSetRef valuesToUnion
) {
267 CFMutableSetRef unionedSet
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, valuesToUnion
);
268 CFSetRef foundSet
= asSet(SOSAccountGetValue(account
, key
, NULL
), NULL
);
270 CFSetUnion(unionedSet
, foundSet
);
272 SOSAccountSetValue(account
, key
, unionedSet
, NULL
);
273 CFReleaseNull(unionedSet
);
276 static void SOSAccountValueSubtractFrom(SOSAccountRef account
, CFStringRef key
, CFSetRef valuesToSubtract
) {
277 CFSetRef foundSet
= asSet(SOSAccountGetValue(account
, key
, NULL
), NULL
);
279 CFMutableSetRef subtractedSet
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, foundSet
);
280 CFSetSubtract(subtractedSet
, valuesToSubtract
);
281 SOSAccountSetValue(account
, key
, subtractedSet
, NULL
);
282 CFReleaseNull(subtractedSet
);
286 void SOSAccountPendEnableViewSet(SOSAccountRef account
, CFSetRef enabledViews
)
288 if(CFSetGetValue(enabledViews
, kSOSViewKeychainV0
) != NULL
) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
290 SOSAccountValueUnionWith(account
, kSOSPendingEnableViewsToBeSetKey
, enabledViews
);
291 SOSAccountValueSubtractFrom(account
, kSOSPendingDisableViewsToBeSetKey
, enabledViews
);
295 void SOSAccountPendDisableViewSet(SOSAccountRef account
, CFSetRef disabledViews
)
297 SOSAccountValueUnionWith(account
, kSOSPendingDisableViewsToBeSetKey
, disabledViews
);
298 SOSAccountValueSubtractFrom(account
, kSOSPendingEnableViewsToBeSetKey
, disabledViews
);
301 static 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
;
309 require_action_quiet(actionCode
== kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now"));
310 retval
= kSOSCCViewNotMember
;
317 SOSViewResultCode
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
318 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
319 SOSViewResultCode currentStatus
= kSOSCCGeneralViewError
;
320 bool alreadyInSync
= SOSAccountHasCompletedInitialSync(account
);
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"));
329 if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) {
330 retval
= SOSAccountVirtualV0Behavior(account
, actionCode
);
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
;
336 CFMutableSetRef pendingSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
337 CFSetAddValue(pendingSet
, viewname
);
339 if(actionCode
== kSOSCCViewEnable
&& currentStatus
== kSOSCCViewNotMember
) {
341 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
342 if(retval
== kSOSCCViewMember
) updateCircle
= true;
344 SOSAccountPendEnableViewSet(account
, pendingSet
);
345 retval
= kSOSCCViewMember
;
346 updateCircle
= false;
348 } else if(actionCode
== kSOSCCViewDisable
&& currentStatus
== kSOSCCViewMember
) {
350 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
351 if(retval
== kSOSCCViewNotMember
) updateCircle
= true;
353 SOSAccountPendDisableViewSet(account
, pendingSet
);
354 retval
= kSOSCCViewNotMember
;
355 updateCircle
= false;
358 retval
= currentStatus
;
361 CFReleaseNull(pendingSet
);
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
));
375 SOSViewResultCode
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
));
380 if (SOSAccountValueSetContainsValue(account
, kSOSPendingEnableViewsToBeSetKey
, viewname
)) {
381 retval
= kSOSCCViewMember
;
382 } else if (SOSAccountValueSetContainsValue(account
, kSOSPendingDisableViewsToBeSetKey
, viewname
)) {
383 retval
= kSOSCCViewNotMember
;
385 retval
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
);
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
;
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
);
400 retval
= kSOSCCViewPending
;
408 static void dumpViewSet(CFStringRef label
, CFSetRef views
) {
410 CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) {
411 secnotice("circleChange", "%@ list: %@", label
, description
);
414 secnotice("circleChange", "No %@ list provided.", label
);
418 static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account
, CFMutableSetRef viewSet
, SOSViewActionCode actionCode
) {
420 if(viewSet
&& CFSetContainsValue(viewSet
, kSOSViewKeychainV0
)) {
421 retval
= SOSAccountVirtualV0Behavior(account
, actionCode
) != kSOSCCGeneralViewError
;
422 CFSetRemoveValue(viewSet
, kSOSViewKeychainV0
);
427 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef origEnabledViews
, CFSetRef origDisabledViews
) {
429 bool updateCircle
= false;
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
);
435 dumpViewSet(CFSTR("Enabled"), enabledViews
);
436 dumpViewSet(CFSTR("Disabled"), disabledViews
);
438 require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle"));
440 // Make sure we have a peerInfo capable of supporting views.
441 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
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"));
445 pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
);
447 require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef"));
449 if(!SOSPeerInfoVersionIsCurrent(pi
)) {
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");
457 CFStringSetPerformWithDescription(enabledViews
, ^(CFStringRef description
) {
458 secnotice("viewChange", "Enabling %@", description
);
461 CFStringSetPerformWithDescription(disabledViews
, ^(CFStringRef description
) {
462 secnotice("viewChange", "Disabling %@", description
);
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"));
468 if(SOSAccountHasCompletedInitialSync(account
)) {
469 if(enabledViews
) updateCircle
|= SOSViewSetEnable(pi
, enabledViews
);
470 if(disabledViews
) updateCircle
|= SOSViewSetDisable(pi
, disabledViews
);
473 //hold on to the views and enable them later
474 if(enabledViews
) SOSAccountPendEnableViewSet(account
, enabledViews
);
475 if(disabledViews
) SOSAccountPendDisableViewSet(account
, disabledViews
);
480 /* UPDATE FULLPEERINFO VIEWS */
481 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
), errOut
);
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
));
488 // Make sure we update the engine
489 account
->circle_rings_retirements_need_attention
= true;
493 CFReleaseNull(enabledViews
);
494 CFReleaseNull(disabledViews
);
500 SOSAccountRef
SOSAccountCreate(CFAllocatorRef allocator
,
501 CFDictionaryRef gestalt
,
502 SOSDataSourceFactoryRef factory
) {
503 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
505 SOSAccountEnsureFactoryCircles(a
);
507 a
->key_interests_need_updating
= true;
512 static void SOSAccountDestroy(CFTypeRef aObj
) {
513 SOSAccountRef a
= (SOSAccountRef
) aObj
;
515 // We don't own the factory, merely have a reference to the singleton
519 SOSAccountCancelSyncChecking(a
);
521 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
);
524 SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
);
526 dispatch_sync(a
->queue
, ^{
527 CFReleaseNull(a
->gestalt
);
529 CFReleaseNull(a
->my_identity
);
530 CFReleaseNull(a
->trusted_circle
);
531 CFReleaseNull(a
->backups
);
532 CFReleaseNull(a
->retirees
);
534 a
->user_public_trusted
= false;
535 CFReleaseNull(a
->user_public
);
536 CFReleaseNull(a
->user_key_parameters
);
538 SOSAccountPurgePrivateCredential(a
);
539 CFReleaseNull(a
->previous_public
);
540 CFReleaseNull(a
->_user_private
);
541 CFReleaseNull(a
->_password_tmp
);
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
);
550 dispatch_release(a
->user_private_timer
);
551 CFReleaseNull(a
->change_blocks
);
552 CFReleaseNull(a
->waitForInitialSync_blocks
);
553 CFReleaseNull(a
->expansion
);
555 CFReleaseNull(a
->saveBlock
);
556 CFReleaseNull(a
->deviceID
);
560 static OSStatus
do_delete(CFDictionaryRef query
) {
563 result
= SecItemDelete(query
);
565 secerror("SecItemDelete: %d", (int)result
);
571 do_keychain_delete_aks_bags()
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
,
583 result
= do_delete(item
);
590 do_keychain_delete_identities()
593 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
594 kSecClass
, kSecClassKey
,
595 kSecAttrSynchronizable
, kCFBooleanTrue
,
596 kSecUseTombstones
, kCFBooleanFalse
,
597 kSecAttrAccessGroup
, CFSTR("com.apple.security.sos"),
600 result
= do_delete(item
);
607 do_keychain_delete_lakitu()
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"),
619 result
= do_delete(item
);
626 do_keychain_delete_sbd()
629 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
630 kSecClass
, kSecClassGenericPassword
,
631 kSecAttrSynchronizable
, kCFBooleanTrue
,
632 kSecUseTombstones
, kCFBooleanFalse
,
633 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
636 result
= do_delete(item
);
642 void 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
) {
646 secerror("Error updating keys: %@", error
);
649 CFReleaseNull(emptyDictionary
);
652 void SOSAccountSetToNew(SOSAccountRef a
) {
653 secnotice("accountChange", "Setting Account to New");
656 CFReleaseNull(a
->my_identity
);
657 CFReleaseNull(a
->trusted_circle
);
658 CFReleaseNull(a
->backups
);
659 CFReleaseNull(a
->retirees
);
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
);
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
);
672 CFReleaseNull(a
->deviceID
);
674 /* remove all syncable items */
675 result
= do_keychain_delete_aks_bags(); (void) result
;
676 secdebug("set to new", "result for deleting aks bags: %d", result
);
678 result
= do_keychain_delete_identities(); (void) result
;
679 secdebug("set to new", "result for deleting identities: %d", result
);
681 result
= do_keychain_delete_lakitu(); (void) result
;
682 secdebug("set to new", "result for deleting lakitu: %d", result
);
684 result
= do_keychain_delete_sbd(); (void) result
;
685 secdebug("set to new", "result for deleting sbd: %d", result
);
687 a
->user_public_trusted
= false;
688 a
->departure_code
= kSOSNeverAppliedToCircle
;
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();
697 if (a
->lock_notification_token
!= NOTIFY_TOKEN_INVALID
) {
698 notify_cancel(a
->lock_notification_token
);
699 a
->lock_notification_token
= NOTIFY_TOKEN_INVALID
;
706 // update_interest_block;
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
;
714 a
->backups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
716 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
717 a
->expansion
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
718 SOSAccountAddRingDictionary(a
);
720 SOSAccountEnsureFactoryCircles(a
); // Does rings too
722 // Reset our key interests since we are new, we need to hear about everything:
723 SOSAccountResetKeyInterests(a
);
725 a
->key_interests_need_updating
= true;
728 bool SOSAccountIsNew(SOSAccountRef account
, CFErrorRef
*error
){
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
);
742 static CFStringRef
SOSAccountCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
743 SOSAccountRef a
= (SOSAccountRef
) aObj
;
745 CFStringRef gestaltDescription
= CFDictionaryCopyCompactDescription(a
->gestalt
);
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
);
755 CFReleaseNull(gestaltDescription
);
760 CFStringRef
SOSAccountCreateCompactDescription(SOSAccountRef a
) {
762 CFStringRef gestaltDescription
= CFDictionaryCopySuperCompactDescription(a
->gestalt
);
764 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), gestaltDescription
);
766 CFReleaseNull(gestaltDescription
);
771 static Boolean
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
)
773 SOSAccountRef laccount
= (SOSAccountRef
) lhs
;
774 SOSAccountRef raccount
= (SOSAccountRef
) rhs
;
776 return CFEqualSafe(laccount
->gestalt
, raccount
->gestalt
)
777 && CFEqualSafe(laccount
->trusted_circle
, raccount
->trusted_circle
)
778 && CFEqualSafe(laccount
->expansion
, raccount
->expansion
)
779 && CFEqualSafe(laccount
->my_identity
, raccount
->my_identity
);
782 dispatch_queue_t
SOSAccountGetQueue(SOSAccountRef account
) {
783 return account
->queue
;
786 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account
){
787 account
->user_public_trusted
= true;
790 SOSFullPeerInfoRef
SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
)
792 return CFRetainSafe(account
->my_identity
);
795 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account
) {
797 __block CFErrorRef error
= NULL
;
799 if (!SOSAccountHasPublicKey(account
, &error
)) {
800 CFReleaseSafe(error
);
806 require_action_quiet(account
->my_identity
, xit
,
807 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account identity not set"), NULL
, &error
));
809 SOSTransportMessageIDSGetIDSDeviceID(account
);
811 require_action_quiet(account
->trusted_circle
, xit
,
812 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account trusted circle not set"), NULL
, &error
));
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
);
820 secerror("sync with device failure: %@", error
);
822 CFReleaseSafe(error
);
826 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef peerID
) {
827 SOSPeerInfoRef mypi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
828 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(mypi
);
830 return myPeerID
&& CFEqualSafe(myPeerID
, peerID
);
833 bool SOSAccountSendIKSPSyncList(SOSAccountRef account
, CFErrorRef
*error
){
835 __block CFErrorRef localError
= NULL
;
836 __block CFMutableArrayRef ids
= NULL
;
837 SOSCircleRef circle
= NULL
;
839 require_action_quiet(SOSAccountIsInCircle(account
, NULL
), xit
,
840 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device is not in circle"),
843 circle
= SOSAccountGetCircle(account
, error
);
844 ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
846 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
847 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
850 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
851 if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) {
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
);
859 CFReleaseNull(deviceID
);
863 require_quiet(CFArrayGetCount(ids
) != 0, xit
);
864 secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids
);
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
);
869 secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error
);
872 if(error
&& *error
!= NULL
)
873 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error
);
876 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError
);
879 CFReleaseNull(localError
);
884 bool SOSAccountSyncWithAllKVSPeers(SOSAccountRef account
, CFErrorRef
*error
)
886 __block
bool result
= true;
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
);
895 secnotice("KVS Transport", "synced with peer: %@", SOSPeerInfoGetPeerID(peer
));
898 secnotice("KVS Transport", "failed to sync with peer: %@", SOSPeerInfoGetPeerID(peer
));
901 CFReleaseNull(deviceID
);
905 secnotice("sync", "SOSAccountSyncWithAllKVSPeers returns: %d", result
);
909 static CFMutableArrayRef
SOSAccountCopyPeerIDsForDSID(SOSAccountRef account
, CFStringRef deviceID
, CFErrorRef
* error
) {
910 CFMutableArrayRef peerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
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
));
917 CFReleaseNull(peerDeviceID
);
920 if (peerIDs
== NULL
|| CFArrayGetCount(peerIDs
) == 0) {
921 CFReleaseNull(peerIDs
);
922 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer with DSID: %@"), deviceID
);
928 static bool SOSAccountSyncWithKVSPeers(SOSAccountRef account
, CFArrayRef peerIDs
, CFErrorRef
*error
) {
929 CFDictionaryRef circleToPeerIDs
= NULL
;
931 CFErrorRef localError
= NULL
;
933 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
934 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
937 circleToPeerIDs
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
938 SOSCircleGetName(account
->trusted_circle
), peerIDs
,
940 result
= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToPeerIDs
, &localError
);
942 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->kvs_message_transport
);
943 result
&= SOSEngineSyncWithPeers(engine
, &localError
);
946 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
949 CFReleaseNull(circleToPeerIDs
);
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;
956 CFErrorPropagate(localError
, error
);
963 bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account
, CFStringRef deviceID
, CFErrorRef
*error
) {
965 CFErrorRef localError
= NULL
;
967 secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID
);
969 CFArrayRef peerIDs
= SOSAccountCopyPeerIDsForDSID(account
, deviceID
, &localError
);
970 require_quiet(peerIDs
, xit
);
972 CFStringArrayPerfromWithDescription(peerIDs
, ^(CFStringRef peerIDList
) {
973 secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList
);
976 result
= SOSAccountSyncWithKVSPeers(account
, peerIDs
, &localError
);
977 secerror("KVS sync %s. (%@)", result
? "succeeded" : "failed", localError
);
980 CFReleaseNull(peerIDs
);
981 CFErrorPropagate(localError
, error
);
986 bool SOSAccountSyncWithKVSPeer(SOSAccountRef account
, CFStringRef peerID
, CFErrorRef
*error
)
989 CFErrorRef localError
= NULL
;
991 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID
);
993 CFArrayRef peerIDs
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, peerID
, NULL
);
995 result
= SOSAccountSyncWithKVSPeers(account
, peerIDs
, &localError
);
996 secerror("KVS sync %s. (%@)", result
? "succeeded" : "failed", localError
);
998 CFReleaseNull(peerIDs
);
999 CFErrorPropagate(localError
, error
);
1004 #define LOG_ENGINE_STATE_INTERVAL 20
1006 bool SOSAccountSyncWithIDSPeer(SOSAccountRef account
, CFStringRef deviceID
, CFErrorRef
*error
)
1008 CFErrorRef localError
= NULL
;
1009 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1010 CFMutableArrayRef ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1011 static int engineLogCountDown
= 0;
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
));
1022 CFReleaseNull(peerDeviceID
);
1025 require_action_quiet(CFArrayGetCount(ids
), xit
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("Cannot find peer in circle"),
1026 NULL
, &localError
));
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
);
1033 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->ids_message_transport
);
1034 result
&= SOSEngineSyncWithPeers(engine
, &localError
);
1037 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
1039 if(engineLogCountDown
<= 0) {
1040 SOSEngineLogState(engine
);
1041 engineLogCountDown
= LOG_ENGINE_STATE_INTERVAL
;
1043 engineLogCountDown
--;
1047 CFReleaseNull(circleToPeerIDs
);
1050 secdebug("Account", "Could not sync with peer %@, error: %@", deviceID
, localError
);
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");
1054 account
->engine_peer_state_needs_repair
= true;
1056 CFErrorPropagate(localError
, error
);
1060 CFReleaseSafe(localError
);
1067 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
1068 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
1070 bool success
= true;
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
);
1076 CFStringRef cleanupPeerID
= SOSPeerInfoGetPeerID(cleanupPeer
);
1078 CFStringRef circle_name
= SOSCircleGetName(circle
);
1080 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1081 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
);
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
);
1088 if (account
->ids_message_transport
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) {
1089 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
1092 CFReleaseNull(localError
);
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
);
1099 CFReleaseNull(localError
);
1100 CFReleaseNull(circleToPeerIDs
);
1106 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
1107 CFMutableSetRef retirees_to_remove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
1109 __block
bool success
= true;
1111 CFSetForEach(account
->retirees
, ^(const void *value
) {
1112 SOSPeerInfoRef retiree
= (SOSPeerInfoRef
) value
;
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
);
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
);
1129 CFReleaseNull(retirees_to_remove
);
1131 CFDictionaryRef retirements_to_remove
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1132 SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
,
1135 CFReleaseNull(retirees_to_cleanup
);
1137 success
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
);
1139 CFReleaseNull(retirements_to_remove
);
1144 bool 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
);
1151 CFReleaseSafe(cleanupError
);
1156 SOSCircleRef
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef
*error
) {
1157 SOSCircleRef new_circle
= SOSCircleCopyCircle(NULL
, starting_circle
, error
);
1158 SOSFullPeerInfoRef meFull
= SOSAccountGetMyFullPeerInfo(account
);
1159 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(meFull
);
1160 bool iAmApplicant
= me
&& SOSCircleHasApplicant(new_circle
, me
, NULL
);
1162 if(!new_circle
) return NULL
;
1163 __block
bool workDone
= false;
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
);
1174 if(workDone
&& SOSCircleCountPeers(new_circle
) == 0) {
1175 SecKeyRef userPrivKey
= SOSAccountGetPrivateCredential(account
, error
);
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
);
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
);
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
);
1204 // MARK: Circle Membership change notificaion
1207 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
1208 SOSAccountCircleMembershipChangeBlock copy
= Block_copy(changeBlock
);
1209 CFArrayAppendValue(a
->change_blocks
, copy
);
1210 CFReleaseNull(copy
);
1213 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
1214 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
1217 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) {
1218 if (!changeBlock
) return;
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
) {
1225 if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
))
1228 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
);
1229 CFStringRef myPi_id
= myPi
? SOSPeerInfoGetPeerID(myPi
) : NULL
;
1231 CFMutableArrayRef peer_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1232 CFMutableArrayRef added_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1233 CFMutableArrayRef removed_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1235 if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) {
1236 SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) {
1237 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
);
1240 CFSetForEach(added_peers
, ^(const void *value
) {
1241 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
1244 CFSetForEach(removed_peers
, ^(const void *value
) {
1245 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
1249 if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
))
1250 changeBlock(peer_ids
, added_ids
, removed_ids
);
1252 CFReleaseSafe(peer_ids
);
1253 CFReleaseSafe(added_ids
);
1254 CFReleaseSafe(removed_ids
);
1257 CFRetainSafe(changeBlock
);
1258 SOSAccountAddChangeBlock(a
, block_to_register
);
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
);
1264 CFReleaseSafe(empty
);
1267 void 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
);
1274 CFReleaseNull(purgeError
);
1276 CFReleaseNull(account
->my_identity
);
1280 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
1281 SOSFullPeerInfoRef fpi
= account
->my_identity
;
1282 if(!fpi
) return false;
1284 CFErrorRef localError
= NULL
;
1286 bool retval
= false;
1288 SOSPeerInfoRef retire_peer
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
);
1290 secerror("Create ticket failed for peer %@: %@", fpi
, localError
);
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
);
1302 CFReleaseSafe(cleanupError
);
1306 // Store the retirement record locally.
1307 CFSetAddValue(account
->retirees
, retire_peer
);
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
);
1314 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){
1315 secwarning("Couldn't flush retirement data (%@)", postError
);
1317 CFReleaseNull(postError
);
1320 SOSAccountPurgeIdentity(account
);
1324 CFReleaseNull(localError
);
1325 CFReleaseNull(retire_peer
);
1329 bool 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
);
1335 CFErrorRef localError
= NULL
;
1337 bool retval
= false;
1338 bool writeRing
= false;
1339 bool writePeerInfo
= false;
1341 if(SOSRingHasPeerID(ring
, peerID
)) {
1342 writePeerInfo
= true;
1346 // this was circle behavior - at some point
1347 if(SOSRingHasApplicant(ring
, peerID
)) {
1352 if(writePeerInfo
|| writeRing
) {
1353 SOSRingWithdraw(ring
, NULL
, fpi
, error
);
1356 // Write leave thing to Transport
1357 CFDataRef peerInfoData
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
);
1358 SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors?
1361 CFDataRef ring_data
= SOSRingCopyEncodedData(ring
, error
);
1364 SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors?
1366 CFReleaseNull(ring_data
);
1369 CFReleaseNull(localError
);
1373 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef
*error
) {
1374 bool result
= false;
1375 SOSTransportCircleRef transport
= account
->circle_transport
;
1377 result
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
);
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.
1389 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1390 with server but initial round trip with server does not imply
1391 NSUbiquitousKeyValueStoreInitialSyncChange.
1396 // MARK: Status summary
1399 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) {
1401 return kSOSCCNotInCircle
;
1403 if (circle
&& SOSCircleCountPeers(circle
) == 0)
1404 return kSOSCCCircleAbsent
;
1408 if(SOSPeerInfoIsRetirementTicket(this_peer
))
1409 return kSOSCCNotInCircle
;
1411 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
1412 return kSOSCCInCircle
;
1414 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
1415 return kSOSCCRequestPending
;
1418 return kSOSCCNotInCircle
;
1421 CFStringRef
SOSAccountGetSOSCCStatusString(SOSCCStatus 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");
1429 return CFSTR("kSOSCCError");
1432 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef
*error
) {
1433 SOSCCStatus result
= SOSAccountGetCircleStatus(account
, error
);
1435 if (result
!= kSOSCCInCircle
) {
1436 SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle"));
1443 SOSCCStatus
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) {
1444 if (!SOSAccountHasPublicKey(account
, error
)) {
1448 return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
));
1452 // MARK: Account Reset Circles
1455 // This needs to be called within a SOSAccountModifyCircle() block
1457 bool 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
);
1472 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account
, SOSCircleRef circle
, SecKeyRef privKey
, CFErrorRef
*error
) {
1473 bool retval
= false;
1474 CFMutableSetRef iCloud2Remove
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1476 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
1477 if(SOSPeerInfoIsCloudIdentity(peer
)) {
1478 SOSFullPeerInfoRef icfpi
= SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, peer
, NULL
);
1480 CFSetAddValue(iCloud2Remove
, peer
);
1482 CFReleaseNull(icfpi
);
1486 if(CFSetGetCount(iCloud2Remove
) > 0) {
1488 SOSCircleRemovePeers(circle
, privKey
, account
->my_identity
, iCloud2Remove
, error
);
1490 CFReleaseNull(iCloud2Remove
);
1494 static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn
, SecKeyRef user_key
, CFErrorRef
*error
) {
1495 SOSAccountRef account
= aTxn
->account
;
1496 bool result
= false;
1498 require(SOSAccountHasCircle(account
, error
), fail
);
1499 require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1501 (void) SOSAccountResetAllRings(account
, error
);
1503 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1504 bool result
= false;
1505 SOSFullPeerInfoRef cloud_identity
= NULL
;
1506 CFErrorRef localError
= NULL
;
1508 require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
);
1510 account
->departure_code
= kSOSNeverLeftCircle
;
1511 require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
);
1513 require_quiet(SOSAccountAddiCloudIdentity(account
, circle
, user_key
, error
), err_out
);
1515 SOSAccountPublishCloudParameters(account
, NULL
);
1518 if (result
== false)
1519 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
1520 if (localError
&& error
&& *error
== NULL
) {
1521 *error
= localError
;
1524 CFReleaseNull(localError
);
1525 CFReleaseNull(cloud_identity
);
1529 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1530 SOSAccountUpdateOutOfSyncViews(aTxn
, SOSViewsGetAllCurrent());
1539 bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) {
1540 SOSAccountRef account
= aTxn
->account
;
1541 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1545 CFReleaseNull(account
->my_identity
);
1546 secnotice("resetToOffering", "Resetting circle to offering by request from client");
1548 return user_key
&& SOSAccountResetCircleToOffering(aTxn
, user_key
, error
);
1551 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
1552 if (!SOSAccountHasPublicKey(account
, error
))
1554 __block
bool result
= true;
1556 result
&= SOSAccountResetAllRings(account
, error
);
1558 CFReleaseNull(account
->my_identity
);
1560 account
->departure_code
= kSOSWithdrewMembership
;
1561 secnotice("resetToEmpty", "Reset Circle to empty by client request");
1562 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1563 result
= SOSCircleResetToEmpty(circle
, error
);
1568 secerror("error: %@", error
? *error
: NULL
);
1573 // MARK: start backups
1576 bool SOSAccountEnsureInBackupRings(SOSAccountRef account
) {
1577 __block
bool result
= false;
1578 __block CFErrorRef error
= NULL
;
1579 secnotice("backup", "Ensuring in rings");
1581 CFDataRef backupKey
= NULL
;
1583 require_action_quiet(account
->backup_key
, exit
, result
= true);
1585 backupKey
= SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
);
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
);
1592 require_quiet(account
->backup_key
, exit
); // If it went null, we're done now.
1594 require_quiet(SOSBSKBIsGoodBackupPublic(account
->backup_key
, &error
), exit
);
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
);
1601 CFReleaseNull(localError
);
1605 // Setup backups the new way.
1606 SOSAccountForEachBackupView(account
, ^(const void *value
) {
1607 CFStringRef viewName
= (CFStringRef
)value
;
1608 result
&= SOSAccountNewBKSBForView(account
, viewName
, &error
);
1613 secnotice("backupkey", "Failed to setup backup public key: %@", error
? (CFTypeRef
) error
: (CFTypeRef
) CFSTR("No error space provided"));
1615 CFReleaseNull(backupKey
);
1623 static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn
, SecKeyRef user_key
,
1624 bool use_cloud_peer
, CFErrorRef
* error
) {
1625 SOSAccountRef account
= aTxn
->account
;
1627 __block
bool result
= false;
1628 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
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
);
1633 SOSFullPeerInfoRef myCirclePeer
= account
->my_identity
;
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
);
1640 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1642 if (use_cloud_peer
) {
1643 cloud_full_peer
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
);
1646 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1647 result
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
);
1648 result
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
);
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
);
1659 secerror("Failed to join with cloud identity: %@", localError
);
1660 CFReleaseNull(localError
);
1666 if (use_cloud_peer
) {
1667 SOSAccountUpdateOutOfSyncViews(aTxn
, SOSViewsGetAllCurrent());
1672 CFReleaseNull(cloud_full_peer
);
1676 static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn
, bool use_cloud_identity
, CFErrorRef
* error
) {
1677 SOSAccountRef account
= aTxn
->account
;
1678 bool success
= false;
1680 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1681 require_quiet(user_key
, done
); // Fail if we don't get one.
1683 require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join")));
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
);
1690 SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now.
1692 if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) {
1693 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
));
1695 CFReleaseNull(account
->my_identity
);
1700 success
= SOSAccountJoinCircle(aTxn
, user_key
, use_cloud_identity
, error
);
1702 require_quiet(success
, done
);
1704 account
->departure_code
= kSOSNeverLeftCircle
;
1710 bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) {
1711 return SOSAccountJoinCircles_internal(aTxn
, false, error
);
1714 CFStringRef
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef
*error
){
1715 CFStringRef result
= NULL
;
1717 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1719 result
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1725 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){
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")));
1732 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
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
);
1739 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1745 SOSCCSyncWithAllPeers();
1748 CFReleaseNull(account
->deviceID
);
1749 account
->deviceID
= CFRetainSafe(IDS
);
1753 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1755 //construct message dictionary, circle -> peerID -> message
1757 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1758 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1760 char *messageCharStar
;
1761 asprintf(&messageCharStar
, "%d", kIDSSendOneMessage
);
1762 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1764 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kIDSOperationType
, messageString
, kIDSMessageToSendKey
, CFSTR("send IDS test message"), NULL
);
1766 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1767 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
)))
1768 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1771 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1772 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1774 CFReleaseNull(mutableDictionary
);
1775 CFReleaseNull(peerToMessage
);
1776 CFReleaseNull(circleToPeerMessages
);
1777 CFReleaseNull(messageString
);
1778 free(messageCharStar
);
1782 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1783 bool result
= false;
1784 //construct message dictionary, circle -> peerID -> message
1786 if(account
->ids_message_transport
== NULL
)
1787 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1789 require_quiet(account
->ids_message_transport
, fail
);
1790 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1791 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1793 char *messageCharStar
;
1794 asprintf(&messageCharStar
, "%d", kIDSStartPingTestMessage
);
1795 CFStringRef operationToString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1796 CFStringRef messageToSend
= CFSTR("send IDS test message");
1798 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kIDSOperationType
, operationToString
, kIDSMessageToSendKey
, messageToSend
, NULL
);
1801 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1802 if(CFStringCompare(SOSAccountGetMyPeerID(account
), SOSPeerInfoGetPeerID(peer
), 0) != 0)
1803 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1806 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1807 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1809 CFReleaseNull(mutableDictionary
);
1810 CFReleaseNull(peerToMessage
);
1811 CFReleaseNull(circleToPeerMessages
);
1812 CFReleaseNull(operationToString
);
1813 free(messageCharStar
);
1818 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef
*error
){
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
1826 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1827 success
= (sync_error
== NULL
);
1829 CFRetainAssign(localError
, sync_error
);
1832 dispatch_semaphore_signal(wait_for
);
1833 dispatch_release(wait_for
);
1836 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
1837 dispatch_release(wait_for
);
1839 if(!success
&& localError
!= NULL
&& error
!= NULL
){
1840 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1841 *error
= localError
;
1845 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1850 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) {
1851 return SOSAccountJoinCircles_internal(aTxn
, true, error
);
1855 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
)
1859 secnotice("leaveCircle", "Leaving circle by client request");
1860 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1861 return sosAccountLeaveCircle(account
, circle
, error
);
1864 account
->departure_code
= kSOSWithdrewMembership
;
1869 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
)
1871 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1877 CFMutableSetRef peersToRemove
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
);
1879 bool leaveCircle
= CFSetContainsValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1881 CFSetRemoveValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1883 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1884 bool success
= false;
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;
1891 if (success
&& leaveCircle
) {
1892 secnotice("leaveCircle", "Leaving circle by client request");
1893 success
= sosAccountLeaveCircle(account
, circle
, error
);
1905 bool 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
) {
1913 secnotice("leaveCircle", "Leaving circle by client request");
1914 return sosAccountLeaveCircle(account
, circle
, error
);
1917 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
1918 dispatch_group_wait(group
, milestone
);
1920 account
->departure_code
= kSOSWithdrewMembership
;
1922 dispatch_release(group
);
1928 // MARK: Application
1931 static 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
)) {
1951 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
1952 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
1955 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1956 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1960 __block
bool success
= true;
1961 __block
int64_t num_peers
= 0;
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
);
1968 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1975 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1976 __block
bool success
= true;
1977 __block
int64_t num_peers
= 0;
1979 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1980 bool rejected
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
);
1984 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1992 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) {
1993 return CFSTR("We're compatible, go away");
1996 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) {
1997 return account
->departure_code
;
2000 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) {
2001 account
->departure_code
= reason
;
2005 CFArrayRef
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef
*error
) {
2006 CFArrayRef result
= NULL
;
2007 CFNumberRef generation
= NULL
;
2009 require_quiet(SOSAccountHasPublicKey(account
, error
), fail
);
2010 require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle")));
2012 generation
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
);
2013 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
);
2019 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef
*error
) {
2020 if (!SOSAccountHasPublicKey(account
, error
))
2023 return account
->user_public_trusted
;
2026 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef
*error
) {
2027 // TODO: this result is never set or used
2030 secnotice("updates", "Ensuring peer registration.");
2032 require_quiet(account
->trusted_circle
, done
);
2033 require_quiet(account
->my_identity
, done
);
2034 require_quiet(account
->user_public_trusted
, done
);
2036 // If we are not in the circle, there is no point in setting up peers
2037 require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
);
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"
2042 // - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder
2044 CFStringRef my_id
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
2046 SOSCircleForEachValidSyncingPeer(account
->trusted_circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
2047 if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) {
2048 CFErrorRef localError
= NULL
;
2049 SOSTransportMessageRef messageTransport
= NULL
;
2051 messageTransport
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport
: account
->kvs_message_transport
;
2053 SOSEngineInitializePeerCoder(messageTransport
->engine
, account
->my_identity
, peer
, &localError
);
2055 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
);
2056 CFReleaseSafe(localError
);
2060 //Initialize our device ID
2061 SOSTransportMessageIDSGetIDSDeviceID(account
);
2068 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef
*error
) {
2069 if (!account
->expansion
) {
2070 account
->expansion
= CFDictionaryCreateMutableForCFTypes(NULL
);
2073 return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary"));
2077 // Value manipulation
2080 bool SOSAccountClearValue(SOSAccountRef account
, CFStringRef key
, CFErrorRef
*error
) {
2081 bool success
= SOSAccountEnsureExpansion(account
, error
);
2082 require_quiet(success
, errOut
);
2084 CFDictionaryRemoveValue(account
->expansion
, key
);
2089 bool SOSAccountSetValue(SOSAccountRef account
, CFStringRef key
, CFTypeRef value
, CFErrorRef
*error
) {
2090 if (value
== NULL
) return SOSAccountClearValue(account
, key
, error
);
2092 bool success
= SOSAccountEnsureExpansion(account
, error
);
2093 require_quiet(success
, errOut
);
2095 CFDictionarySetValue(account
->expansion
, key
, value
);
2100 CFTypeRef
SOSAccountGetValue(SOSAccountRef account
, CFStringRef key
, CFErrorRef
*error
) {
2101 if (!account
->expansion
) {
2104 return CFDictionaryGetValue(account
->expansion
, key
);
2107 bool 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;
2112 if(isDictionary(escrowRecords
) && escrowRecords
!= NULL
)
2113 escrowCopied
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
);
2115 escrowCopied
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
2117 CFDictionaryAddValue(escrowCopied
, dsid
, record
);
2118 SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
);
2123 CFReleaseNull(escrowCopied
);
2129 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef
*error
){
2130 bool success
= false;
2132 CFDictionaryRef escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
2133 success
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
);
2138 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef
*error
)
2140 CFMutableDictionaryRef circleToPeerMessages
= NULL
;
2141 CFStringRef operationTypeAsString
= NULL
;
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
);
2150 require_quiet(account
->ids_message_transport
, fail
);
2151 circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
2153 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
2155 asprintf(&message
, "%d", kIDSPeerAvailability
);
2156 operationTypeAsString
= CFStringCreateWithCString(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
);
2158 mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kIDSOperationType
, operationTypeAsString
, kIDSMessageToSendKey
, CFSTR("checking peers"), NULL
);
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
);
2165 peerList
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
2166 SOSCircleRef circle
= account
->trusted_circle
;
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
);
2180 CFReleaseNull(peerViews
);
2181 CFReleaseNull(intersectSets
);
2185 require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
);
2186 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerList
);
2187 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
2190 CFReleaseNull(mutableDictionary
);
2191 CFReleaseNull(operationTypeAsString
);
2192 CFReleaseNull(peerList
);
2193 CFReleaseNull(circleToPeerMessages
);
2194 CFReleaseNull(peers
);
2200 void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) {
2201 if (!SOSAccountIsInCircle(account
, NULL
))
2204 SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) {
2205 __block
bool updated
= false;
2206 CFSetForEach(account
->retirees
, ^(CFTypeRef element
){
2207 SOSPeerInfoRef retiree
= asSOSPeerInfo(element
);
2209 if (retiree
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) {
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
);
2223 static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFErrorRef
*error
) {
2224 size_t total_payload
= 0;
2226 CFDataRef publicBytes
= NULL
;
2227 OSStatus result
= SecKeyCopyPublicBytes(pubKey
, &publicBytes
);
2229 if (result
!= errSecSuccess
) {
2230 SOSCreateError(kSOSErrorBadKey
, CFSTR("Failed to export public bytes"), NULL
, error
);
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
);
2240 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
2244 static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) {
2245 CFDataRef publicBytes
= NULL
;
2247 OSStatus result
= SecKeyCopyPublicBytes(pubKey
, &publicBytes
);
2249 if (result
!= errSecSuccess
) {
2250 SOSCreateError(kSOSErrorBadKey
, CFSTR("Failed to export public bytes"), NULL
, error
);
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
))));
2262 static CFDataRef
SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFAllocatorRef allocator
, CFErrorRef
*error
)
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
);
2269 struct piggyBackBlob
{
2270 SOSGenCountRef gencount
;
2272 CFDataRef signature
;
2275 static 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
;
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
);
2298 CFReleaseNull(gencount
);
2299 CFReleaseNull(publicBytes
);
2300 CFReleaseNull(signature
);
2305 static struct piggyBackBlob
*SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator
, CFDataRef blobData
, CFErrorRef
*error
)
2307 size_t size
= CFDataGetLength(blobData
);
2308 const uint8_t *der
= CFDataGetBytePtr(blobData
);
2309 struct piggyBackBlob
*inflated
= SOSPiggyBackBlobCreateFromDER(allocator
, error
, &der
, der
+ size
);
2315 SOSPeerInfoRef
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
);
2327 CFDataRef
SOSAccountCopyCircleJoiningBlob(SOSAccountRef account
, SOSPeerInfoRef applicant
, CFErrorRef
*error
) {
2328 SOSGenCountRef gencount
= NULL
;
2329 CFDataRef signature
= NULL
;
2330 SecKeyRef ourKey
= NULL
;
2332 CFDataRef pbblob
= NULL
;
2334 SecKeyRef userKey
= SOSAccountGetTrustedPublicCredential(account
, error
);
2335 require_quiet(userKey
, errOut
);
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
);
2341 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
2342 ourKey
= SOSFullPeerInfoCopyDeviceKey(fpi
, error
);
2343 require_quiet(ourKey
, errOut
);
2346 SOSCircleRef currentCircle
= SOSAccountGetCircle(account
, error
);
2347 require_quiet(currentCircle
, errOut
);
2349 SOSCircleRef prunedCircle
= SOSCircleCopyCircle(NULL
, currentCircle
, error
);
2350 require_quiet(prunedCircle
, errOut
);
2351 require_quiet(SOSCirclePreGenerationSign(prunedCircle
, userKey
, error
), errOut
);
2353 gencount
= SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle
));
2355 signature
= SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle
, applicant
, ourKey
, error
);
2356 require_quiet(signature
, errOut
);
2358 pbblob
= SOSPiggyBackBlobCopyEncodedData(gencount
, ourKey
, signature
, kCFAllocatorDefault
, error
);
2361 CFReleaseNull(gencount
);
2362 CFReleaseNull(signature
);
2363 CFReleaseNull(ourKey
);
2368 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account
, CFDataRef joiningBlob
, CFErrorRef
*error
) {
2369 bool retval
= false;
2370 SecKeyRef userKey
= NULL
;
2371 struct piggyBackBlob
*pbb
= NULL
;
2373 userKey
= SOSAccountGetPrivateCredential(account
, error
);
2374 require_quiet(userKey
, errOut
);
2375 pbb
= SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault
, joiningBlob
, error
);
2376 require_quiet(pbb
, errOut
);
2378 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
2380 retval
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef copyOfCurrent
) {
2381 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent
, userKey
,
2385 account
->my_identity
, error
);;
2391 CFReleaseNull(pbb
->gencount
);
2392 CFReleaseNull(pbb
->pubKey
);
2393 CFReleaseNull(pbb
->signature
);
2399 static char boolToChars(bool val
, char truechar
, char falsechar
) {
2400 return val
? truechar
: falsechar
;
2403 #define ACCOUNTLOGSTATE "accountLogState"
2404 void 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*"));
2412 secnotice(ACCOUNTLOGSTATE
, "Start");
2414 secnotice(ACCOUNTLOGSTATE
, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2415 boolToChars(hasPubKey
, 'U', 'u'), boolToChars(pubTrusted
, 'T', 't'), boolToChars(hasPriv
, 'I', 'i'),
2417 SOSAccountGetSOSCCStatusString(stat
)
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");
2424 void 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
);
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'),
2438 CFReleaseNull(views
);
2439 CFSetRef unsyncedViews
= SOSAccountCopyOutstandingViews(account
);
2440 CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) {
2441 secnotice(ACCOUNTLOGSTATE
, "outstanding views: %@", description
);
2443 CFReleaseNull(unsyncedViews
);
2446 secnotice(ACCOUNTLOGSTATE
, "Finish");