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"); 
  38 #define DATE_LENGTH 25 
  39 const CFStringRef kSOSAccountDebugScope 
= CFSTR("Scope"); 
  42 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
) 
  45     CFStringRef circle_name 
= NULL
; 
  47     require_quiet(a
, xit
); 
  48     require_quiet(a
->factory
, xit
); 
  50     circle_name 
= SOSDataSourceFactoryCopyName(a
->factory
); 
  51     require(circle_name
, xit
); 
  53     SOSAccountEnsureCircle(a
, circle_name
, NULL
); 
  58     // We don't own name, so don't release it. 
  59     CFReleaseNull(circle_name
); 
  64 SOSAccountRef 
SOSAccountCreateBasic(CFAllocatorRef allocator
, 
  65                                     CFDictionaryRef gestalt
, 
  66                                     SOSDataSourceFactoryRef factory
) { 
  67     SOSAccountRef a 
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
); 
  69     a
->queue 
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
); 
  71     a
->notification_cleanups 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
  73     a
->gestalt 
= CFRetainSafe(gestalt
); 
  75     a
->trusted_circle 
= NULL
; 
  76     a
->trusted_rings 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
  77     a
->backups 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
  78     a
->my_identity 
= NULL
; 
  79     a
->retirees 
= CFSetCreateMutableForSOSPeerInfosByID(allocator
); 
  81     a
->factory 
= factory
; // We adopt the factory. kthanksbai. 
  83     a
->_user_private 
= NULL
; 
  84     a
->_password_tmp 
= NULL
; 
  85     a
->user_private_timer 
= NULL
; 
  87     a
->change_blocks 
= CFArrayCreateMutableForCFTypes(allocator
); 
  88     a
->waitForInitialSync_blocks 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
  89     a
->departure_code 
= kSOSNeverAppliedToCircle
; 
  91     a
->key_transport 
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
); 
  92     a
->circle_transport 
= NULL
; 
  93     a
->kvs_message_transport 
= NULL
; 
  94     a
->ids_message_transport 
= NULL
; 
  95     a
->expansion 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
 100 SOSSecurityPropertyResultCode 
SOSAccountUpdateSecurityProperty(SOSAccountRef account
, CFStringRef property
, SOSSecurityPropertyActionCode actionCode
, CFErrorRef 
*error
) { 
 101     SOSSecurityPropertyResultCode retval 
= kSOSCCGeneralSecurityPropertyError
; 
 102     bool updateCircle 
= false; 
 103     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 104     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 105     retval 
= SOSFullPeerInfoUpdateSecurityProperty(account
->my_identity
, actionCode
, property
, error
); 
 107     if(actionCode 
== kSOSCCSecurityPropertyEnable 
&& retval 
== kSOSCCSecurityPropertyValid
) { 
 109     } else if(actionCode 
== kSOSCCSecurityPropertyDisable 
&& retval 
== kSOSCCSecurityPropertyNotValid
) { 
 111     } else if(actionCode 
== kSOSCCSecurityPropertyPending
) { 
 116         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 117             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change"); 
 118             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 126 SOSSecurityPropertyResultCode 
SOSAccountSecurityPropertyStatus(SOSAccountRef account
, CFStringRef property
, CFErrorRef 
*error
) { 
 127     SOSSecurityPropertyResultCode retval 
= kSOSCCGeneralViewError
; 
 128     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 129     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 130     retval 
= SOSFullPeerInfoSecurityPropertyStatus(account
->my_identity
, property
, error
); 
 135 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
) 
 137     if (CFEqualSafe(new_gestalt
, account
->gestalt
)) 
 140     if (account
->trusted_circle 
&& account
->my_identity
 
 141         && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) { 
 142         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 143             secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change"); 
 144             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
)); 
 148     CFRetainAssign(account
->gestalt
, new_gestalt
); 
 152 bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){ 
 153     SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
); 
 154     //send new DSID over account changed 
 155     SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
); 
 160 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
, CFSetRef excludedViews
) { 
 161     if (account
->trusted_circle 
&& account
->my_identity
) { 
 162         if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
, excludedViews
)) { 
 163             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 164                 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change"); 
 165                 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 173 SOSViewResultCode 
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef 
*error
) { 
 174     SOSViewResultCode retval 
= kSOSCCGeneralViewError
; 
 175     SOSViewResultCode currentStatus 
= kSOSCCGeneralViewError
; 
 176     bool updateCircle 
= false; 
 177     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 178     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 179     require_action_quiet((actionCode 
== kSOSCCViewEnable
) || (actionCode 
== kSOSCCViewDisable
), errOut
, CFSTR("Invalid View Action")); 
 180     currentStatus 
= SOSAccountViewStatus(account
, viewname
, error
); 
 181     require_action_quiet((currentStatus 
== kSOSCCViewNotMember
) || (currentStatus 
== kSOSCCViewMember
), errOut
, CFSTR("View Membership Not Actionable")); 
 183     if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) { 
 184         // The V0 view switches on and off all on it's own, we allow people the delusion 
 185         // of control and status if it's what we're stuck at., otherwise error. 
 186         if (SOSAccountSyncingV0(account
)) { 
 187             require_action_quiet(actionCode 
= kSOSCCViewDisable
, errOut
, CFSTR("Can't disable V0 view and it's on right now")); 
 188             retval 
= kSOSCCViewMember
; 
 190             require_action_quiet(actionCode 
= kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now")); 
 191             retval 
= kSOSCCViewNotMember
; 
 193     } else if (SOSAccountSyncingV0(account
) && SOSViewsIsV0Subview(viewname
)) { 
 194         // Subviews of V0 syncing can't be turned off if V0 is on. 
 195         require_action_quiet(actionCode 
= kSOSCCViewDisable
, errOut
, CFSTR("Have V0 peer can't disable")); 
 196         retval 
= kSOSCCViewMember
; 
 198         if(actionCode 
== kSOSCCViewEnable 
&& currentStatus 
== kSOSCCViewNotMember
) { 
 199             retval 
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
); 
 200             if(retval 
== kSOSCCViewMember
) updateCircle 
= true; 
 201         } else if(actionCode 
== kSOSCCViewDisable 
&& currentStatus 
== kSOSCCViewMember
) { 
 202             retval 
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
); 
 203             if(retval 
== kSOSCCViewNotMember
) updateCircle 
= true; 
 205             retval 
= currentStatus
; 
 209             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 210                 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change"); 
 211                 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 220 SOSViewResultCode 
SOSAccountViewStatus(SOSAccountRef account
, CFStringRef viewname
, CFErrorRef 
*error
) { 
 221     SOSViewResultCode retval 
= kSOSCCGeneralViewError
; 
 222     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 223     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 225     retval 
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
); 
 227     // 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 
 228     if (retval 
!= kSOSCCViewMember
) { 
 229         if ((CFEqualSafe(viewname
, kSOSViewKeychainV0
) || SOSViewsIsV0Subview(viewname
)) 
 230           && SOSAccountSyncingV0(account
)) { 
 231             retval 
= kSOSCCViewMember
; 
 235     // If we're only an applicant we report pending if we would be a view member 
 236     if (retval 
== kSOSCCViewMember
) { 
 237         bool isApplicant 
= SOSCircleHasApplicant(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
), error
); 
 239             retval 
= kSOSCCViewPending
; 
 247 static void dumpViewSet(CFStringRef label
, CFSetRef views
) { 
 249         secnotice("circleChange", "%@ list: %@", label
, views
); 
 251         secnotice("circleChange", "No %@ list provided.", label
); 
 255 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef enabledViews
, CFSetRef disabledViews
) { 
 256     bool updateCircle 
= false; 
 257     dumpViewSet(CFSTR("Enabled"), enabledViews
); 
 258     dumpViewSet(CFSTR("Disabled"), disabledViews
); 
 260     require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle")); 
 261     require_action_quiet(account
->my_identity
, errOut
, secnotice("views", "Attempt to set viewsets with no fullPeerInfo")); 
 262     require_action_quiet(enabledViews 
|| disabledViews
, errOut
, secnotice("views", "No work to do")); 
 265     SOSFullPeerInfoRef fpi 
= SOSAccountGetMyFullPeerInfo(account
); 
 266     SOSPeerInfoRef  pi 
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
); 
 268     require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef")); 
 271     if(!SOSPeerInfoVersionIsCurrent(pi
)) { 
 272         if(!SOSPeerInfoUpdateToV2(pi
, NULL
)) { 
 273             secnotice("views", "Unable to update peer to V2- can't update views"); 
 278     if(enabledViews
) updateCircle 
= SOSViewSetEnable(pi
, enabledViews
); 
 279     if(disabledViews
) updateCircle 
|= SOSViewSetDisable(pi
, disabledViews
); 
 281     /* UPDATE FULLPEERINFO VIEWS */ 
 283     if (updateCircle 
&& SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
)) { 
 284         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 285             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change"); 
 286             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 295 SOSAccountRef 
SOSAccountCreate(CFAllocatorRef allocator
, 
 296                                CFDictionaryRef gestalt
, 
 297                                SOSDataSourceFactoryRef factory
) { 
 298     SOSAccountRef a 
= SOSAccountCreateBasic(allocator
, gestalt
, factory
); 
 300     SOSAccountEnsureFactoryCircles(a
); 
 302     SOSUpdateKeyInterest(a
); 
 307 static void SOSAccountDestroy(CFTypeRef aObj
) { 
 308     SOSAccountRef a 
= (SOSAccountRef
) aObj
; 
 310     // We don't own the factory, merely have a reference to the singleton 
 314     SOSAccountCleanupNotificationForAllPeers(a
); 
 316     SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
); 
 319         SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
); 
 321     dispatch_sync(a
->queue
, ^{ 
 322         CFReleaseNull(a
->gestalt
); 
 324         CFReleaseNull(a
->my_identity
); 
 325         CFReleaseNull(a
->trusted_circle
); 
 326         CFReleaseNull(a
->trusted_rings
); 
 327         CFReleaseNull(a
->backups
); 
 328         CFReleaseNull(a
->retirees
); 
 330         a
->user_public_trusted 
= false; 
 331         CFReleaseNull(a
->user_public
); 
 332         CFReleaseNull(a
->user_key_parameters
); 
 334         SOSAccountPurgePrivateCredential(a
); 
 335         CFReleaseNull(a
->previous_public
); 
 336         CFReleaseNull(a
->_user_private
); 
 337         CFReleaseNull(a
->_password_tmp
); 
 339         a
->departure_code 
= kSOSNeverAppliedToCircle
; 
 340         CFReleaseNull(a
->kvs_message_transport
); 
 341         CFReleaseNull(a
->ids_message_transport
); 
 342         CFReleaseNull(a
->key_transport
); 
 343         CFReleaseNull(a
->circle_transport
); 
 344         dispatch_release(a
->queue
); 
 345         CFReleaseNull(a
->notification_cleanups
); 
 347         dispatch_release(a
->user_private_timer
); 
 348         CFReleaseNull(a
->change_blocks
); 
 349         CFReleaseNull(a
->waitForInitialSync_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 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef 
*error
) 
 589     __block 
bool SyncingCompletedOverIDS 
= true; 
 590     __block 
bool SyncingCompletedOverKVS 
= true; 
 591     __block CFErrorRef localError 
= NULL
; 
 592     SOSCircleRef circle  
= SOSAccountGetCircle(account
, error
); 
 593     CFMutableDictionaryRef circleToPeerIDs 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 594     CFMutableArrayRef peerIds 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 596     require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
, 
 597                          SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"), 
 600     SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
 601         if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) { 
 602             if (SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)) { 
 603                 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!"); 
 604                 CFMutableDictionaryRef circleToIdsId 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 605                 CFMutableArrayRef ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 606                 CFArrayAppendValue(ids
, SOSPeerInfoGetPeerID(peer
)); 
 607                 CFDictionaryAddValue(circleToIdsId
, SOSCircleGetName(circle
), ids
); 
 608                 SyncingCompletedOverIDS 
= SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, circleToIdsId
, &localError
); 
 609                 if(!SyncingCompletedOverIDS
){ 
 610                     secerror("Failed to sync over IDS, falling back to KVS"); 
 611                     SyncingCompletedOverIDS 
= SOSTransportMessageSyncWithPeers(account
->kvs_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         // Tell account to update SOSEngine with current trusted peers 
 640         if (isSOSErrorCoded(localError
, kSOSErrorPeerNotFound
)) { 
 641             secnotice("Account", "Arming account to update SOSEngine with current trusted peers"); 
 642             account
->circle_rings_retirements_need_attention 
= true; 
 644         CFErrorPropagate(localError
, error
); 
 647     CFReleaseNull(peerIds
); 
 648     CFReleaseSafe(localError
); 
 652 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
, 
 653                                 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
) 
 657     SOSPeerInfoRef myPeerInfo 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
 658     require_action_quiet(account
->my_identity 
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer"))); 
 659     require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
); 
 661     CFStringRef cleanupPeerID 
= SOSPeerInfoGetPeerID(cleanupPeer
); 
 663     CFStringRef circle_name 
= SOSCircleGetName(circle
); 
 665     CFMutableDictionaryRef circleToPeerIDs 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 666     CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
); 
 668     CFErrorRef localError 
= NULL
; 
 669     if (!(success 
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) { 
 670         secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
); 
 673     if (account
->ids_message_transport 
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) { 
 674         secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
); 
 677     CFReleaseNull(localError
); 
 679     if((success 
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) { 
 680         if (!(success 
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) { 
 681             secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
); 
 684     CFReleaseNull(localError
); 
 685     CFReleaseNull(circleToPeerIDs
); 
 691 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) { 
 692     CFMutableSetRef retirees_to_remove 
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
); 
 694     __block 
bool success 
= true; 
 696     CFSetForEach(account
->retirees
, ^(const void *value
) { 
 697         SOSPeerInfoRef retiree 
= (SOSPeerInfoRef
) value
; 
 700             // 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. 
 701             if (!SOSPeerInfoIsRetirementTicket(retiree
) || 
 702                 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) { 
 703                 CFSetAddValue(retirees_to_remove
, retiree
); 
 708     CFMutableArrayRef retirees_to_cleanup 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 709     CFSetForEach(retirees_to_remove
, ^(const void *value
) { 
 710         CFArrayAppendValue(retirees_to_cleanup
, value
); 
 711         CFSetRemoveValue(account
->retirees
, value
); 
 714     CFReleaseNull(retirees_to_remove
); 
 716     CFDictionaryRef retirements_to_remove 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 717                                                                          SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
, 
 720     CFReleaseNull(retirees_to_cleanup
); 
 722     success 
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
); 
 724     CFReleaseNull(retirements_to_remove
); 
 729 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef 
*error
) { 
 730     SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) { 
 731         CFSetSetValue(account
->retirees
, peer
); 
 732         CFErrorRef cleanupError 
= NULL
; 
 733         if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) { 
 734             secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
); 
 736         CFReleaseSafe(cleanupError
); 
 741 SOSCircleRef 
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef 
*error
) { 
 742     SOSCircleRef new_circle 
= SOSCircleCopyCircle(NULL
, starting_circle
, error
); 
 743     if(!new_circle
) return NULL
; 
 745     if (account
->retirees
) { 
 746         CFSetForEach(account
->retirees
, ^(const void* value
) { 
 747             SOSPeerInfoRef pi 
= (SOSPeerInfoRef
) value
; 
 748             if (isSOSPeerInfo(pi
)) { 
 749                 SOSCircleUpdatePeerInfo(new_circle
, pi
); 
 754     if(SOSCircleCountPeers(new_circle
) == 0) { 
 755         SOSCircleResetToEmpty(new_circle
, NULL
); 
 762 // MARK: Circle Membership change notificaion 
 765 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) { 
 766     SOSAccountCircleMembershipChangeBlock copy 
= Block_copy(changeBlock
); 
 767     CFArrayAppendValue(a
->change_blocks
, copy
); 
 771 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) { 
 772     CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
); 
 775 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) { 
 776     if (!changeBlock
) return; 
 778     CFRetainSafe(ds_name
); 
 779     SOSAccountCircleMembershipChangeBlock block_to_register 
= ^void (SOSCircleRef new_circle
, 
 780                                                                      CFSetRef added_peers
, CFSetRef removed_peers
, 
 781                                                                      CFSetRef added_applicants
, CFSetRef removed_applicants
) { 
 783         if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
)) 
 786         SOSPeerInfoRef myPi 
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
); 
 787         CFStringRef myPi_id 
= myPi 
? SOSPeerInfoGetPeerID(myPi
) : NULL
; 
 789         CFMutableArrayRef peer_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 790         CFMutableArrayRef added_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 791         CFMutableArrayRef removed_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 793         if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) { 
 794             SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) { 
 795                 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
); 
 798             CFSetForEach(added_peers
, ^(const void *value
) { 
 799                 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
); 
 802             CFSetForEach(removed_peers
, ^(const void *value
) { 
 803                 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
); 
 807         if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
)) 
 808             changeBlock(peer_ids
, added_ids
, removed_ids
); 
 810         CFReleaseSafe(peer_ids
); 
 811         CFReleaseSafe(added_ids
); 
 812         CFReleaseSafe(removed_ids
); 
 815     CFRetainSafe(changeBlock
); 
 816     SOSAccountAddChangeBlock(a
, block_to_register
); 
 818     CFSetRef empty 
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
); 
 819     if (a
->trusted_circle 
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) { 
 820         block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
); 
 822     CFReleaseSafe(empty
); 
 825 void SOSAccountPurgeIdentity(SOSAccountRef account
) { 
 826     if (account
->my_identity
) { 
 827         // Purge private key but don't return error if we can't. 
 828         CFErrorRef purgeError 
= NULL
; 
 829         if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) { 
 830             secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
); 
 832         CFReleaseNull(purgeError
); 
 834         CFReleaseNull(account
->my_identity
); 
 838 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) { 
 839     SOSFullPeerInfoRef fpi 
= account
->my_identity
; 
 840     if(!fpi
) return false; 
 842         CFErrorRef localError 
= NULL
; 
 846     SOSPeerInfoRef retire_peer 
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
); 
 848         secerror("Create ticket failed for peer %@: %@", fpi
, localError
); 
 850         // See if we need to repost the circle we could either be an applicant or a peer already in the circle 
 851         if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) { 
 852             // Remove our application if we have one. 
 853             SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
); 
 854         } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) { 
 855             if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) { 
 856                 CFErrorRef cleanupError 
= NULL
; 
 857                 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) { 
 858                     secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
); 
 860                 CFReleaseSafe(cleanupError
); 
 864         // Store the retirement record locally. 
 865         CFSetAddValue(account
->retirees
, retire_peer
); 
 867         // Write retirement to Transport 
 868         CFErrorRef postError 
= NULL
; 
 869         if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){ 
 870             secwarning("Couldn't post retirement (%@)", postError
); 
 872         if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){ 
 873             secwarning("Couldn't flush retirement data (%@)", postError
); 
 875         CFReleaseNull(postError
); 
 878     SOSAccountPurgeIdentity(account
); 
 882     CFReleaseNull(localError
); 
 883     CFReleaseNull(retire_peer
); 
 887 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) { 
 888     SOSFullPeerInfoRef fpi 
= account
->my_identity
; 
 889     if(!fpi
) return false; 
 890     SOSPeerInfoRef     pi 
= SOSFullPeerInfoGetPeerInfo(fpi
); 
 891     CFStringRef        peerID 
= SOSPeerInfoGetPeerID(pi
); 
 893     CFErrorRef localError 
= NULL
; 
 896     bool writeRing 
= false; 
 897     bool writePeerInfo 
= false; 
 899     if(SOSRingHasPeerID(ring
, peerID
)) { 
 900         writePeerInfo 
= true; 
 904     // this was circle behavior - at some point 
 905     if(SOSRingHasApplicant(ring
, peerID
)) { 
 910     if(writePeerInfo 
|| writeRing
) { 
 911         SOSRingWithdraw(ring
, NULL
, fpi
, error
); 
 914     // Write leave thing to Transport 
 915     CFDataRef peerInfoData 
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
); 
 916     SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors? 
 919         CFDataRef ring_data 
= SOSRingCopyEncodedData(ring
, error
); 
 922             SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors? 
 924         CFReleaseNull(ring_data
); 
 927     CFReleaseNull(localError
); 
 931 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef 
*error
) { 
 933     SOSTransportCircleRef transport 
= account
->circle_transport
; 
 935         result 
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
); 
 941  NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any 
 942  local value that has been overwritten by a distant value. If there is no 
 943  conflict between the local and the distant values when doing the initial 
 944  sync (e.g. if the cloud has no data stored or the client has not stored 
 945  any data yet), you'll never see that notification. 
 947  NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip 
 948  with server but initial round trip with server does not imply 
 949  NSUbiquitousKeyValueStoreInitialSyncChange. 
 954 // MARK: Status summary 
 957 static SOSCCStatus 
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) { 
 959         return kSOSCCNotInCircle
; 
 961     if (circle 
&& SOSCircleCountPeers(circle
) == 0) 
 962         return kSOSCCCircleAbsent
; 
 966         if(SOSPeerInfoIsRetirementTicket(this_peer
)) 
 967             return kSOSCCNotInCircle
; 
 969         if (SOSCircleHasPeer(circle
, this_peer
, NULL
)) 
 970             return kSOSCCInCircle
; 
 972         if (SOSCircleHasApplicant(circle
, this_peer
, NULL
)) 
 973             return kSOSCCRequestPending
; 
 976     return kSOSCCNotInCircle
; 
 979 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef 
*error
) { 
 980     SOSCCStatus result 
= SOSAccountGetCircleStatus(account
, error
); 
 982     if (result 
!= kSOSCCInCircle 
&& result 
!= kSOSCCError
) { 
 983         SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle")); 
 990 SOSCCStatus 
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) { 
 991     if (!SOSAccountHasPublicKey(account
, error
)) { 
 995     return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
)); 
 999 // MARK: Account Reset Circles 
1002 static bool SOSAccountResetCircleToOffering(SOSAccountRef account
, SecKeyRef user_key
, CFErrorRef 
*error
) { 
1003     bool result 
= false; 
1005     require(SOSAccountHasCircle(account
, error
), fail
); 
1006     require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
); 
1008     (void) SOSAccountResetAllRings(account
, error
); 
1010     SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1011         bool result 
= false; 
1012         SOSFullPeerInfoRef cloud_identity 
= NULL
; 
1013         CFErrorRef localError 
= NULL
; 
1015         require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
); 
1018             SOSPeerInfoRef cloud_peer 
= GenerateNewCloudIdentityPeerInfo(error
); 
1019             require_quiet(cloud_peer
, err_out
); 
1020             cloud_identity 
= CopyCloudKeychainIdentity(cloud_peer
, error
); 
1021             CFReleaseNull(cloud_peer
); 
1022             require_quiet(cloud_identity
, err_out
); 
1025         account
->departure_code 
= kSOSNeverLeftCircle
; 
1026         require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
); 
1027         require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, &localError
), err_out
); 
1028         require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), &localError
), err_out
); 
1030         SOSAccountPublishCloudParameters(account
, NULL
); 
1033         if (result 
== false) 
1034             secerror("error resetting circle (%@) to offering: %@", circle
, localError
); 
1035         if (localError 
&& error 
&& *error 
== NULL
) { 
1036             *error 
= localError
; 
1039         CFReleaseNull(localError
); 
1040         CFReleaseNull(cloud_identity
); 
1051 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) { 
1052     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1056     CFReleaseNull(account
->my_identity
); 
1058     return user_key 
&& SOSAccountResetCircleToOffering(account
, user_key
, error
); 
1061 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) { 
1062     if (!SOSAccountHasPublicKey(account
, error
)) 
1064     __block 
bool result 
= true; 
1066     result 
&= SOSAccountResetAllRings(account
, error
); 
1068     CFReleaseNull(account
->my_identity
); 
1070     account
->departure_code 
= kSOSWithdrewMembership
; 
1071     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1072         result 
= SOSCircleResetToEmpty(circle
, error
); 
1077         secerror("error: %@", error 
? *error 
: NULL
); 
1083 // MARK: start backups 
1086 bool SOSAccountEnsureBackupStarts(SOSAccountRef account
) { 
1088     __block 
bool result 
= false; 
1089     __block CFErrorRef error 
= NULL
; 
1090     secnotice("backup", "Starting new backups"); 
1092     CFDataRef backupKey 
= SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
); 
1094     if (CFEqualSafe(backupKey
, account
->backup_key
)){ 
1095         CFReleaseNull(backupKey
); 
1099     if(account
->backup_key 
!= NULL
){ 
1100         require_quiet(SOSBSKBIsGoodBackupPublic(account
->backup_key
, &error
), exit
); 
1101         require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), &error
, 
1102                                                ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef 
*error
) { 
1103                                                    return SOSFullPeerInfoUpdateBackupKey(fpi
, account
->backup_key
, error
); 
1105         CFErrorRef localError 
= NULL
; 
1106         if (!SOSDeleteV0Keybag(&localError
)) { 
1107             secerror("Failed to delete v0 keybag: %@", localError
); 
1109         CFReleaseNull(localError
); 
1113         SOSAccountForEachBackupView(account
, ^(const void *value
) { 
1114             CFStringRef viewName 
= (CFStringRef
)value
; 
1115             result 
&= SOSAccountStartNewBackup(account
, viewName
, &error
); 
1119         if(account
->backup_key 
== NULL
){ 
1120             secerror("account backup key is NULL!"); 
1126         secnotice("backupkey", "Failed to setup backup public key: %@", error 
? (CFTypeRef
) error 
: (CFTypeRef
) CFSTR("No error space provided")); 
1128     CFReleaseNull(backupKey
); 
1133 // MARK: Waiting for in-sync 
1136 static bool SOSAccountHasBeenInSync(SOSAccountRef account
) { 
1137     CFTypeRef unsyncedObject 
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
); 
1138     CFSetRef unsynced 
= asSet(unsyncedObject
, NULL
); 
1140     return !(unsyncedObject 
== kCFBooleanTrue 
|| (unsynced 
&& (CFSetGetCount(unsynced
) > 0))); 
1143 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account
, CFSetRef viewsInSync
) { 
1144     bool notifyOfChange 
= false; 
1146     SOSCCStatus circleStatus 
= SOSAccountGetCircleStatus(account
, NULL
); 
1147     bool inOrApplying 
= (circleStatus 
== kSOSCCInCircle
) || (circleStatus 
== kSOSCCRequestPending
); 
1149     CFTypeRef unsyncedObject 
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
); 
1151     if (!inOrApplying
) { 
1152         if (unsyncedObject 
!= NULL
) { 
1153             SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
); 
1154             secnotice("initial-sync", "in sync, clearing pending"); 
1155             notifyOfChange 
= true; 
1157     } else if (circleStatus 
== kSOSCCInCircle
) { 
1158         __block CFMutableSetRef viewsToSync 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
1159         SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
1160             SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) { 
1161                 CFSetUnion(viewsToSync
, enabled
); 
1166             CFSetSubtract(viewsToSync
, viewsInSync
); 
1170         if (unsyncedObject 
== kCFBooleanTrue
) { 
1171             if (CFSetGetCount(viewsToSync
) == 0) { 
1172                 secnotice("initial-sync", "No views to wait for"); 
1173                 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
); 
1175                 __block CFSetRef newViews 
= NULL
; 
1176                 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account
), ^(CFSetRef enabled
) { 
1177                     newViews 
= CFSetCreateIntersection(kCFAllocatorDefault
, enabled
, viewsToSync
); 
1179                 secnotice("initial-sync", "Pending views set from True: %@", newViews
); 
1180                 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
); 
1181                 CFReleaseNull(newViews
); 
1183             notifyOfChange 
= true; 
1184         } else if (isSet(unsyncedObject
)) { 
1185             CFSetRef waiting 
= (CFMutableSetRef
) unsyncedObject
; 
1186             CFSetRef newViews 
= CFSetCreateIntersection(kCFAllocatorDefault
, waiting
, viewsToSync
); 
1187             if (!CFEqualSafe(waiting
, newViews
)) { 
1188                 if (CFSetGetCount(newViews
) == 0) { 
1189                     secnotice("initial-sync", "No views left to wait for."); 
1190                     SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
); 
1192                     secnotice("initial-sync", "Pending views updated: %@", newViews
); 
1193                     SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
); 
1195                 notifyOfChange 
= true; 
1197             CFReleaseNull(newViews
); 
1200         CFReleaseNull(viewsToSync
); 
1203     if (notifyOfChange
) { 
1204         if(SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
) == NULL
){ 
1205             CFDictionaryRef syncBlocks 
= account
->waitForInitialSync_blocks
; 
1206             account
->waitForInitialSync_blocks 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1208             CFDictionaryForEach(syncBlocks
, ^(const void *key
, const void *value
) { 
1209                 secnotice("updates", "calling in sync block [%@]", key
); 
1210                 ((SOSAccountWaitForInitialSyncBlock
)value
)(account
); 
1213             CFReleaseNull(syncBlocks
); 
1216         // Make sure we update the engine 
1217         account
->circle_rings_retirements_need_attention 
= true; 
1220     return SOSAccountHasBeenInSync(account
); 
1223 static void SOSAccountPeerGotInSync(SOSAccountRef account
, CFStringRef peerID
) { 
1224     secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID
); 
1226     if (account
->trusted_circle
) { 
1227         SOSPeerInfoRef peer 
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
); 
1229             CFSetRef views 
= SOSPeerInfoCopyEnabledViews(peer
); 
1230             SOSAccountUpdateOutOfSyncViews(account
, views
); 
1231             CFReleaseNull(views
); 
1233         CFReleaseNull(peer
); 
1237 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account
) { 
1238     SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
); 
1240     CFDictionaryForEach(account
->notification_cleanups
, ^(const void *key
, const void *value
) { 
1242             SOSEngineSetSyncCompleteListener(engine
, key
, NULL
); 
1244         dispatch_async(account
->queue
, value
); 
1247     CFDictionaryRemoveAllValues(account
->notification_cleanups
); 
1250 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account
, CFStringRef peerID
) { 
1251     dispatch_block_t cleanup 
= CFDictionaryGetValue(account
->notification_cleanups
, peerID
); 
1254         SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
); 
1257             SOSEngineSetSyncCompleteListener(engine
, peerID
, NULL
); 
1260         dispatch_async(account
->queue
, cleanup
); 
1263     CFDictionaryRemoveValue(account
->notification_cleanups
, peerID
); 
1267 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account
, CFStringRef peerID
, dispatch_block_t block
) { 
1268     dispatch_block_t copy 
= Block_copy(block
); 
1269     CFDictionarySetValue(account
->notification_cleanups
, peerID
, copy
); 
1270     CFReleaseNull(copy
); 
1273 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) { 
1274     if (CFDictionaryGetCount(account
->notification_cleanups
) == 0) { 
1275         secnotice("initial-sync", "Setting up notifications to monitor in-sync"); 
1276         SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
); 
1278         SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
); 
1281             SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
1282                 CFStringRef peerID 
= CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerID(peer
)); 
1284                 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID
); 
1285                 SOSAccountRegisterCleanupBlock(account
, peerID
, ^{ 
1286                     CFReleaseSafe(peerID
); 
1289                 SOSEngineSetSyncCompleteListener(engine
, peerID
, ^{ 
1290                     SOSAccountPeerGotInSync(account
, peerID
); 
1291                     SOSAccountCleanupNotificationForPeer(account
, peerID
); 
1292                     SOSAccountFinishTransaction(account
); 
1296             secerror("Couldn't find engine to setup notifications!!!"); 
1301 void SOSAccountCancelSyncChecking(SOSAccountRef account
) { 
1302     SOSAccountCleanupNotificationForAllPeers(account
); 
1303     SOSAccountUpdateOutOfSyncViews(account
, NULL
); 
1306 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account
) { 
1307     bool hasBeenInSync 
= false; 
1309     if (!SOSAccountIsInCircle(account
, NULL
)) { 
1310         SOSAccountCancelSyncChecking(account
); 
1312         hasBeenInSync 
= SOSAccountHasBeenInSync(account
); 
1313         if (!hasBeenInSync
) { 
1314             hasBeenInSync 
= SOSAccountUpdateOutOfSyncViews(account
, NULL
); 
1315             if (hasBeenInSync
) { 
1316                 // Cancel and declare victory 
1318                 SOSAccountCancelSyncChecking(account
); 
1320                 // Make sure we're watching in case this is the fist attempt 
1321                 SOSAccountEnsureSyncChecking(account
); 
1326     return hasBeenInSync
; 
1333 static bool SOSAccountJoinCircle(SOSAccountRef account
, SecKeyRef user_key
, 
1334                                 bool use_cloud_peer
, CFErrorRef
* error
) { 
1335     __block 
bool result 
= false; 
1336     __block SOSFullPeerInfoRef cloud_full_peer 
= NULL
; 
1338     require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???"))); 
1339     require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
); 
1341     SOSFullPeerInfoRef myCirclePeer 
= account
->my_identity
; 
1343     if (use_cloud_peer
) { 
1344         cloud_full_peer 
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
); 
1346         SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
); 
1349     if (SOSCircleCountPeers(account
->trusted_circle
) == 0) { 
1350         result 
= SOSAccountResetCircleToOffering(account
, user_key
, error
); 
1352         SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1353             result 
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
); 
1354             result 
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
); 
1355             account
->departure_code 
= kSOSNeverLeftCircle
; 
1356             if(result 
&& cloud_full_peer
) { 
1357                 CFErrorRef localError 
= NULL
; 
1358                 CFStringRef cloudid 
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
)); 
1359                 require_quiet(cloudid
, finish
); 
1360                 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
); 
1361                 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
); 
1364                     secerror("Failed to join with cloud identity: %@", localError
); 
1365                     CFReleaseNull(localError
); 
1373     CFReleaseNull(cloud_full_peer
); 
1377 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) { 
1378     bool success 
= false; 
1380     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1381     require_quiet(user_key
, done
); // Fail if we don't get one. 
1383     require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join"))); 
1385     if (account
->my_identity 
!= NULL
) { 
1386         SOSPeerInfoRef myPeer 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
1387         success 
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
); 
1388         require_quiet(!success
, done
); 
1390         SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now. 
1392         if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) { 
1393                 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
)); 
1395                         CFReleaseNull(account
->my_identity
); 
1400     success 
= SOSAccountJoinCircle(account
, user_key
, use_cloud_identity
, error
); 
1402     require_quiet(success
, done
); 
1404     account
->departure_code 
= kSOSNeverLeftCircle
; 
1410 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) { 
1411     return SOSAccountJoinCircles_internal(account
, false, error
); 
1414 CFStringRef 
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef 
*error
){ 
1415     CFStringRef result 
= NULL
; 
1417     require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me"))); 
1419     result 
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
1425 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){ 
1428     if(whichTransportType 
== kSOSTransportIDS 
|| whichTransportType 
== kSOSTransportFuture
){ 
1429         secdebug("IDS Transport", "We are setting our device ID: %@", IDS
); 
1430         if(IDS 
!= NULL 
&& (CFStringGetLength(IDS
) > 0)){ 
1431             require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me"))); 
1433             result 
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) { 
1435                 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
); 
1436                 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDS
, error
); 
1437                 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
); 
1439                 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
); 
1446         secdebug("IDS Transport", "We are setting our device ID: %@", IDS
); 
1447         if(IDS 
!= NULL 
&& (CFStringGetLength(IDS
) > 0)){ 
1448             require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me"))); 
1450             result 
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) { 
1452                 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
); 
1453                 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeKVS
, error
); 
1454                 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
); 
1456                 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
); 
1464     SOSCCSyncWithAllPeers(); 
1471 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef 
*error
){ 
1473     if(whichTransportType 
== kSOSTransportIDS 
|| whichTransportType 
== kSOSTransportFuture 
|| whichTransportType 
== kSOSTransportPresent
){ 
1474         //construct message dictionary, circle -> peerID -> message 
1476         CFMutableDictionaryRef circleToPeerMessages 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1477         CFMutableDictionaryRef peerToMessage 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1479         char *messageCharStar
; 
1480         asprintf(&messageCharStar
, "%d", kIDSSendOneMessage
); 
1481         CFStringRef messageString 
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
); 
1483         CFMutableDictionaryRef mutableDictionary 
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
); 
1485         SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) { 
1486             if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))) 
1487             CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
); 
1490         CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
); 
1491         result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
); 
1493         CFReleaseNull(mutableDictionary
); 
1494         CFReleaseNull(peerToMessage
); 
1495         CFReleaseNull(circleToPeerMessages
); 
1496         CFReleaseNull(messageString
); 
1497         free(messageCharStar
); 
1502 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef 
*error
){ 
1503     bool result 
= false; 
1504     //construct message dictionary, circle -> peerID -> message 
1506     if(account
->ids_message_transport 
== NULL
) 
1507         account
->ids_message_transport 
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
); 
1509     require_quiet(account
->ids_message_transport
, fail
); 
1510     CFMutableDictionaryRef circleToPeerMessages 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1511     CFMutableDictionaryRef peerToMessage 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1513     char *messageCharStar
; 
1514     asprintf(&messageCharStar
, "%d", kIDSStartPingTestMessage
); 
1515     CFStringRef messageString 
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
); 
1517     CFMutableDictionaryRef mutableDictionary 
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
); 
1519     SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) { 
1520         if(CFStringCompare(SOSAccountGetMyPeerID(account
), SOSPeerInfoGetPeerID(peer
), 0) != 0) 
1521             CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
); 
1524     CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
); 
1525     result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
); 
1527     CFReleaseNull(mutableDictionary
); 
1528     CFReleaseNull(peerToMessage
); 
1529     CFReleaseNull(circleToPeerMessages
); 
1530     CFReleaseNull(messageString
); 
1531     free(messageCharStar
); 
1536 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef 
*error
){ 
1538     if(whichTransportType 
== kSOSTransportIDS 
|| whichTransportType 
== kSOSTransportFuture 
|| whichTransportType 
== kSOSTransportPresent
){ 
1540         __block 
bool success 
= true; 
1541         __block CFErrorRef localError 
= NULL
; 
1542         dispatch_semaphore_t wait_for 
= dispatch_semaphore_create(0); 
1543         dispatch_retain(wait_for
); // Both this scope and the block own it 
1545         SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){ 
1546             success 
= (sync_error 
== NULL
); 
1548                 CFRetainAssign(localError
, sync_error
); 
1551             dispatch_semaphore_signal(wait_for
); 
1552             dispatch_release(wait_for
); 
1555         dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
); 
1556         dispatch_release(wait_for
); 
1558         if(!success 
&& localError 
!= NULL 
&& error 
!= NULL
){ 
1559             secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
); 
1560             *error 
= localError
; 
1563             secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID"); 
1569 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) { 
1570     return SOSAccountJoinCircles_internal(account
, true, error
); 
1574 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
) 
1578     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1579         return sosAccountLeaveCircle(account
, circle
, error
); 
1582     account
->departure_code 
= kSOSWithdrewMembership
; 
1587 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
) 
1589     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1595     CFMutableSetRef peersToRemove 
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
); 
1597     bool leaveCircle 
= CFSetContainsValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
)); 
1599     CFSetRemoveValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
)); 
1601     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1602         bool success 
= false; 
1604         if(CFSetGetCount(peersToRemove
) != 0) { 
1605             require_quiet(SOSCircleRemovePeers(circle
, user_key
, SOSAccountGetMyFullPeerInfo(account
), peersToRemove
, error
), done
); 
1606             success 
= SOSAccountGenerationSignatureUpdate(account
, error
); 
1607         } else success 
= true; 
1609         if (success 
&& leaveCircle
) { 
1610             success 
= sosAccountLeaveCircle(account
, circle
, error
); 
1622 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) { 
1623     dispatch_queue_t queue 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0); 
1624     dispatch_group_t group 
= dispatch_group_create(); 
1625     __block 
bool result 
= false; 
1626     secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
); 
1627     // Add a task to the group 
1628     dispatch_group_async(group
, queue
, ^{ 
1629         SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1630             return sosAccountLeaveCircle(account
, circle
, error
); 
1633     dispatch_time_t milestone 
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds 
* NSEC_PER_SEC
); 
1634     dispatch_group_wait(group
, milestone
); 
1636     account
->departure_code 
= kSOSWithdrewMembership
; 
1638     dispatch_release(group
); 
1644 // MARK: Application 
1647 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
, 
1648                                               bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) { 
1649         SOSPeerInfoRef me 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
1650         CFErrorRef peer_error 
= NULL
; 
1651         if (account
->trusted_circle 
&& me 
&& 
1652             SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) { 
1653             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) { 
1654                 __block 
bool modified 
= false; 
1655                 CFArrayForEach(peer_infos
, ^(const void *value
) { 
1656                     SOSPeerInfoRef peer 
= (SOSPeerInfoRef
) value
; 
1657                     if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) { 
1658                         if (action(circle
, account
->my_identity
, peer
)) { 
1667             secerror("Got error in SOSCircleHasPeer: %@", peer_error
); 
1668         CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here. 
1671 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) { 
1672     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1676     __block 
bool success 
= true; 
1677     __block 
int64_t num_peers 
= 0; 
1679     for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) { 
1680         bool accepted 
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
); 
1684                         num_peers 
= MAX(num_peers
, SOSCircleCountPeers(circle
)); 
1691 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) { 
1692     __block 
bool success 
= true; 
1693     __block 
int64_t num_peers 
= 0; 
1695     for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) { 
1696         bool rejected 
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
); 
1700                         num_peers 
= MAX(num_peers
, SOSCircleCountPeers(circle
)); 
1708 CFStringRef 
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) { 
1709     return CFSTR("We're compatible, go away"); 
1712 enum DepartureReason 
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) { 
1713     return account
->departure_code
; 
1716 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) { 
1717         account
->departure_code 
= reason
; 
1721 CFArrayRef 
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef 
*error
) { 
1722     CFArrayRef result 
= NULL
; 
1723     CFNumberRef generation 
= NULL
; 
1725     require_quiet(SOSAccountHasPublicKey(account
, error
), fail
); 
1726     require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle"))); 
1728     generation 
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
); 
1729     result 
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
); 
1735 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef 
*error
) { 
1736     if (!SOSAccountHasPublicKey(account
, error
)) 
1739     return account
->user_public_trusted
; 
1742 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef 
*error
) { 
1743     // TODO: this result is never set or used 
1746     secnotice("updates", "Ensuring peer registration."); 
1748     require_quiet(account
->trusted_circle
, done
); 
1749     require_quiet(account
->my_identity
, done
); 
1750     // If we are not in the circle, there is no point in setting up peers 
1751     require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
); 
1753     // This code only uses the SOSFullPeerInfoRef for two things: 
1754     //  - Finding out if this device is in the trusted circle 
1755     //  - Using the peerID for this device to see if the current peer is "me" 
1756     //  - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer 
1758     CFStringRef my_id 
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
1760     SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) { 
1761         if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) { 
1762             CFErrorRef localError 
= NULL
; 
1763             SOSTransportMessageRef messageTransport 
= NULL
; 
1765             if(whichTransportType 
== kSOSTransportIDS 
|| whichTransportType 
== kSOSTransportFuture 
|| whichTransportType 
== kSOSTransportPresent
){ 
1766                  messageTransport 
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport 
: account
->kvs_message_transport
; 
1769                 messageTransport 
= account
->kvs_message_transport
; 
1771             SOSPeerCoderInitializeForPeer(messageTransport
->engine
, account
->my_identity
, peer
, &localError
); 
1773                 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
); 
1774             CFReleaseSafe(localError
); 
1778     //Initialize our device ID 
1779     SOSTransportMessageIDSGetIDSDeviceID(account
);     
1786 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef 
*error
) { 
1787     if (!account
->expansion
) { 
1788         account
->expansion 
= CFDictionaryCreateMutableForCFTypes(NULL
); 
1791     return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary")); 
1794 bool SOSAccountClearValue(SOSAccountRef account
, const void *key
, CFErrorRef 
*error
) { 
1795     bool success 
= SOSAccountEnsureExpansion(account
, error
); 
1796     require_quiet(success
, errOut
); 
1798     CFDictionaryRemoveValue(account
->expansion
, key
); 
1803 bool SOSAccountSetValue(SOSAccountRef account
, const void *key
, const void *value
, CFErrorRef 
*error
) { 
1804     bool success 
= SOSAccountEnsureExpansion(account
, error
); 
1805     require_quiet(success
, errOut
); 
1807     CFDictionarySetValue(account
->expansion
, key
, value
); 
1813 const void *SOSAccountGetValue(SOSAccountRef account
, const void *key
, CFErrorRef 
*error
) { 
1814     if (!account
->expansion
) { 
1817     return CFDictionaryGetValue(account
->expansion
, key
); 
1820 bool SOSAccountAddEscrowRecords(SOSAccountRef account
, CFStringRef dsid
, CFDictionaryRef record
, CFErrorRef 
*error
){ 
1821     CFMutableDictionaryRef escrowRecords 
= (CFMutableDictionaryRef
)SOSAccountGetValue(account
, kSOSEscrowRecord
, error
); 
1822     CFMutableDictionaryRef escrowCopied 
= NULL
; 
1823     bool success 
= false; 
1825     if(isDictionary(escrowRecords
) && escrowRecords 
!= NULL
) 
1826         escrowCopied 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
); 
1828         escrowCopied 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1830     CFDictionaryAddValue(escrowCopied
, dsid
, record
); 
1831     SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
); 
1836     CFReleaseNull(escrowCopied
); 
1842 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef 
*error
){ 
1843     bool success 
= false; 
1845     CFDictionaryRef escrowRecords 
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
); 
1846     success 
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
); 
1851 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef 
*error
) 
1853     CFMutableDictionaryRef circleToPeerMessages 
= NULL
; 
1854     CFStringRef messageString 
= NULL
; 
1855     CFMutableDictionaryRef mutableDictionary 
= NULL
; 
1856     CFMutableSetRef peers 
= NULL
; 
1857     CFMutableDictionaryRef peerList 
= NULL
; 
1858     char* message 
= NULL
; 
1859     bool result 
= false; 
1860     if(account
->ids_message_transport 
== NULL
) 
1861         account
->ids_message_transport 
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
); 
1863     require_quiet(account
->ids_message_transport
, fail
); 
1864     circleToPeerMessages 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1866     //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item 
1868     asprintf(&message
, "%d", kIDSPeerAvailability
); 
1869     messageString 
= CFStringCreateWithCString(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
); 
1871     mutableDictionary 
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("checking peers"), NULL
); 
1873     //make sure there are peers in the circle 
1874     peers 
= SOSCircleCopyPeers(account
->trusted_circle
, kCFAllocatorDefault
); 
1875     require_quiet(CFSetGetCount(peers
) > 0, fail
); 
1876     CFReleaseNull(peers
); 
1878     peerList 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1879     SOSCircleRef circle 
= account
->trusted_circle
; 
1881     //check each peer to make sure they have the right view set enabled 
1882     CFSetRef mySubSet 
= SOSViewsGetV0SubviewSet(); 
1883     SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
1884         if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))){ 
1885             CFMutableSetRef peerViews 
= SOSPeerInfoCopyEnabledViews(peer
); 
1886             CFSetRef intersectSets 
= CFSetCreateIntersection(kCFAllocatorDefault
, mySubSet
, peerViews
); 
1887             if(CFEqualSafe(intersectSets
, mySubSet
)){ 
1888                 CFStringRef deviceID 
= SOSPeerInfoCopyDeviceID(peer
); 
1889                 if(deviceID 
!= NULL
) 
1890                     CFDictionaryAddValue(peerList
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
); 
1891                 CFReleaseNull(deviceID
); 
1893             CFReleaseNull(peerViews
); 
1894             CFReleaseNull(intersectSets
); 
1898     require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
); 
1899     CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerList
); 
1900     result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
); 
1903     CFReleaseNull(mutableDictionary
); 
1904     CFReleaseNull(messageString
); 
1905     CFReleaseNull(peerList
); 
1906     CFReleaseNull(circleToPeerMessages
); 
1907     CFReleaseNull(peers
); 
1913 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) { 
1914     if (!SOSAccountIsInCircle(account
, NULL
)) 
1917     SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) { 
1918         __block 
bool updated 
= false; 
1919         CFSetForEach(account
->retirees
, ^(CFTypeRef element
){ 
1920             SOSPeerInfoRef retiree 
= asSOSPeerInfo(element
); 
1922             if (retiree 
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) { 
1924                 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
); 
1925                 CFErrorRef cleanupError 
= NULL
; 
1926                 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
)) 
1927                     secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
); 
1928                 CFReleaseSafe(cleanupError
); 
1935 void SOSAccountFinishTransaction(SOSAccountRef account
) { 
1936     if(account
->circle_rings_retirements_need_attention
){ 
1937         SOSAccountRecordRetiredPeersInCircle(account
); 
1939         CFErrorRef localError 
= NULL
; 
1940         if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &localError
)) { 
1941             secerror("flush circle failed %@", localError
); 
1943         CFReleaseSafe(localError
); 
1945         SOSAccountNotifyEngines(account
); // For now our only rings are backup rings. 
1948     SOSAccountCheckHasBeenInSync(account
); 
1950     account
->circle_rings_retirements_need_attention 
= false;