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
) {
162 if (account
->trusted_circle
&& account
->my_identity
) {
163 if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
)) {
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 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
563 if(deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
565 secerror("Cannot sync with all peers at this time, securityd needs the IDS device ID first.");
567 __block
bool success
= true;
569 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
570 success
= (sync_error
== NULL
);
572 CFRetainAssign(error
, sync_error
);
577 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", error
);
580 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
583 CFReleaseNull(deviceID
);
586 require_action_quiet(account
->trusted_circle
, xit
,
587 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account trusted circle not set"), NULL
, &error
));
589 require_action_quiet(hasID
, xit
,
590 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Missing IDS device ID"), NULL
, &error
));
591 ok
= SOSCircleHasPeerWithID(account
->trusted_circle
,
592 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)), &error
);
595 secerror("sync with device failure: %@", error
);
597 CFReleaseSafe(error
);
601 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef peerID
) {
602 SOSPeerInfoRef mypi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
603 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(mypi
);
605 return myPeerID
&& CFEqualSafe(myPeerID
, peerID
);
608 static bool isDefaultsWriteSetupToSyncOverIDS(){
609 return ((whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
));
612 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef
*error
)
615 __block
bool SyncingCompletedOverIDS
= true;
616 __block
bool SyncingCompletedOverKVS
= true;
617 __block CFErrorRef localError
= NULL
;
618 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
619 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
620 CFMutableArrayRef peerIds
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
622 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
623 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
626 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
627 if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) {
628 if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)) {
629 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
630 CFMutableDictionaryRef circleToIdsId
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
631 CFMutableArrayRef ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
632 CFArrayAppendValue(ids
, SOSPeerInfoGetPeerID(peer
));
633 CFDictionaryAddValue(circleToIdsId
, SOSCircleGetName(circle
), ids
);
634 SyncingCompletedOverIDS
= SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, circleToIdsId
, &localError
);
635 CFReleaseNull(circleToIdsId
);
637 CFArrayAppendValue(peerIds
, SOSPeerInfoGetPeerID(peer
));
641 if (CFArrayGetCount(peerIds
)) {
642 secnotice("KVS", "Syncing with KVS capable peers");
643 CFDictionarySetValue(circleToPeerIDs
, SOSCircleGetName(circle
), peerIds
);
644 SyncingCompletedOverKVS
&= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToPeerIDs
, &localError
);
647 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->kvs_message_transport
);
648 result
= SOSEngineSyncWithPeers(engine
, account
->ids_message_transport
, account
->kvs_message_transport
, &localError
);
650 result
&= ((SyncingCompletedOverIDS
) &&
651 (SyncingCompletedOverKVS
|| (CFDictionaryGetCount(circleToPeerIDs
) == 0)));
654 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
657 CFReleaseNull(circleToPeerIDs
);
660 secdebug("Account", "Could not sync with all peers: %@", localError
);
661 CFErrorPropagate(localError
, error
);
664 CFReleaseNull(peerIds
);
665 CFReleaseSafe(localError
);
669 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
670 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
674 SOSPeerInfoRef myPeerInfo
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
675 require_action_quiet(account
->my_identity
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer")));
676 require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
);
678 CFStringRef cleanupPeerID
= SOSPeerInfoGetPeerID(cleanupPeer
);
680 CFStringRef circle_name
= SOSCircleGetName(circle
);
682 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
683 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
);
685 CFErrorRef localError
= NULL
;
686 if (!(success
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) {
687 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
690 if (account
->ids_message_transport
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) {
691 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
694 CFReleaseNull(localError
);
696 if((success
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) {
697 if (!(success
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) {
698 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
);
701 CFReleaseNull(localError
);
702 CFReleaseNull(circleToPeerIDs
);
708 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
709 CFMutableSetRef retirees_to_remove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
711 __block
bool success
= true;
713 CFSetForEach(account
->retirees
, ^(const void *value
) {
714 SOSPeerInfoRef retiree
= (SOSPeerInfoRef
) value
;
717 // 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.
718 if (!SOSPeerInfoIsRetirementTicket(retiree
) ||
719 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) {
720 CFSetAddValue(retirees_to_remove
, retiree
);
725 CFMutableArrayRef retirees_to_cleanup
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
726 CFSetForEach(retirees_to_remove
, ^(const void *value
) {
727 CFArrayAppendValue(retirees_to_cleanup
, value
);
728 CFSetRemoveValue(account
->retirees
, value
);
731 CFReleaseNull(retirees_to_remove
);
733 CFDictionaryRef retirements_to_remove
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
734 SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
,
737 CFReleaseNull(retirees_to_cleanup
);
739 success
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
);
741 CFReleaseNull(retirements_to_remove
);
746 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
) {
747 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
748 CFSetSetValue(account
->retirees
, peer
);
749 CFErrorRef cleanupError
= NULL
;
750 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) {
751 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
);
753 CFReleaseSafe(cleanupError
);
758 SOSCircleRef
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef
*error
) {
759 SOSCircleRef new_circle
= SOSCircleCopyCircle(NULL
, starting_circle
, error
);
760 if(!new_circle
) return NULL
;
762 if (account
->retirees
) {
763 CFSetForEach(account
->retirees
, ^(const void* value
) {
764 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
765 if (isSOSPeerInfo(pi
)) {
766 SOSCircleUpdatePeerInfo(new_circle
, pi
);
771 if(SOSCircleCountPeers(new_circle
) == 0) {
772 SOSCircleResetToEmpty(new_circle
, NULL
);
779 // MARK: Circle Membership change notificaion
782 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
783 SOSAccountCircleMembershipChangeBlock copy
= Block_copy(changeBlock
);
784 CFArrayAppendValue(a
->change_blocks
, copy
);
788 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
789 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
792 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) {
793 if (!changeBlock
) return;
795 CFRetainSafe(ds_name
);
796 SOSAccountCircleMembershipChangeBlock block_to_register
= ^void (SOSCircleRef new_circle
,
797 CFSetRef added_peers
, CFSetRef removed_peers
,
798 CFSetRef added_applicants
, CFSetRef removed_applicants
) {
800 if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
))
803 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
);
804 CFStringRef myPi_id
= myPi
? SOSPeerInfoGetPeerID(myPi
) : NULL
;
806 CFMutableArrayRef peer_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
807 CFMutableArrayRef added_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
808 CFMutableArrayRef removed_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
810 if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) {
811 SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) {
812 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
);
815 CFSetForEach(added_peers
, ^(const void *value
) {
816 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
819 CFSetForEach(removed_peers
, ^(const void *value
) {
820 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
824 if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
))
825 changeBlock(peer_ids
, added_ids
, removed_ids
);
827 CFReleaseSafe(peer_ids
);
828 CFReleaseSafe(added_ids
);
829 CFReleaseSafe(removed_ids
);
832 CFRetainSafe(changeBlock
);
833 SOSAccountAddChangeBlock(a
, block_to_register
);
835 CFSetRef empty
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
836 if (a
->trusted_circle
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) {
837 block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
);
839 CFReleaseSafe(empty
);
842 void SOSAccountPurgeIdentity(SOSAccountRef account
) {
843 if (account
->my_identity
) {
844 // Purge private key but don't return error if we can't.
845 CFErrorRef purgeError
= NULL
;
846 if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) {
847 secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
);
849 CFReleaseNull(purgeError
);
851 CFReleaseNull(account
->my_identity
);
855 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
856 SOSFullPeerInfoRef fpi
= account
->my_identity
;
857 if(!fpi
) return false;
859 CFErrorRef localError
= NULL
;
863 SOSPeerInfoRef retire_peer
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
);
865 secerror("Create ticket failed for peer %@: %@", fpi
, localError
);
867 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
868 if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) {
869 // Remove our application if we have one.
870 SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
);
871 } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) {
872 if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) {
873 CFErrorRef cleanupError
= NULL
;
874 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) {
875 secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
);
877 CFReleaseSafe(cleanupError
);
881 // Store the retirement record locally.
882 CFSetAddValue(account
->retirees
, retire_peer
);
884 // Write retirement to Transport
885 CFErrorRef postError
= NULL
;
886 if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){
887 secwarning("Couldn't post retirement (%@)", postError
);
889 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){
890 secwarning("Couldn't flush retirement data (%@)", postError
);
892 CFReleaseNull(postError
);
895 SOSAccountPurgeIdentity(account
);
899 CFReleaseNull(localError
);
900 CFReleaseNull(retire_peer
);
904 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) {
905 SOSFullPeerInfoRef fpi
= account
->my_identity
;
906 if(!fpi
) return false;
907 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
908 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
910 CFErrorRef localError
= NULL
;
913 bool writeRing
= false;
914 bool writePeerInfo
= false;
916 if(SOSRingHasPeerID(ring
, peerID
)) {
917 writePeerInfo
= true;
921 // this was circle behavior - at some point
922 if(SOSRingHasApplicant(ring
, peerID
)) {
927 if(writePeerInfo
|| writeRing
) {
928 SOSRingWithdraw(ring
, NULL
, fpi
, error
);
931 // Write leave thing to Transport
932 CFDataRef peerInfoData
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
);
933 SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors?
936 CFDataRef ring_data
= SOSRingCopyEncodedData(ring
, error
);
939 SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors?
941 CFReleaseNull(ring_data
);
944 CFReleaseNull(localError
);
948 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef
*error
) {
950 SOSTransportCircleRef transport
= account
->circle_transport
;
952 result
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
);
958 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
959 local value that has been overwritten by a distant value. If there is no
960 conflict between the local and the distant values when doing the initial
961 sync (e.g. if the cloud has no data stored or the client has not stored
962 any data yet), you'll never see that notification.
964 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
965 with server but initial round trip with server does not imply
966 NSUbiquitousKeyValueStoreInitialSyncChange.
971 // MARK: Status summary
974 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) {
976 return kSOSCCNotInCircle
;
978 if (circle
&& SOSCircleCountPeers(circle
) == 0)
979 return kSOSCCCircleAbsent
;
983 if(SOSPeerInfoIsRetirementTicket(this_peer
))
984 return kSOSCCNotInCircle
;
986 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
987 return kSOSCCInCircle
;
989 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
990 return kSOSCCRequestPending
;
993 return kSOSCCNotInCircle
;
996 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef
*error
) {
997 SOSCCStatus result
= SOSAccountGetCircleStatus(account
, error
);
999 if (result
!= kSOSCCInCircle
&& result
!= kSOSCCError
) {
1000 SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle"));
1007 SOSCCStatus
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) {
1008 if (!SOSAccountHasPublicKey(account
, error
)) {
1012 return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
));
1016 // MARK: Account Reset Circles
1019 static bool SOSAccountResetCircleToOffering(SOSAccountRef account
, SecKeyRef user_key
, CFErrorRef
*error
) {
1020 bool result
= false;
1022 require(SOSAccountHasCircle(account
, error
), fail
);
1023 require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1025 (void) SOSAccountResetAllRings(account
, error
);
1027 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1028 bool result
= false;
1029 SOSFullPeerInfoRef cloud_identity
= NULL
;
1030 CFErrorRef localError
= NULL
;
1032 require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
);
1035 SOSPeerInfoRef cloud_peer
= GenerateNewCloudIdentityPeerInfo(error
);
1036 require_quiet(cloud_peer
, err_out
);
1037 cloud_identity
= CopyCloudKeychainIdentity(cloud_peer
, error
);
1038 CFReleaseNull(cloud_peer
);
1039 require_quiet(cloud_identity
, err_out
);
1042 account
->departure_code
= kSOSNeverLeftCircle
;
1043 require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
);
1044 require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, &localError
), err_out
);
1045 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), &localError
), err_out
);
1047 SOSAccountPublishCloudParameters(account
, NULL
);
1050 if (result
== false)
1051 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
1052 if (localError
&& error
&& *error
== NULL
) {
1053 *error
= localError
;
1056 CFReleaseNull(localError
);
1057 CFReleaseNull(cloud_identity
);
1068 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) {
1069 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1073 CFReleaseNull(account
->my_identity
);
1075 return user_key
&& SOSAccountResetCircleToOffering(account
, user_key
, error
);
1078 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
1079 if (!SOSAccountHasPublicKey(account
, error
))
1081 __block
bool result
= true;
1083 result
&= SOSAccountResetAllRings(account
, error
);
1085 CFReleaseNull(account
->my_identity
);
1087 account
->departure_code
= kSOSWithdrewMembership
;
1088 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1089 result
= SOSCircleResetToEmpty(circle
, error
);
1094 secerror("error: %@", error
? *error
: NULL
);
1102 // MARK: Waiting for in-sync
1105 static bool SOSAccountHasBeenInSync(SOSAccountRef account
) {
1106 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1107 CFSetRef unsynced
= asSet(unsyncedObject
, NULL
);
1109 return !(unsyncedObject
== kCFBooleanTrue
|| (unsynced
&& (CFSetGetCount(unsynced
) > 0)));
1112 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account
, CFSetRef viewsInSync
) {
1113 bool notifyOfChange
= false;
1115 SOSCCStatus circleStatus
= SOSAccountGetCircleStatus(account
, NULL
);
1116 bool inOrApplying
= (circleStatus
== kSOSCCInCircle
) || (circleStatus
== kSOSCCRequestPending
);
1118 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1120 if (!inOrApplying
) {
1121 if (unsyncedObject
!= NULL
) {
1122 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1123 secnotice("initial-sync", "in sync, clearing pending");
1124 notifyOfChange
= true;
1126 } else if (circleStatus
== kSOSCCInCircle
) {
1127 __block CFMutableSetRef viewsToSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1128 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1129 SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) {
1130 CFSetUnion(viewsToSync
, enabled
);
1135 CFSetSubtract(viewsToSync
, viewsInSync
);
1139 if (unsyncedObject
== kCFBooleanTrue
) {
1140 if (CFSetGetCount(viewsToSync
) == 0) {
1141 secnotice("initial-sync", "No views to wait for");
1142 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1144 __block CFSetRef newViews
= NULL
;
1145 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account
), ^(CFSetRef enabled
) {
1146 newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, enabled
, viewsToSync
);
1148 secnotice("initial-sync", "Pending views set from True: %@", newViews
);
1149 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1150 CFReleaseNull(newViews
);
1152 notifyOfChange
= true;
1153 } else if (isSet(unsyncedObject
)) {
1154 CFSetRef waiting
= (CFMutableSetRef
) unsyncedObject
;
1155 CFSetRef newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, waiting
, viewsToSync
);
1156 if (!CFEqualSafe(waiting
, newViews
)) {
1157 secnotice("initial-sync", "Pending views updated: %@", newViews
);
1158 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1159 notifyOfChange
= true;
1161 CFReleaseNull(newViews
);
1164 CFReleaseNull(viewsToSync
);
1167 if (notifyOfChange
) {
1168 secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification
);
1169 notify_post(kSOSCCInitialSyncChangedNotification
);
1170 // Make sure we update the engine
1171 account
->circle_rings_retirements_need_attention
= true;
1174 return SOSAccountHasBeenInSync(account
);
1177 static void SOSAccountPeerGotInSync(SOSAccountRef account
, CFStringRef peerID
) {
1178 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID
);
1180 if (account
->trusted_circle
) {
1181 SOSPeerInfoRef peer
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
);
1183 CFSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
1184 SOSAccountUpdateOutOfSyncViews(account
, views
);
1185 CFReleaseNull(views
);
1187 CFReleaseNull(peer
);
1191 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account
) {
1192 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1194 CFDictionaryForEach(account
->notification_cleanups
, ^(const void *key
, const void *value
) {
1196 SOSEngineSetSyncCompleteListener(engine
, key
, NULL
);
1198 dispatch_async(account
->queue
, value
);
1201 CFDictionaryRemoveAllValues(account
->notification_cleanups
);
1204 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account
, CFStringRef peerID
) {
1205 dispatch_block_t cleanup
= CFDictionaryGetValue(account
->notification_cleanups
, peerID
);
1208 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1211 SOSEngineSetSyncCompleteListener(engine
, peerID
, NULL
);
1214 dispatch_async(account
->queue
, cleanup
);
1217 CFDictionaryRemoveValue(account
->notification_cleanups
, peerID
);
1221 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account
, CFStringRef peerID
, dispatch_block_t block
) {
1222 dispatch_block_t copy
= Block_copy(block
);
1223 CFDictionarySetValue(account
->notification_cleanups
, peerID
, copy
);
1224 CFReleaseNull(copy
);
1227 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) {
1228 if (CFDictionaryGetCount(account
->notification_cleanups
) == 0) {
1229 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
1230 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1232 SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
);
1235 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1236 CFStringRef peerID
= CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerID(peer
));
1238 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID
);
1239 SOSAccountRegisterCleanupBlock(account
, peerID
, ^{
1240 CFReleaseSafe(peerID
);
1243 SOSEngineSetSyncCompleteListener(engine
, peerID
, ^{
1244 SOSAccountPeerGotInSync(account
, peerID
);
1245 SOSAccountCleanupNotificationForPeer(account
, peerID
);
1246 SOSAccountFinishTransaction(account
);
1250 secerror("Couldn't find engine to setup notifications!!!");
1255 void SOSAccountCancelSyncChecking(SOSAccountRef account
) {
1256 SOSAccountCleanupNotificationForAllPeers(account
);
1257 SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1260 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account
) {
1261 bool hasBeenInSync
= false;
1263 if (!SOSAccountIsInCircle(account
, NULL
)) {
1264 SOSAccountCancelSyncChecking(account
);
1266 hasBeenInSync
= SOSAccountHasBeenInSync(account
);
1267 if (!hasBeenInSync
) {
1268 hasBeenInSync
= SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1269 if (hasBeenInSync
) {
1270 // Cancel and declare victory
1271 SOSAccountCancelSyncChecking(account
);
1273 // Make sure we're watching in case this is the fist attempt
1274 SOSAccountEnsureSyncChecking(account
);
1279 return hasBeenInSync
;
1286 static bool SOSAccountJoinCircle(SOSAccountRef account
, SecKeyRef user_key
,
1287 bool use_cloud_peer
, CFErrorRef
* error
) {
1288 __block
bool result
= false;
1289 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1291 require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???")));
1292 require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1294 SOSFullPeerInfoRef myCirclePeer
= account
->my_identity
;
1296 if (use_cloud_peer
) {
1297 cloud_full_peer
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
);
1299 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1302 if (SOSCircleCountPeers(account
->trusted_circle
) == 0) {
1303 result
= SOSAccountResetCircleToOffering(account
, user_key
, error
);
1305 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1306 result
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
);
1307 result
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
);
1308 account
->departure_code
= kSOSNeverLeftCircle
;
1309 if(result
&& cloud_full_peer
) {
1310 CFErrorRef localError
= NULL
;
1311 CFStringRef cloudid
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
));
1312 require_quiet(cloudid
, finish
);
1313 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
);
1314 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
);
1317 secerror("Failed to join with cloud identity: %@", localError
);
1318 CFReleaseNull(localError
);
1326 CFReleaseNull(cloud_full_peer
);
1330 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) {
1331 bool success
= false;
1333 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1334 require_quiet(user_key
, done
); // Fail if we don't get one.
1336 require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join")));
1338 if (account
->my_identity
!= NULL
) {
1339 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1340 success
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
);
1341 require_quiet(!success
, done
);
1343 SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now.
1345 if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) {
1346 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
));
1348 CFReleaseNull(account
->my_identity
);
1353 success
= SOSAccountJoinCircle(account
, user_key
, use_cloud_identity
, error
);
1355 require_quiet(success
, done
);
1357 account
->departure_code
= kSOSNeverLeftCircle
;
1363 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) {
1364 return SOSAccountJoinCircles_internal(account
, false, error
);
1367 CFStringRef
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef
*error
){
1368 CFStringRef result
= NULL
;
1370 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1372 result
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1378 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){
1381 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
){
1382 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1383 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1384 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1386 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1388 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1389 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDS
, error
);
1390 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1392 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1399 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1400 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1401 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1403 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1405 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1406 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeKVS
, error
);
1407 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1409 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1417 SOSCCSyncWithAllPeers();
1424 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1426 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1427 //construct message dictionary, circle -> peerID -> message
1429 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1430 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1432 char *messageCharStar
;
1433 asprintf(&messageCharStar
, "%d", kIDSSendOneMessage
);
1434 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1436 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1438 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1439 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
)))
1440 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1443 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1444 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1446 CFReleaseNull(mutableDictionary
);
1447 CFReleaseNull(peerToMessage
);
1448 CFReleaseNull(circleToPeerMessages
);
1449 CFReleaseNull(messageString
);
1450 free(messageCharStar
);
1455 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1456 bool result
= false;
1457 //construct message dictionary, circle -> peerID -> message
1459 if(account
->ids_message_transport
== NULL
)
1460 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1462 require_quiet(account
->ids_message_transport
, fail
);
1463 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1464 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1466 char *messageCharStar
;
1467 asprintf(&messageCharStar
, "%d", kIDSStartPingTestMessage
);
1468 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1470 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1472 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1473 if(CFStringCompare(SOSAccountGetMyPeerID(account
), SOSPeerInfoGetPeerID(peer
), 0) != 0)
1474 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1477 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1478 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1480 CFReleaseNull(mutableDictionary
);
1481 CFReleaseNull(peerToMessage
);
1482 CFReleaseNull(circleToPeerMessages
);
1483 CFReleaseNull(messageString
);
1484 free(messageCharStar
);
1489 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef
*error
){
1491 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1493 __block
bool success
= true;
1494 __block CFErrorRef localError
= NULL
;
1495 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
1496 dispatch_retain(wait_for
); // Both this scope and the block own it
1498 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1499 success
= (sync_error
== NULL
);
1501 CFRetainAssign(localError
, sync_error
);
1504 dispatch_semaphore_signal(wait_for
);
1505 dispatch_release(wait_for
);
1508 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
1509 dispatch_release(wait_for
);
1511 if(!success
&& localError
!= NULL
&& error
!= NULL
){
1512 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1513 *error
= localError
;
1516 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1522 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) {
1523 return SOSAccountJoinCircles_internal(account
, true, error
);
1527 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
)
1531 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1532 return sosAccountLeaveCircle(account
, circle
, error
);
1535 account
->departure_code
= kSOSWithdrewMembership
;
1540 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
)
1542 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1548 CFMutableSetRef peersToRemove
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
);
1550 bool leaveCircle
= CFSetContainsValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1552 CFSetRemoveValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1554 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1555 bool success
= false;
1557 require_quiet(SOSCircleRemovePeers(circle
, user_key
, SOSAccountGetMyFullPeerInfo(account
), peersToRemove
, error
), done
);
1560 success
= sosAccountLeaveCircle(account
, circle
, error
);
1562 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
1574 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) {
1575 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
1576 dispatch_group_t group
= dispatch_group_create();
1577 __block
bool result
= false;
1578 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
);
1579 // Add a task to the group
1580 dispatch_group_async(group
, queue
, ^{
1581 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1582 return sosAccountLeaveCircle(account
, circle
, error
);
1585 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
1586 dispatch_group_wait(group
, milestone
);
1588 account
->departure_code
= kSOSWithdrewMembership
;
1590 dispatch_release(group
);
1596 // MARK: Application
1599 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
,
1600 bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) {
1601 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1602 CFErrorRef peer_error
= NULL
;
1603 if (account
->trusted_circle
&& me
&&
1604 SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) {
1605 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) {
1606 __block
bool modified
= false;
1607 CFArrayForEach(peer_infos
, ^(const void *value
) {
1608 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1609 if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) {
1610 if (action(circle
, account
->my_identity
, peer
)) {
1619 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
1620 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
1623 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1624 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1628 __block
bool success
= true;
1629 __block
int64_t num_peers
= 0;
1631 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1632 bool accepted
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
);
1636 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1643 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1644 __block
bool success
= true;
1645 __block
int64_t num_peers
= 0;
1647 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1648 bool rejected
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
);
1652 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1660 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) {
1661 return CFSTR("We're compatible, go away");
1664 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) {
1665 return account
->departure_code
;
1668 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) {
1669 account
->departure_code
= reason
;
1673 CFArrayRef
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef
*error
) {
1674 CFArrayRef result
= NULL
;
1675 CFNumberRef generation
= NULL
;
1677 require_quiet(SOSAccountHasPublicKey(account
, error
), fail
);
1678 require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle")));
1680 generation
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
);
1681 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
);
1687 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef
*error
) {
1688 if (!SOSAccountHasPublicKey(account
, error
))
1691 return account
->user_public_trusted
;
1694 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef
*error
) {
1695 // TODO: this result is never set or used
1698 secnotice("updates", "Ensuring peer registration.");
1700 require_quiet(account
->trusted_circle
, done
);
1701 require_quiet(account
->my_identity
, done
);
1702 // If we are not in the circle, there is no point in setting up peers
1703 require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
);
1705 // This code only uses the SOSFullPeerInfoRef for two things:
1706 // - Finding out if this device is in the trusted circle
1707 // - Using the peerID for this device to see if the current peer is "me"
1708 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1710 CFStringRef my_id
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1712 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1713 if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) {
1714 CFErrorRef localError
= NULL
;
1715 SOSTransportMessageRef messageTransport
= NULL
;
1717 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1718 messageTransport
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport
: account
->kvs_message_transport
;
1721 messageTransport
= account
->kvs_message_transport
;
1723 SOSPeerCoderInitializeForPeer(messageTransport
->engine
, account
->my_identity
, peer
, &localError
);
1725 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
);
1726 CFReleaseSafe(localError
);
1730 //Initialize our device ID
1731 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1732 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1733 if( deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
1735 __block
bool success
= true;
1736 __block CFErrorRef localError
= NULL
;
1738 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1739 success
= (sync_error
== NULL
);
1741 CFRetainAssign(localError
, sync_error
);
1745 if(!success
&& localError
!= NULL
&& error
!= NULL
){
1746 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1747 *error
= localError
;
1750 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1752 CFReleaseNull(localError
);
1754 CFReleaseNull(deviceID
);
1761 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef
*error
) {
1762 if (!account
->expansion
) {
1763 account
->expansion
= CFDictionaryCreateMutableForCFTypes(NULL
);
1766 return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary"));
1769 bool SOSAccountClearValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1770 bool success
= SOSAccountEnsureExpansion(account
, error
);
1771 require_quiet(success
, errOut
);
1773 CFDictionaryRemoveValue(account
->expansion
, key
);
1778 bool SOSAccountSetValue(SOSAccountRef account
, const void *key
, const void *value
, CFErrorRef
*error
) {
1779 bool success
= SOSAccountEnsureExpansion(account
, error
);
1780 require_quiet(success
, errOut
);
1782 CFDictionarySetValue(account
->expansion
, key
, value
);
1788 const void *SOSAccountGetValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1789 if (!account
->expansion
) {
1792 return CFDictionaryGetValue(account
->expansion
, key
);
1795 bool SOSAccountAddEscrowRecords(SOSAccountRef account
, CFStringRef dsid
, CFDictionaryRef record
, CFErrorRef
*error
){
1796 CFMutableDictionaryRef escrowRecords
= (CFMutableDictionaryRef
)SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1797 CFMutableDictionaryRef escrowCopied
= NULL
;
1798 bool success
= false;
1800 if(isDictionary(escrowRecords
) && escrowRecords
!= NULL
)
1801 escrowCopied
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
);
1803 escrowCopied
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1805 CFDictionaryAddValue(escrowCopied
, dsid
, record
);
1806 SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
);
1811 CFReleaseNull(escrowCopied
);
1817 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef
*error
){
1818 bool success
= false;
1820 CFDictionaryRef escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1821 success
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
);
1826 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef
*error
)
1828 CFMutableDictionaryRef circleToPeerMessages
= NULL
;
1829 CFStringRef messageString
= NULL
;
1830 CFMutableDictionaryRef mutableDictionary
= NULL
;
1831 CFMutableSetRef peers
= NULL
;
1832 CFMutableDictionaryRef peerList
= NULL
;
1833 char* message
= NULL
;
1834 bool result
= false;
1835 if(account
->ids_message_transport
== NULL
)
1836 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1838 require_quiet(account
->ids_message_transport
, fail
);
1839 circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1841 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
1843 asprintf(&message
, "%d", kIDSPeerAvailability
);
1844 messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
);
1846 mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("checking peers"), NULL
);
1848 //make sure there are peers in the circle
1849 peers
= SOSCircleCopyPeers(account
->trusted_circle
, kCFAllocatorDefault
);
1850 require_quiet(CFSetGetCount(peers
) > 0, fail
);
1851 CFReleaseNull(peers
);
1853 peerList
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1854 SOSCircleRef circle
= account
->trusted_circle
;
1856 //check each peer to make sure they have the right view set enabled
1857 CFSetRef mySubSet
= SOSViewsGetV0SubviewSet();
1858 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
1859 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))){
1860 CFMutableSetRef peerViews
= SOSPeerInfoCopyEnabledViews(peer
);
1861 CFSetRef intersectSets
= CFSetCreateIntersection(kCFAllocatorDefault
, mySubSet
, peerViews
);
1862 if(CFEqualSafe(intersectSets
, mySubSet
)){
1863 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
1864 if(deviceID
!= NULL
)
1865 CFDictionaryAddValue(peerList
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1866 CFReleaseNull(deviceID
);
1868 CFReleaseNull(peerViews
);
1869 CFReleaseNull(intersectSets
);
1873 require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
);
1874 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerList
);
1875 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1878 CFReleaseNull(mutableDictionary
);
1879 CFReleaseNull(messageString
);
1880 CFReleaseNull(peerList
);
1881 CFReleaseNull(circleToPeerMessages
);
1882 CFReleaseNull(peers
);
1888 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) {
1889 if (!SOSAccountIsInCircle(account
, NULL
))
1892 SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) {
1893 __block
bool updated
= false;
1894 CFSetForEach(account
->retirees
, ^(CFTypeRef element
){
1895 SOSPeerInfoRef retiree
= asSOSPeerInfo(element
);
1897 if (retiree
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) {
1899 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
);
1900 CFErrorRef cleanupError
= NULL
;
1901 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
))
1902 secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
);
1903 CFReleaseSafe(cleanupError
);
1910 void SOSAccountFinishTransaction(SOSAccountRef account
) {
1911 if(account
->circle_rings_retirements_need_attention
){
1912 SOSAccountRecordRetiredPeersInCircle(account
);
1914 CFErrorRef localError
= NULL
;
1915 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &localError
)) {
1916 secerror("flush circle failed %@", localError
);
1918 CFReleaseSafe(localError
);
1920 SOSAccountNotifyEngines(account
); // For now our only rings are backup rings.
1923 SOSAccountCheckHasBeenInSync(account
);
1925 account
->circle_rings_retirements_need_attention
= false;