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