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/SecItemInternal.h>
27 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
28 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
30 CFGiblisWithCompareFor(SOSAccount
);
32 const CFStringRef SOSTransportMessageTypeIDS
= CFSTR("IDS");
33 const CFStringRef SOSTransportMessageTypeKVS
= CFSTR("KVS");
34 const CFStringRef kSOSDSIDKey
= CFSTR("AccountDSID");
35 const CFStringRef kSOSEscrowRecord
= CFSTR("EscrowRecord");
36 const CFStringRef kSOSUnsyncedViewsKey
= CFSTR("unsynced");
39 #define DATE_LENGTH 25
40 const CFStringRef kSOSAccountDebugScope
= CFSTR("Scope");
43 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
)
46 CFStringRef circle_name
= NULL
;
48 require_quiet(a
, xit
);
49 require_quiet(a
->factory
, xit
);
51 circle_name
= SOSDataSourceFactoryCopyName(a
->factory
);
52 require(circle_name
, xit
);
54 SOSAccountEnsureCircle(a
, circle_name
, NULL
);
59 // We don't own name, so don't release it.
60 CFReleaseNull(circle_name
);
65 SOSAccountRef
SOSAccountCreateBasic(CFAllocatorRef allocator
,
66 CFDictionaryRef gestalt
,
67 SOSDataSourceFactoryRef factory
) {
68 SOSAccountRef a
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
);
70 a
->queue
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
);
72 a
->notification_cleanups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
74 a
->gestalt
= CFRetainSafe(gestalt
);
76 a
->trusted_circle
= NULL
;
77 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(allocator
);
78 a
->backups
= CFDictionaryCreateMutableForCFTypes(allocator
);
79 a
->my_identity
= NULL
;
80 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
82 a
->factory
= factory
; // We adopt the factory. kthanksbai.
84 a
->_user_private
= NULL
;
85 a
->_password_tmp
= NULL
;
86 a
->user_private_timer
= NULL
;
88 a
->change_blocks
= CFArrayCreateMutableForCFTypes(allocator
);
90 a
->departure_code
= kSOSNeverAppliedToCircle
;
92 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
93 a
->circle_transport
= NULL
;
94 a
->kvs_message_transport
= NULL
;
95 a
->ids_message_transport
= NULL
;
96 a
->expansion
= CFDictionaryCreateMutableForCFTypes(allocator
);
101 SOSSecurityPropertyResultCode
SOSAccountUpdateSecurityProperty(SOSAccountRef account
, CFStringRef property
, SOSSecurityPropertyActionCode actionCode
, CFErrorRef
*error
) {
102 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralSecurityPropertyError
;
103 bool updateCircle
= false;
104 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
105 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
106 retval
= SOSFullPeerInfoUpdateSecurityProperty(account
->my_identity
, actionCode
, property
, error
);
108 if(actionCode
== kSOSCCSecurityPropertyEnable
&& retval
== kSOSCCSecurityPropertyValid
) {
110 } else if(actionCode
== kSOSCCSecurityPropertyDisable
&& retval
== kSOSCCSecurityPropertyNotValid
) {
112 } else if(actionCode
== kSOSCCSecurityPropertyPending
) {
117 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
118 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
119 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
127 SOSSecurityPropertyResultCode
SOSAccountSecurityPropertyStatus(SOSAccountRef account
, CFStringRef property
, CFErrorRef
*error
) {
128 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralViewError
;
129 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
130 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
131 retval
= SOSFullPeerInfoSecurityPropertyStatus(account
->my_identity
, property
, error
);
136 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
)
138 if (CFEqualSafe(new_gestalt
, account
->gestalt
))
141 if (account
->trusted_circle
&& account
->my_identity
142 && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) {
143 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
144 secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
145 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
149 CFRetainAssign(account
->gestalt
, new_gestalt
);
153 bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){
154 SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
);
155 //send new DSID over account changed
156 SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
);
161 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
, CFSetRef excludedViews
) {
162 if (account
->trusted_circle
&& account
->my_identity
) {
163 if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
, excludedViews
)) {
164 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
165 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
166 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
174 SOSViewResultCode
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
175 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
176 SOSViewResultCode currentStatus
= kSOSCCGeneralViewError
;
177 bool updateCircle
= false;
178 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
179 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
180 require_action_quiet((actionCode
== kSOSCCViewEnable
) || (actionCode
== kSOSCCViewDisable
), errOut
, CFSTR("Invalid View Action"));
181 currentStatus
= SOSAccountViewStatus(account
, viewname
, error
);
182 require_action_quiet((currentStatus
== kSOSCCViewNotMember
) || (currentStatus
== kSOSCCViewMember
), errOut
, CFSTR("View Membership Not Actionable"));
184 if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) {
185 // The V0 view switches on and off all on it's own, we allow people the delusion
186 // of control and status if it's what we're stuck at., otherwise error.
187 if (SOSAccountSyncingV0(account
)) {
188 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Can't disable V0 view and it's on right now"));
189 retval
= kSOSCCViewMember
;
191 require_action_quiet(actionCode
= kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now"));
192 retval
= kSOSCCViewNotMember
;
194 } else if (SOSAccountSyncingV0(account
) && SOSViewsIsV0Subview(viewname
)) {
195 // Subviews of V0 syncing can't be turned off if V0 is on.
196 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Have V0 peer can't disable"));
197 retval
= kSOSCCViewMember
;
199 if(actionCode
== kSOSCCViewEnable
&& currentStatus
== kSOSCCViewNotMember
) {
200 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
201 if(retval
== kSOSCCViewMember
) updateCircle
= true;
202 } else if(actionCode
== kSOSCCViewDisable
&& currentStatus
== kSOSCCViewMember
) {
203 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
204 if(retval
== kSOSCCViewNotMember
) updateCircle
= true;
206 retval
= currentStatus
;
210 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
211 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
212 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
221 SOSViewResultCode
SOSAccountViewStatus(SOSAccountRef account
, CFStringRef viewname
, CFErrorRef
*error
) {
222 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
223 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
224 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
226 retval
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
);
228 // 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
229 if (retval
!= kSOSCCViewMember
) {
230 if ((CFEqualSafe(viewname
, kSOSViewKeychainV0
) || SOSViewsIsV0Subview(viewname
))
231 && SOSAccountSyncingV0(account
)) {
232 retval
= kSOSCCViewMember
;
236 // If we're only an applicant we report pending if we would be a view member
237 if (retval
== kSOSCCViewMember
) {
238 bool isApplicant
= SOSCircleHasApplicant(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
), error
);
240 retval
= kSOSCCViewPending
;
248 static void dumpViewSet(CFStringRef label
, CFSetRef views
) {
250 secnotice("circleChange", "%@ list: %@", label
, views
);
252 secnotice("circleChange", "No %@ list provided.", label
);
256 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef enabledViews
, CFSetRef disabledViews
) {
257 bool updateCircle
= false;
258 dumpViewSet(CFSTR("Enabled"), enabledViews
);
259 dumpViewSet(CFSTR("Disabled"), disabledViews
);
261 require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle"));
262 require_action_quiet(account
->my_identity
, errOut
, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
263 require_action_quiet(enabledViews
|| disabledViews
, errOut
, secnotice("views", "No work to do"));
266 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
267 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
);
269 require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef"));
272 if(!SOSPeerInfoVersionIsCurrent(pi
)) {
273 if(!SOSPeerInfoUpdateToV2(pi
, NULL
)) {
274 secnotice("views", "Unable to update peer to V2- can't update views");
279 if(enabledViews
) updateCircle
= SOSViewSetEnable(pi
, enabledViews
);
280 if(disabledViews
) updateCircle
|= SOSViewSetDisable(pi
, disabledViews
);
282 /* UPDATE FULLPEERINFO VIEWS */
284 if (updateCircle
&& SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
)) {
285 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
286 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
287 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
296 SOSAccountRef
SOSAccountCreate(CFAllocatorRef allocator
,
297 CFDictionaryRef gestalt
,
298 SOSDataSourceFactoryRef factory
) {
299 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
301 SOSAccountEnsureFactoryCircles(a
);
303 SOSUpdateKeyInterest(a
);
308 static void SOSAccountDestroy(CFTypeRef aObj
) {
309 SOSAccountRef a
= (SOSAccountRef
) aObj
;
311 // We don't own the factory, merely have a reference to the singleton
315 SOSAccountCleanupNotificationForAllPeers(a
);
317 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
);
320 SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
);
322 dispatch_sync(a
->queue
, ^{
323 CFReleaseNull(a
->gestalt
);
325 CFReleaseNull(a
->my_identity
);
326 CFReleaseNull(a
->trusted_circle
);
327 CFReleaseNull(a
->trusted_rings
);
328 CFReleaseNull(a
->backups
);
329 CFReleaseNull(a
->retirees
);
331 a
->user_public_trusted
= false;
332 CFReleaseNull(a
->user_public
);
333 CFReleaseNull(a
->user_key_parameters
);
335 SOSAccountPurgePrivateCredential(a
);
336 CFReleaseNull(a
->previous_public
);
337 CFReleaseNull(a
->_user_private
);
338 CFReleaseNull(a
->_password_tmp
);
340 a
->departure_code
= kSOSNeverAppliedToCircle
;
341 CFReleaseNull(a
->kvs_message_transport
);
342 CFReleaseNull(a
->ids_message_transport
);
343 CFReleaseNull(a
->key_transport
);
344 CFReleaseNull(a
->circle_transport
);
345 dispatch_release(a
->queue
);
346 CFReleaseNull(a
->notification_cleanups
);
348 dispatch_release(a
->user_private_timer
);
349 CFReleaseNull(a
->change_blocks
);
350 CFReleaseNull(a
->expansion
);
355 static OSStatus
do_delete(CFDictionaryRef query
) {
358 result
= SecItemDelete(query
);
360 secerror("SecItemDelete: %d", (int)result
);
366 do_keychain_delete_aks_bags()
369 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
370 kSecClass
, kSecClassGenericPassword
,
371 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
372 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
373 kSecAttrService
, CFSTR("SecureBackupService"),
374 kSecAttrSynchronizable
, kCFBooleanTrue
,
375 kSecUseTombstones
, kCFBooleanFalse
,
378 result
= do_delete(item
);
385 do_keychain_delete_identities()
388 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
389 kSecClass
, kSecClassKey
,
390 kSecAttrSynchronizable
, kCFBooleanTrue
,
391 kSecUseTombstones
, kCFBooleanFalse
,
392 kSecAttrAccessGroup
, CFSTR("com.apple.security.sos"),
395 result
= do_delete(item
);
402 do_keychain_delete_lakitu()
405 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
406 kSecClass
, kSecClassGenericPassword
,
407 kSecAttrSynchronizable
, kCFBooleanTrue
,
408 kSecUseTombstones
, kCFBooleanFalse
,
409 kSecAttrAccessGroup
, CFSTR("com.apple.lakitu"),
410 kSecAttrAccount
, CFSTR("EscrowServiceBypassToken"),
411 kSecAttrService
, CFSTR("EscrowService"),
414 result
= do_delete(item
);
421 do_keychain_delete_sbd()
424 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
425 kSecClass
, kSecClassGenericPassword
,
426 kSecAttrSynchronizable
, kCFBooleanTrue
,
427 kSecUseTombstones
, kCFBooleanFalse
,
428 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
431 result
= do_delete(item
);
437 void SOSAccountSetToNew(SOSAccountRef a
) {
438 secnotice("accountChange", "Setting Account to New");
441 CFReleaseNull(a
->my_identity
);
442 CFReleaseNull(a
->trusted_circle
);
443 CFReleaseNull(a
->trusted_rings
);
444 CFReleaseNull(a
->backups
);
445 CFReleaseNull(a
->retirees
);
447 CFReleaseNull(a
->user_key_parameters
);
448 CFReleaseNull(a
->user_public
);
449 CFReleaseNull(a
->previous_public
);
450 CFReleaseNull(a
->_user_private
);
451 CFReleaseNull(a
->_password_tmp
);
453 CFReleaseNull(a
->key_transport
);
454 CFReleaseNull(a
->circle_transport
);
455 CFReleaseNull(a
->kvs_message_transport
);
456 CFReleaseNull(a
->ids_message_transport
);
457 CFReleaseNull(a
->expansion
);
459 /* remove all syncable items */
460 result
= do_keychain_delete_aks_bags();
461 secdebug("set to new", "result for deleting aks bags: %d", result
);
463 result
= do_keychain_delete_identities();
464 secdebug("set to new", "result for deleting identities: %d", result
);
466 result
= do_keychain_delete_lakitu();
467 secdebug("set to new", "result for deleting lakitu: %d", result
);
469 result
= do_keychain_delete_sbd();
470 secdebug("set to new", "result for deleting sbd: %d", result
);
472 a
->user_public_trusted
= false;
473 a
->departure_code
= kSOSNeverAppliedToCircle
;
474 a
->user_private_timer
= 0;
475 a
->lock_notification_token
= 0;
481 // update_interest_block;
484 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
485 a
->circle_transport
= NULL
;
486 a
->kvs_message_transport
= NULL
;
487 a
->ids_message_transport
= NULL
;
489 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
490 a
->backups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
492 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
493 a
->expansion
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
495 SOSAccountEnsureFactoryCircles(a
); // Does rings too
497 SOSUpdateKeyInterest(a
);
501 static CFStringRef
SOSAccountCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
502 SOSAccountRef a
= (SOSAccountRef
) aObj
;
504 CFStringRef gestaltDescription
= CFDictionaryCopyCompactDescription(a
->gestalt
);
506 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a
, gestaltDescription
, a
->trusted_circle
, a
->my_identity
);
508 CFReleaseNull(gestaltDescription
);
513 CFStringRef
SOSAccountCreateCompactDescription(SOSAccountRef a
) {
515 CFStringRef gestaltDescription
= CFDictionaryCopySuperCompactDescription(a
->gestalt
);
517 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), gestaltDescription
);
519 CFReleaseNull(gestaltDescription
);
524 static Boolean
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
)
526 SOSAccountRef laccount
= (SOSAccountRef
) lhs
;
527 SOSAccountRef raccount
= (SOSAccountRef
) rhs
;
529 return CFEqualSafe(laccount
->gestalt
, raccount
->gestalt
)
530 && CFEqualSafe(laccount
->trusted_circle
, raccount
->trusted_circle
)
531 && CFEqualSafe(laccount
->trusted_rings
, raccount
->trusted_rings
)
532 && CFEqualSafe(laccount
->my_identity
, raccount
->my_identity
);
535 dispatch_queue_t
SOSAccountGetQueue(SOSAccountRef account
) {
536 return account
->queue
;
539 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account
){
540 account
->user_public_trusted
= true;
543 SOSFullPeerInfoRef
SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
)
545 return CFRetainSafe(account
->my_identity
);
548 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account
) {
550 __block CFErrorRef error
= NULL
;
552 if (!SOSAccountHasPublicKey(account
, &error
)) {
553 CFReleaseSafe(error
);
559 require_action_quiet(account
->my_identity
, xit
,
560 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account identity not set"), NULL
, &error
));
562 SOSTransportMessageIDSGetIDSDeviceID(account
);
564 require_action_quiet(account
->trusted_circle
, xit
,
565 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account trusted circle not set"), NULL
, &error
));
567 require_action_quiet(hasID
, xit
,
568 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Missing IDS device ID"), NULL
, &error
));
569 ok
= SOSCircleHasPeerWithID(account
->trusted_circle
,
570 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)), &error
);
573 secerror("sync with device failure: %@", error
);
575 CFReleaseSafe(error
);
579 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef peerID
) {
580 SOSPeerInfoRef mypi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
581 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(mypi
);
583 return myPeerID
&& CFEqualSafe(myPeerID
, peerID
);
586 static bool isDefaultsWriteSetupToSyncOverIDS(){
587 return ((whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
));
590 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef
*error
)
593 __block
bool SyncingCompletedOverIDS
= true;
594 __block
bool SyncingCompletedOverKVS
= true;
595 __block CFErrorRef localError
= NULL
;
596 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
597 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
598 CFMutableArrayRef peerIds
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
600 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
601 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
604 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
605 if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) {
606 if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)) {
607 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
608 CFMutableDictionaryRef circleToIdsId
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
609 CFMutableArrayRef ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
610 CFArrayAppendValue(ids
, SOSPeerInfoGetPeerID(peer
));
611 CFDictionaryAddValue(circleToIdsId
, SOSCircleGetName(circle
), ids
);
612 SyncingCompletedOverIDS
= SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, circleToIdsId
, &localError
);
613 CFReleaseNull(circleToIdsId
);
615 CFArrayAppendValue(peerIds
, SOSPeerInfoGetPeerID(peer
));
619 if (CFArrayGetCount(peerIds
)) {
620 secnotice("KVS", "Syncing with KVS capable peers");
621 CFDictionarySetValue(circleToPeerIDs
, SOSCircleGetName(circle
), peerIds
);
622 SyncingCompletedOverKVS
&= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToPeerIDs
, &localError
);
625 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->kvs_message_transport
);
626 result
= SOSEngineSyncWithPeers(engine
, account
->ids_message_transport
, account
->kvs_message_transport
, &localError
);
628 result
&= ((SyncingCompletedOverIDS
) &&
629 (SyncingCompletedOverKVS
|| (CFDictionaryGetCount(circleToPeerIDs
) == 0)));
632 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
635 CFReleaseNull(circleToPeerIDs
);
638 secdebug("Account", "Could not sync with all peers: %@", localError
);
639 CFErrorPropagate(localError
, error
);
642 CFReleaseNull(peerIds
);
643 CFReleaseSafe(localError
);
647 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
648 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
652 SOSPeerInfoRef myPeerInfo
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
653 require_action_quiet(account
->my_identity
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer")));
654 require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
);
656 CFStringRef cleanupPeerID
= SOSPeerInfoGetPeerID(cleanupPeer
);
658 CFStringRef circle_name
= SOSCircleGetName(circle
);
660 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
661 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
);
663 CFErrorRef localError
= NULL
;
664 if (!(success
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) {
665 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
668 if (account
->ids_message_transport
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) {
669 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
672 CFReleaseNull(localError
);
674 if((success
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) {
675 if (!(success
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) {
676 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
);
679 CFReleaseNull(localError
);
680 CFReleaseNull(circleToPeerIDs
);
686 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
687 CFMutableSetRef retirees_to_remove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
689 __block
bool success
= true;
691 CFSetForEach(account
->retirees
, ^(const void *value
) {
692 SOSPeerInfoRef retiree
= (SOSPeerInfoRef
) value
;
695 // 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.
696 if (!SOSPeerInfoIsRetirementTicket(retiree
) ||
697 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) {
698 CFSetAddValue(retirees_to_remove
, retiree
);
703 CFMutableArrayRef retirees_to_cleanup
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
704 CFSetForEach(retirees_to_remove
, ^(const void *value
) {
705 CFArrayAppendValue(retirees_to_cleanup
, value
);
706 CFSetRemoveValue(account
->retirees
, value
);
709 CFReleaseNull(retirees_to_remove
);
711 CFDictionaryRef retirements_to_remove
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
712 SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
,
715 CFReleaseNull(retirees_to_cleanup
);
717 success
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
);
719 CFReleaseNull(retirements_to_remove
);
724 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
) {
725 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
726 CFSetSetValue(account
->retirees
, peer
);
727 CFErrorRef cleanupError
= NULL
;
728 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) {
729 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
);
731 CFReleaseSafe(cleanupError
);
736 SOSCircleRef
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef
*error
) {
737 SOSCircleRef new_circle
= SOSCircleCopyCircle(NULL
, starting_circle
, error
);
738 if(!new_circle
) return NULL
;
740 if (account
->retirees
) {
741 CFSetForEach(account
->retirees
, ^(const void* value
) {
742 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
743 if (isSOSPeerInfo(pi
)) {
744 SOSCircleUpdatePeerInfo(new_circle
, pi
);
749 if(SOSCircleCountPeers(new_circle
) == 0) {
750 SOSCircleResetToEmpty(new_circle
, NULL
);
757 // MARK: Circle Membership change notificaion
760 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
761 SOSAccountCircleMembershipChangeBlock copy
= Block_copy(changeBlock
);
762 CFArrayAppendValue(a
->change_blocks
, copy
);
766 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
767 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
770 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) {
771 if (!changeBlock
) return;
773 CFRetainSafe(ds_name
);
774 SOSAccountCircleMembershipChangeBlock block_to_register
= ^void (SOSCircleRef new_circle
,
775 CFSetRef added_peers
, CFSetRef removed_peers
,
776 CFSetRef added_applicants
, CFSetRef removed_applicants
) {
778 if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
))
781 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
);
782 CFStringRef myPi_id
= myPi
? SOSPeerInfoGetPeerID(myPi
) : NULL
;
784 CFMutableArrayRef peer_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
785 CFMutableArrayRef added_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
786 CFMutableArrayRef removed_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
788 if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) {
789 SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) {
790 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
);
793 CFSetForEach(added_peers
, ^(const void *value
) {
794 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
797 CFSetForEach(removed_peers
, ^(const void *value
) {
798 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
802 if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
))
803 changeBlock(peer_ids
, added_ids
, removed_ids
);
805 CFReleaseSafe(peer_ids
);
806 CFReleaseSafe(added_ids
);
807 CFReleaseSafe(removed_ids
);
810 CFRetainSafe(changeBlock
);
811 SOSAccountAddChangeBlock(a
, block_to_register
);
813 CFSetRef empty
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
814 if (a
->trusted_circle
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) {
815 block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
);
817 CFReleaseSafe(empty
);
820 void SOSAccountPurgeIdentity(SOSAccountRef account
) {
821 if (account
->my_identity
) {
822 // Purge private key but don't return error if we can't.
823 CFErrorRef purgeError
= NULL
;
824 if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) {
825 secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
);
827 CFReleaseNull(purgeError
);
829 CFReleaseNull(account
->my_identity
);
833 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
834 SOSFullPeerInfoRef fpi
= account
->my_identity
;
835 if(!fpi
) return false;
837 CFErrorRef localError
= NULL
;
841 SOSPeerInfoRef retire_peer
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
);
843 secerror("Create ticket failed for peer %@: %@", fpi
, localError
);
845 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
846 if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) {
847 // Remove our application if we have one.
848 SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
);
849 } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) {
850 if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) {
851 CFErrorRef cleanupError
= NULL
;
852 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) {
853 secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
);
855 CFReleaseSafe(cleanupError
);
859 // Store the retirement record locally.
860 CFSetAddValue(account
->retirees
, retire_peer
);
862 // Write retirement to Transport
863 CFErrorRef postError
= NULL
;
864 if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){
865 secwarning("Couldn't post retirement (%@)", postError
);
867 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){
868 secwarning("Couldn't flush retirement data (%@)", postError
);
870 CFReleaseNull(postError
);
873 SOSAccountPurgeIdentity(account
);
877 CFReleaseNull(localError
);
878 CFReleaseNull(retire_peer
);
882 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) {
883 SOSFullPeerInfoRef fpi
= account
->my_identity
;
884 if(!fpi
) return false;
885 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
886 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
888 CFErrorRef localError
= NULL
;
891 bool writeRing
= false;
892 bool writePeerInfo
= false;
894 if(SOSRingHasPeerID(ring
, peerID
)) {
895 writePeerInfo
= true;
899 // this was circle behavior - at some point
900 if(SOSRingHasApplicant(ring
, peerID
)) {
905 if(writePeerInfo
|| writeRing
) {
906 SOSRingWithdraw(ring
, NULL
, fpi
, error
);
909 // Write leave thing to Transport
910 CFDataRef peerInfoData
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
);
911 SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors?
914 CFDataRef ring_data
= SOSRingCopyEncodedData(ring
, error
);
917 SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors?
919 CFReleaseNull(ring_data
);
922 CFReleaseNull(localError
);
926 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef
*error
) {
928 SOSTransportCircleRef transport
= account
->circle_transport
;
930 result
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
);
936 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
937 local value that has been overwritten by a distant value. If there is no
938 conflict between the local and the distant values when doing the initial
939 sync (e.g. if the cloud has no data stored or the client has not stored
940 any data yet), you'll never see that notification.
942 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
943 with server but initial round trip with server does not imply
944 NSUbiquitousKeyValueStoreInitialSyncChange.
949 // MARK: Status summary
952 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) {
954 return kSOSCCNotInCircle
;
956 if (circle
&& SOSCircleCountPeers(circle
) == 0)
957 return kSOSCCCircleAbsent
;
961 if(SOSPeerInfoIsRetirementTicket(this_peer
))
962 return kSOSCCNotInCircle
;
964 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
965 return kSOSCCInCircle
;
967 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
968 return kSOSCCRequestPending
;
971 return kSOSCCNotInCircle
;
974 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef
*error
) {
975 SOSCCStatus result
= SOSAccountGetCircleStatus(account
, error
);
977 if (result
!= kSOSCCInCircle
&& result
!= kSOSCCError
) {
978 SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle"));
985 SOSCCStatus
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) {
986 if (!SOSAccountHasPublicKey(account
, error
)) {
990 return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
));
994 // MARK: Account Reset Circles
997 static bool SOSAccountResetCircleToOffering(SOSAccountRef account
, SecKeyRef user_key
, CFErrorRef
*error
) {
1000 require(SOSAccountHasCircle(account
, error
), fail
);
1001 require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1003 (void) SOSAccountResetAllRings(account
, error
);
1005 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1006 bool result
= false;
1007 SOSFullPeerInfoRef cloud_identity
= NULL
;
1008 CFErrorRef localError
= NULL
;
1010 require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
);
1013 SOSPeerInfoRef cloud_peer
= GenerateNewCloudIdentityPeerInfo(error
);
1014 require_quiet(cloud_peer
, err_out
);
1015 cloud_identity
= CopyCloudKeychainIdentity(cloud_peer
, error
);
1016 CFReleaseNull(cloud_peer
);
1017 require_quiet(cloud_identity
, err_out
);
1020 account
->departure_code
= kSOSNeverLeftCircle
;
1021 require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
);
1022 require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, &localError
), err_out
);
1023 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), &localError
), err_out
);
1025 SOSAccountPublishCloudParameters(account
, NULL
);
1028 if (result
== false)
1029 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
1030 if (localError
&& error
&& *error
== NULL
) {
1031 *error
= localError
;
1034 CFReleaseNull(localError
);
1035 CFReleaseNull(cloud_identity
);
1046 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) {
1047 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1051 CFReleaseNull(account
->my_identity
);
1053 return user_key
&& SOSAccountResetCircleToOffering(account
, user_key
, error
);
1056 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
1057 if (!SOSAccountHasPublicKey(account
, error
))
1059 __block
bool result
= true;
1061 result
&= SOSAccountResetAllRings(account
, error
);
1063 CFReleaseNull(account
->my_identity
);
1065 account
->departure_code
= kSOSWithdrewMembership
;
1066 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1067 result
= SOSCircleResetToEmpty(circle
, error
);
1072 secerror("error: %@", error
? *error
: NULL
);
1080 // MARK: Waiting for in-sync
1083 static bool SOSAccountHasBeenInSync(SOSAccountRef account
) {
1084 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1085 CFSetRef unsynced
= asSet(unsyncedObject
, NULL
);
1087 return !(unsyncedObject
== kCFBooleanTrue
|| (unsynced
&& (CFSetGetCount(unsynced
) > 0)));
1090 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account
, CFSetRef viewsInSync
) {
1091 bool notifyOfChange
= false;
1093 SOSCCStatus circleStatus
= SOSAccountGetCircleStatus(account
, NULL
);
1094 bool inOrApplying
= (circleStatus
== kSOSCCInCircle
) || (circleStatus
== kSOSCCRequestPending
);
1096 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1098 if (!inOrApplying
) {
1099 if (unsyncedObject
!= NULL
) {
1100 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1101 secnotice("initial-sync", "in sync, clearing pending");
1102 notifyOfChange
= true;
1104 } else if (circleStatus
== kSOSCCInCircle
) {
1105 __block CFMutableSetRef viewsToSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1106 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1107 SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) {
1108 CFSetUnion(viewsToSync
, enabled
);
1113 CFSetSubtract(viewsToSync
, viewsInSync
);
1117 if (unsyncedObject
== kCFBooleanTrue
) {
1118 if (CFSetGetCount(viewsToSync
) == 0) {
1119 secnotice("initial-sync", "No views to wait for");
1120 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1122 __block CFSetRef newViews
= NULL
;
1123 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account
), ^(CFSetRef enabled
) {
1124 newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, enabled
, viewsToSync
);
1126 secnotice("initial-sync", "Pending views set from True: %@", newViews
);
1127 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1128 CFReleaseNull(newViews
);
1130 notifyOfChange
= true;
1131 } else if (isSet(unsyncedObject
)) {
1132 CFSetRef waiting
= (CFMutableSetRef
) unsyncedObject
;
1133 CFSetRef newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, waiting
, viewsToSync
);
1134 if (!CFEqualSafe(waiting
, newViews
)) {
1135 secnotice("initial-sync", "Pending views updated: %@", newViews
);
1136 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1137 notifyOfChange
= true;
1139 CFReleaseNull(newViews
);
1142 CFReleaseNull(viewsToSync
);
1145 if (notifyOfChange
) {
1146 secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification
);
1147 notify_post(kSOSCCInitialSyncChangedNotification
);
1148 // Make sure we update the engine
1149 account
->circle_rings_retirements_need_attention
= true;
1152 return SOSAccountHasBeenInSync(account
);
1155 static void SOSAccountPeerGotInSync(SOSAccountRef account
, CFStringRef peerID
) {
1156 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID
);
1158 if (account
->trusted_circle
) {
1159 SOSPeerInfoRef peer
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
);
1161 CFSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
1162 SOSAccountUpdateOutOfSyncViews(account
, views
);
1163 CFReleaseNull(views
);
1165 CFReleaseNull(peer
);
1169 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account
) {
1170 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1172 CFDictionaryForEach(account
->notification_cleanups
, ^(const void *key
, const void *value
) {
1174 SOSEngineSetSyncCompleteListener(engine
, key
, NULL
);
1176 dispatch_async(account
->queue
, value
);
1179 CFDictionaryRemoveAllValues(account
->notification_cleanups
);
1182 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account
, CFStringRef peerID
) {
1183 dispatch_block_t cleanup
= CFDictionaryGetValue(account
->notification_cleanups
, peerID
);
1186 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1189 SOSEngineSetSyncCompleteListener(engine
, peerID
, NULL
);
1192 dispatch_async(account
->queue
, cleanup
);
1195 CFDictionaryRemoveValue(account
->notification_cleanups
, peerID
);
1199 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account
, CFStringRef peerID
, dispatch_block_t block
) {
1200 dispatch_block_t copy
= Block_copy(block
);
1201 CFDictionarySetValue(account
->notification_cleanups
, peerID
, copy
);
1202 CFReleaseNull(copy
);
1205 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) {
1206 if (CFDictionaryGetCount(account
->notification_cleanups
) == 0) {
1207 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
1208 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1210 SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
);
1213 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1214 CFStringRef peerID
= CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerID(peer
));
1216 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID
);
1217 SOSAccountRegisterCleanupBlock(account
, peerID
, ^{
1218 CFReleaseSafe(peerID
);
1221 SOSEngineSetSyncCompleteListener(engine
, peerID
, ^{
1222 SOSAccountPeerGotInSync(account
, peerID
);
1223 SOSAccountCleanupNotificationForPeer(account
, peerID
);
1224 SOSAccountFinishTransaction(account
);
1228 secerror("Couldn't find engine to setup notifications!!!");
1233 void SOSAccountCancelSyncChecking(SOSAccountRef account
) {
1234 SOSAccountCleanupNotificationForAllPeers(account
);
1235 SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1238 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account
) {
1239 bool hasBeenInSync
= false;
1241 if (!SOSAccountIsInCircle(account
, NULL
)) {
1242 SOSAccountCancelSyncChecking(account
);
1244 hasBeenInSync
= SOSAccountHasBeenInSync(account
);
1245 if (!hasBeenInSync
) {
1246 hasBeenInSync
= SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1247 if (hasBeenInSync
) {
1248 // Cancel and declare victory
1249 SOSAccountCancelSyncChecking(account
);
1251 // Make sure we're watching in case this is the fist attempt
1252 SOSAccountEnsureSyncChecking(account
);
1257 return hasBeenInSync
;
1264 static bool SOSAccountJoinCircle(SOSAccountRef account
, SecKeyRef user_key
,
1265 bool use_cloud_peer
, CFErrorRef
* error
) {
1266 __block
bool result
= false;
1267 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1269 require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???")));
1270 require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1272 SOSFullPeerInfoRef myCirclePeer
= account
->my_identity
;
1274 if (use_cloud_peer
) {
1275 cloud_full_peer
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
);
1277 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1280 if (SOSCircleCountPeers(account
->trusted_circle
) == 0) {
1281 result
= SOSAccountResetCircleToOffering(account
, user_key
, error
);
1283 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1284 result
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
);
1285 result
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
);
1286 account
->departure_code
= kSOSNeverLeftCircle
;
1287 if(result
&& cloud_full_peer
) {
1288 CFErrorRef localError
= NULL
;
1289 CFStringRef cloudid
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
));
1290 require_quiet(cloudid
, finish
);
1291 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
);
1292 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
);
1295 secerror("Failed to join with cloud identity: %@", localError
);
1296 CFReleaseNull(localError
);
1304 CFReleaseNull(cloud_full_peer
);
1308 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) {
1309 bool success
= false;
1311 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1312 require_quiet(user_key
, done
); // Fail if we don't get one.
1314 require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join")));
1316 if (account
->my_identity
!= NULL
) {
1317 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1318 success
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
);
1319 require_quiet(!success
, done
);
1321 SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now.
1323 if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) {
1324 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
));
1326 CFReleaseNull(account
->my_identity
);
1331 success
= SOSAccountJoinCircle(account
, user_key
, use_cloud_identity
, error
);
1333 require_quiet(success
, done
);
1335 account
->departure_code
= kSOSNeverLeftCircle
;
1341 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) {
1342 return SOSAccountJoinCircles_internal(account
, false, error
);
1345 CFStringRef
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef
*error
){
1346 CFStringRef result
= NULL
;
1348 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1350 result
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1356 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){
1359 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
){
1360 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1361 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1362 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1364 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1366 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1367 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDS
, error
);
1368 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1370 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1377 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1378 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1379 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1381 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1383 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1384 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeKVS
, error
);
1385 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1387 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1395 SOSCCSyncWithAllPeers();
1402 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1404 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1405 //construct message dictionary, circle -> peerID -> message
1407 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1408 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1410 char *messageCharStar
;
1411 asprintf(&messageCharStar
, "%d", kIDSSendOneMessage
);
1412 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1414 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1416 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1417 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
)))
1418 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1421 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1422 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1424 CFReleaseNull(mutableDictionary
);
1425 CFReleaseNull(peerToMessage
);
1426 CFReleaseNull(circleToPeerMessages
);
1427 CFReleaseNull(messageString
);
1428 free(messageCharStar
);
1433 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1434 bool result
= false;
1435 //construct message dictionary, circle -> peerID -> message
1437 if(account
->ids_message_transport
== NULL
)
1438 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1440 require_quiet(account
->ids_message_transport
, fail
);
1441 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1442 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1444 char *messageCharStar
;
1445 asprintf(&messageCharStar
, "%d", kIDSStartPingTestMessage
);
1446 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1448 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1450 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1451 if(CFStringCompare(SOSAccountGetMyPeerID(account
), SOSPeerInfoGetPeerID(peer
), 0) != 0)
1452 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1455 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1456 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1458 CFReleaseNull(mutableDictionary
);
1459 CFReleaseNull(peerToMessage
);
1460 CFReleaseNull(circleToPeerMessages
);
1461 CFReleaseNull(messageString
);
1462 free(messageCharStar
);
1467 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef
*error
){
1469 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1471 __block
bool success
= true;
1472 __block CFErrorRef localError
= NULL
;
1473 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
1474 dispatch_retain(wait_for
); // Both this scope and the block own it
1476 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1477 success
= (sync_error
== NULL
);
1479 CFRetainAssign(localError
, sync_error
);
1482 dispatch_semaphore_signal(wait_for
);
1483 dispatch_release(wait_for
);
1486 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
1487 dispatch_release(wait_for
);
1489 if(!success
&& localError
!= NULL
&& error
!= NULL
){
1490 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1491 *error
= localError
;
1494 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1500 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) {
1501 return SOSAccountJoinCircles_internal(account
, true, error
);
1505 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
)
1509 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1510 return sosAccountLeaveCircle(account
, circle
, error
);
1513 account
->departure_code
= kSOSWithdrewMembership
;
1518 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
)
1520 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1526 CFMutableSetRef peersToRemove
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
);
1528 bool leaveCircle
= CFSetContainsValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1530 CFSetRemoveValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1532 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1533 bool success
= false;
1535 require_quiet(SOSCircleRemovePeers(circle
, user_key
, SOSAccountGetMyFullPeerInfo(account
), peersToRemove
, error
), done
);
1538 success
= sosAccountLeaveCircle(account
, circle
, error
);
1540 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
1552 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) {
1553 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
1554 dispatch_group_t group
= dispatch_group_create();
1555 __block
bool result
= false;
1556 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
);
1557 // Add a task to the group
1558 dispatch_group_async(group
, queue
, ^{
1559 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1560 return sosAccountLeaveCircle(account
, circle
, error
);
1563 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
1564 dispatch_group_wait(group
, milestone
);
1566 account
->departure_code
= kSOSWithdrewMembership
;
1568 dispatch_release(group
);
1574 // MARK: Application
1577 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
,
1578 bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) {
1579 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1580 CFErrorRef peer_error
= NULL
;
1581 if (account
->trusted_circle
&& me
&&
1582 SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) {
1583 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) {
1584 __block
bool modified
= false;
1585 CFArrayForEach(peer_infos
, ^(const void *value
) {
1586 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1587 if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) {
1588 if (action(circle
, account
->my_identity
, peer
)) {
1597 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
1598 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
1601 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1602 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1606 __block
bool success
= true;
1607 __block
int64_t num_peers
= 0;
1609 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1610 bool accepted
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
);
1614 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1621 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1622 __block
bool success
= true;
1623 __block
int64_t num_peers
= 0;
1625 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1626 bool rejected
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
);
1630 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1638 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) {
1639 return CFSTR("We're compatible, go away");
1642 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) {
1643 return account
->departure_code
;
1646 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) {
1647 account
->departure_code
= reason
;
1651 CFArrayRef
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef
*error
) {
1652 CFArrayRef result
= NULL
;
1653 CFNumberRef generation
= NULL
;
1655 require_quiet(SOSAccountHasPublicKey(account
, error
), fail
);
1656 require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle")));
1658 generation
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
);
1659 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
);
1665 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef
*error
) {
1666 if (!SOSAccountHasPublicKey(account
, error
))
1669 return account
->user_public_trusted
;
1672 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef
*error
) {
1673 // TODO: this result is never set or used
1676 secnotice("updates", "Ensuring peer registration.");
1678 require_quiet(account
->trusted_circle
, done
);
1679 require_quiet(account
->my_identity
, done
);
1680 // If we are not in the circle, there is no point in setting up peers
1681 require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
);
1683 // This code only uses the SOSFullPeerInfoRef for two things:
1684 // - Finding out if this device is in the trusted circle
1685 // - Using the peerID for this device to see if the current peer is "me"
1686 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1688 CFStringRef my_id
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1690 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1691 if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) {
1692 CFErrorRef localError
= NULL
;
1693 SOSTransportMessageRef messageTransport
= NULL
;
1695 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1696 messageTransport
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport
: account
->kvs_message_transport
;
1699 messageTransport
= account
->kvs_message_transport
;
1701 SOSPeerCoderInitializeForPeer(messageTransport
->engine
, account
->my_identity
, peer
, &localError
);
1703 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
);
1704 CFReleaseSafe(localError
);
1708 //Initialize our device ID
1709 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1710 SOSTransportMessageIDSGetIDSDeviceID(account
);
1717 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef
*error
) {
1718 if (!account
->expansion
) {
1719 account
->expansion
= CFDictionaryCreateMutableForCFTypes(NULL
);
1722 return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary"));
1725 bool SOSAccountClearValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1726 bool success
= SOSAccountEnsureExpansion(account
, error
);
1727 require_quiet(success
, errOut
);
1729 CFDictionaryRemoveValue(account
->expansion
, key
);
1734 bool SOSAccountSetValue(SOSAccountRef account
, const void *key
, const void *value
, CFErrorRef
*error
) {
1735 bool success
= SOSAccountEnsureExpansion(account
, error
);
1736 require_quiet(success
, errOut
);
1738 CFDictionarySetValue(account
->expansion
, key
, value
);
1744 const void *SOSAccountGetValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1745 if (!account
->expansion
) {
1748 return CFDictionaryGetValue(account
->expansion
, key
);
1751 bool SOSAccountAddEscrowRecords(SOSAccountRef account
, CFStringRef dsid
, CFDictionaryRef record
, CFErrorRef
*error
){
1752 CFMutableDictionaryRef escrowRecords
= (CFMutableDictionaryRef
)SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1753 CFMutableDictionaryRef escrowCopied
= NULL
;
1754 bool success
= false;
1756 if(isDictionary(escrowRecords
) && escrowRecords
!= NULL
)
1757 escrowCopied
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
);
1759 escrowCopied
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1761 CFDictionaryAddValue(escrowCopied
, dsid
, record
);
1762 SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
);
1767 CFReleaseNull(escrowCopied
);
1773 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef
*error
){
1774 bool success
= false;
1776 CFDictionaryRef escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1777 success
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
);
1782 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef
*error
)
1784 CFMutableDictionaryRef circleToPeerMessages
= NULL
;
1785 CFStringRef messageString
= NULL
;
1786 CFMutableDictionaryRef mutableDictionary
= NULL
;
1787 CFMutableSetRef peers
= NULL
;
1788 CFMutableDictionaryRef peerList
= NULL
;
1789 char* message
= NULL
;
1790 bool result
= false;
1791 if(account
->ids_message_transport
== NULL
)
1792 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1794 require_quiet(account
->ids_message_transport
, fail
);
1795 circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1797 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
1799 asprintf(&message
, "%d", kIDSPeerAvailability
);
1800 messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
);
1802 mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("checking peers"), NULL
);
1804 //make sure there are peers in the circle
1805 peers
= SOSCircleCopyPeers(account
->trusted_circle
, kCFAllocatorDefault
);
1806 require_quiet(CFSetGetCount(peers
) > 0, fail
);
1807 CFReleaseNull(peers
);
1809 peerList
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1810 SOSCircleRef circle
= account
->trusted_circle
;
1812 //check each peer to make sure they have the right view set enabled
1813 CFSetRef mySubSet
= SOSViewsGetV0SubviewSet();
1814 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
1815 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))){
1816 CFMutableSetRef peerViews
= SOSPeerInfoCopyEnabledViews(peer
);
1817 CFSetRef intersectSets
= CFSetCreateIntersection(kCFAllocatorDefault
, mySubSet
, peerViews
);
1818 if(CFEqualSafe(intersectSets
, mySubSet
)){
1819 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
1820 if(deviceID
!= NULL
)
1821 CFDictionaryAddValue(peerList
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1822 CFReleaseNull(deviceID
);
1824 CFReleaseNull(peerViews
);
1825 CFReleaseNull(intersectSets
);
1829 require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
);
1830 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerList
);
1831 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1834 CFReleaseNull(mutableDictionary
);
1835 CFReleaseNull(messageString
);
1836 CFReleaseNull(peerList
);
1837 CFReleaseNull(circleToPeerMessages
);
1838 CFReleaseNull(peers
);
1844 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) {
1845 if (!SOSAccountIsInCircle(account
, NULL
))
1848 SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) {
1849 __block
bool updated
= false;
1850 CFSetForEach(account
->retirees
, ^(CFTypeRef element
){
1851 SOSPeerInfoRef retiree
= asSOSPeerInfo(element
);
1853 if (retiree
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) {
1855 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
);
1856 CFErrorRef cleanupError
= NULL
;
1857 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
))
1858 secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
);
1859 CFReleaseSafe(cleanupError
);
1866 void SOSAccountFinishTransaction(SOSAccountRef account
) {
1867 if(account
->circle_rings_retirements_need_attention
){
1868 SOSAccountRecordRetiredPeersInCircle(account
);
1870 CFErrorRef localError
= NULL
;
1871 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &localError
)) {
1872 secerror("flush circle failed %@", localError
);
1874 CFReleaseSafe(localError
);
1876 SOSAccountNotifyEngines(account
); // For now our only rings are backup rings.
1879 SOSAccountCheckHasBeenInSync(account
);
1881 account
->circle_rings_retirements_need_attention
= false;