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/SOSPeerInfo.h> 
  12 #include <Security/SecureObjectSync/SOSPeerInfoV2.h> 
  13 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h> 
  14 #include <Security/SecureObjectSync/SOSTransportCircle.h> 
  15 #include <Security/SecureObjectSync/SOSTransportMessage.h> 
  16 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h> 
  17 #include <Security/SecureObjectSync/SOSKVSKeys.h> 
  18 #include <Security/SecureObjectSync/SOSTransport.h> 
  19 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h> 
  20 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h> 
  21 #include <Security/SecureObjectSync/SOSPeerCoder.h> 
  22 #include <Security/SecureObjectSync/SOSInternal.h> 
  23 #include <Security/SecureObjectSync/SOSRing.h> 
  24 #include <Security/SecureObjectSync/SOSRingUtils.h> 
  25 #include <Security/SecureObjectSync/SOSRingRecovery.h> 
  26 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h> 
  27 #include <Security/SecureObjectSync/SOSAccountTransaction.h> 
  28 #include <Security/SecureObjectSync/SOSAccountGhost.h> 
  30 #include <Security/SecItemInternal.h> 
  31 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h> 
  33 #include <utilities/SecCFWrappers.h> 
  34 #include <utilities/SecCFError.h> 
  36 CFGiblisWithCompareFor(SOSAccount
); 
  38 const CFStringRef SOSTransportMessageTypeIDS 
= CFSTR("IDS"); 
  39 const CFStringRef SOSTransportMessageTypeIDSV2 
= CFSTR("IDS2.0"); 
  40 const CFStringRef SOSTransportMessageTypeKVS 
= CFSTR("KVS"); 
  41 const CFStringRef kSOSDSIDKey 
= CFSTR("AccountDSID"); 
  42 const CFStringRef kSOSEscrowRecord 
= CFSTR("EscrowRecord"); 
  43 const CFStringRef kSOSUnsyncedViewsKey 
= CFSTR("unsynced"); 
  44 const CFStringRef kSOSPendingEnableViewsToBeSetKey 
= CFSTR("pendingEnableViews"); 
  45 const CFStringRef kSOSPendingDisableViewsToBeSetKey 
= CFSTR("pendingDisableViews"); 
  46 const CFStringRef kSOSTestV2Settings 
= CFSTR("v2dictionary"); 
  47 const CFStringRef kSOSRecoveryKey 
= CFSTR("RecoveryKey"); 
  48 const CFStringRef kSOSRecoveryRing 
= CFSTR("RecoveryRing"); 
  49 const CFStringRef kSOSAccountUUID 
= CFSTR("UUID"); 
  51 #define DATE_LENGTH 25 
  52 const CFStringRef kSOSAccountDebugScope 
= CFSTR("Scope"); 
  54 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
) 
  57     CFStringRef circle_name 
= NULL
; 
  59     require_quiet(a
, xit
); 
  60     require_quiet(a
->factory
, xit
); 
  62     circle_name 
= SOSDataSourceFactoryCopyName(a
->factory
); 
  63     require(circle_name
, xit
); 
  65     SOSAccountEnsureCircle(a
, circle_name
, NULL
); 
  70     // We don't own name, so don't release it. 
  71     CFReleaseNull(circle_name
); 
  76 SOSAccountRef 
SOSAccountCreateBasic(CFAllocatorRef allocator
, 
  77                                     CFDictionaryRef gestalt
, 
  78                                     SOSDataSourceFactoryRef factory
) { 
  79     SOSAccountRef a 
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
); 
  81     a
->queue 
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
); 
  83     a
->gestalt 
= CFRetainSafe(gestalt
); 
  85     a
->trusted_circle 
= NULL
; 
  86     a
->backups 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
  87     a
->my_identity 
= NULL
; 
  88     a
->retirees 
= CFSetCreateMutableForSOSPeerInfosByID(allocator
); 
  90     a
->factory 
= factory
; // We adopt the factory. kthanksbai. 
  92     a
->isListeningForSync 
= false; 
  94     a
->_user_private 
= NULL
; 
  95     a
->_password_tmp 
= NULL
; 
  96     a
->user_private_timer 
= NULL
; 
  97     a
->lock_notification_token 
= NOTIFY_TOKEN_INVALID
; 
  99     a
->change_blocks 
= CFArrayCreateMutableForCFTypes(allocator
); 
 100     a
->waitForInitialSync_blocks 
= NULL
; 
 101     a
->departure_code 
= kSOSNeverAppliedToCircle
; 
 103     a
->key_transport 
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
); 
 104     a
->circle_transport 
= NULL
; 
 105     a
->kvs_message_transport 
= NULL
; 
 106     a
->ids_message_transport 
= NULL
; 
 107     a
->expansion 
= CFDictionaryCreateMutableForCFTypes(allocator
); 
 109     SOSAccountAddRingDictionary(a
); 
 112     a
->circle_rings_retirements_need_attention 
= false; 
 113     a
->engine_peer_state_needs_repair 
= false; 
 114     a
->key_interests_need_updating 
= false; 
 121 // MARK: Transactional 
 124 void SOSAccountWithTransaction_Locked(SOSAccountRef account
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) { 
 125     SOSAccountTransactionRef at 
= SOSAccountTransactionCreate(account
); 
 127     SOSAccountTransactionFinish(at
); 
 133 void SOSAccountWithTransaction(SOSAccountRef account
, bool sync
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) { 
 134     dispatch_block_t with_transaction 
=  ^{ 
 135         SOSAccountWithTransaction_Locked(account
, action
); 
 139         dispatch_sync(SOSAccountGetQueue(account
), with_transaction
); 
 141         dispatch_async(SOSAccountGetQueue(account
), with_transaction
); 
 145 void SOSAccountWithTransactionSync(SOSAccountRef account
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) { 
 146     SOSAccountWithTransaction(account
, true, action
); 
 149 void SOSAccountWithTransactionAsync(SOSAccountRef account
, bool sync
, void (^action
)(SOSAccountRef account
, SOSAccountTransactionRef txn
)) { 
 150     SOSAccountWithTransaction(account
, false, action
); 
 157 void SOSAccountSetSaveBlock(SOSAccountRef account
, SOSAccountSaveBlock saveBlock
) { 
 158     CFAssignRetained(account
->saveBlock
, Block_copy(saveBlock
)); 
 161 void SOSAccountFlattenToSaveBlock(SOSAccountRef account
) { 
 162     if (account
->saveBlock
) { 
 163         CFErrorRef localError 
= NULL
; 
 164         CFDataRef saveData 
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &localError
); 
 166         (account
->saveBlock
)(saveData
, localError
); 
 168         CFReleaseNull(saveData
); 
 169         CFReleaseNull(localError
); 
 174 // MARK: Security Properties 
 177 SOSSecurityPropertyResultCode 
SOSAccountUpdateSecurityProperty(SOSAccountRef account
, CFStringRef property
, SOSSecurityPropertyActionCode actionCode
, CFErrorRef 
*error
) { 
 178     SOSSecurityPropertyResultCode retval 
= kSOSCCGeneralSecurityPropertyError
; 
 179     bool updateCircle 
= false; 
 180     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 181     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 182     retval 
= SOSFullPeerInfoUpdateSecurityProperty(account
->my_identity
, actionCode
, property
, error
); 
 184     if(actionCode 
== kSOSCCSecurityPropertyEnable 
&& retval 
== kSOSCCSecurityPropertyValid
) { 
 186     } else if(actionCode 
== kSOSCCSecurityPropertyDisable 
&& retval 
== kSOSCCSecurityPropertyNotValid
) { 
 188     } else if(actionCode 
== kSOSCCSecurityPropertyPending
) { 
 193         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 194             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change"); 
 195             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 203 SOSSecurityPropertyResultCode 
SOSAccountSecurityPropertyStatus(SOSAccountRef account
, CFStringRef property
, CFErrorRef 
*error
) { 
 204     SOSSecurityPropertyResultCode retval 
= kSOSCCGeneralViewError
; 
 205     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 206     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 207     retval 
= SOSFullPeerInfoSecurityPropertyStatus(account
->my_identity
, property
, error
); 
 212 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
) 
 214     if (CFEqualSafe(new_gestalt
, account
->gestalt
)) 
 217     if (account
->trusted_circle 
&& account
->my_identity
 
 218         && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) { 
 219         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 220             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change"); 
 221             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
)); 
 225     CFRetainAssign(account
->gestalt
, new_gestalt
); 
 229 CFDictionaryRef 
SOSAccountCopyGestalt(SOSAccountRef account
) { 
 230     return CFDictionaryCreateCopy(kCFAllocatorDefault
, account
->gestalt
); 
 233 bool SOSAccountUpdateV2Dictionary(SOSAccountRef account
, CFDictionaryRef newV2Dict
) { 
 234     if(!newV2Dict
) return true; 
 235     SOSAccountSetValue(account
, kSOSTestV2Settings
, newV2Dict
, NULL
); 
 236     if (account
->trusted_circle 
&& account
->my_identity
 
 237         && SOSFullPeerInfoUpdateV2Dictionary(account
->my_identity
, newV2Dict
, NULL
)) { 
 238         SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 239             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change"); 
 240             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
)); 
 246 CFDictionaryRef 
SOSAccountCopyV2Dictionary(SOSAccountRef account
) { 
 247     CFDictionaryRef v2dict 
= SOSAccountGetValue(account
, kSOSTestV2Settings
, NULL
); 
 248     return CFDictionaryCreateCopy(kCFAllocatorDefault
, v2dict
); 
 251 static bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){ 
 252     SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
); 
 253     //send new DSID over account changed 
 254     SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
); 
 258 void SOSAccountAssertDSID(SOSAccountRef account
, CFStringRef dsid
) { 
 259     CFStringRef accountDSID 
= SOSAccountGetValue(account
, kSOSDSIDKey
, NULL
); 
 260     if(accountDSID 
== NULL
) { 
 261         secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid
); 
 263         SOSAccountUpdateDSID(account
, dsid
); 
 264     } else if(CFStringCompare(dsid
, accountDSID
, 0) != kCFCompareEqualTo
) { 
 265         secnotice("updates", "Changing DSID from: %@ to %@", accountDSID
, dsid
); 
 267         //DSID has changed, blast the account! 
 268         SOSAccountSetToNew(account
); 
 270         //update DSID to the new DSID 
 271         SOSAccountUpdateDSID(account
, dsid
); 
 273         secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID
, dsid
); 
 277 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
, CFSetRef excludedViews
) { 
 278     if (account
->trusted_circle 
&& account
->my_identity
) { 
 279         if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
, excludedViews
)) { 
 280             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 281                 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change"); 
 282                 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 290 void SOSAccountPendEnableViewSet(SOSAccountRef account
, CFSetRef enabledViews
) 
 292     if(CFSetGetValue(enabledViews
, kSOSViewKeychainV0
) != NULL
) secnotice("viewChange", "Warning, attempting to Add KeychainV0"); 
 294     SOSAccountValueUnionWith(account
, kSOSPendingEnableViewsToBeSetKey
, enabledViews
); 
 295     SOSAccountValueSubtractFrom(account
, kSOSPendingDisableViewsToBeSetKey
, enabledViews
); 
 299 void SOSAccountPendDisableViewSet(SOSAccountRef account
, CFSetRef disabledViews
) 
 301     SOSAccountValueUnionWith(account
, kSOSPendingDisableViewsToBeSetKey
, disabledViews
); 
 302     SOSAccountValueSubtractFrom(account
, kSOSPendingEnableViewsToBeSetKey
, disabledViews
); 
 305 static SOSViewResultCode 
SOSAccountVirtualV0Behavior(SOSAccountRef account
, SOSViewActionCode actionCode
) { 
 306     SOSViewResultCode retval 
= kSOSCCGeneralViewError
; 
 307     // The V0 view switches on and off all on it's own, we allow people the delusion 
 308     // of control and status if it's what we're stuck at., otherwise error. 
 309     if (SOSAccountSyncingV0(account
)) { 
 310         require_action_quiet(actionCode 
== kSOSCCViewDisable
, errOut
, CFSTR("Can't disable V0 view and it's on right now")); 
 311         retval 
= kSOSCCViewMember
; 
 313         require_action_quiet(actionCode 
== kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now")); 
 314         retval 
= kSOSCCViewNotMember
; 
 321 SOSViewResultCode 
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef 
*error
) { 
 322     SOSViewResultCode retval 
= kSOSCCGeneralViewError
; 
 323     SOSViewResultCode currentStatus 
= kSOSCCGeneralViewError
; 
 324     bool alreadyInSync 
= SOSAccountHasCompletedInitialSync(account
); 
 326     bool updateCircle 
= false; 
 327     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 328     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 329     require_action_quiet((actionCode 
== kSOSCCViewEnable
) || (actionCode 
== kSOSCCViewDisable
), errOut
, CFSTR("Invalid View Action")); 
 330     currentStatus 
= SOSAccountViewStatus(account
, viewname
, error
); 
 331     require_action_quiet((currentStatus 
== kSOSCCViewNotMember
) || (currentStatus 
== kSOSCCViewMember
), errOut
, CFSTR("View Membership Not Actionable")); 
 333     if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) { 
 334         retval 
= SOSAccountVirtualV0Behavior(account
, actionCode
); 
 335     } else if (SOSAccountSyncingV0(account
) && SOSViewsIsV0Subview(viewname
)) { 
 336         // Subviews of V0 syncing can't be turned off if V0 is on. 
 337         require_action_quiet(actionCode 
= kSOSCCViewDisable
, errOut
, CFSTR("Have V0 peer can't disable")); 
 338         retval 
= kSOSCCViewMember
; 
 340         CFMutableSetRef pendingSet 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 341         CFSetAddValue(pendingSet
, viewname
); 
 343         if(actionCode 
== kSOSCCViewEnable 
&& currentStatus 
== kSOSCCViewNotMember
) { 
 345                 retval 
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
); 
 346                 if(retval 
== kSOSCCViewMember
) updateCircle 
= true; 
 348                 SOSAccountPendEnableViewSet(account
, pendingSet
); 
 349                 retval 
= kSOSCCViewMember
; 
 350                 updateCircle 
= false; 
 352         } else if(actionCode 
== kSOSCCViewDisable 
&& currentStatus 
== kSOSCCViewMember
) { 
 354                 retval 
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
); 
 355                 if(retval 
== kSOSCCViewNotMember
) updateCircle 
= true; 
 357                 SOSAccountPendDisableViewSet(account
, pendingSet
); 
 358                 retval 
= kSOSCCViewNotMember
; 
 359                 updateCircle 
= false; 
 362             retval 
= currentStatus
; 
 365         CFReleaseNull(pendingSet
); 
 368             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 369                 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change"); 
 370                 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 379 SOSViewResultCode 
SOSAccountViewStatus(SOSAccountRef account
, CFStringRef viewname
, CFErrorRef 
*error
) { 
 380     SOSViewResultCode retval 
= kSOSCCGeneralViewError
; 
 381     require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
)); 
 382     require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
)); 
 384     if (SOSAccountValueSetContainsValue(account
, kSOSPendingEnableViewsToBeSetKey
, viewname
)) { 
 385         retval 
= kSOSCCViewMember
; 
 386     } else if (SOSAccountValueSetContainsValue(account
, kSOSPendingDisableViewsToBeSetKey
, viewname
)) { 
 387         retval 
= kSOSCCViewNotMember
; 
 389         retval 
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
); 
 392     // 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 
 393     if (retval 
!= kSOSCCViewMember
) { 
 394         if ((CFEqualSafe(viewname
, kSOSViewKeychainV0
) || SOSViewsIsV0Subview(viewname
)) 
 395           && SOSAccountSyncingV0(account
)) { 
 396             retval 
= kSOSCCViewMember
; 
 400     // If we're only an applicant we report pending if we would be a view member 
 401     if (retval 
== kSOSCCViewMember
) { 
 402         bool isApplicant 
= SOSCircleHasApplicant(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
), error
); 
 404             retval 
= kSOSCCViewPending
; 
 412 static void dumpViewSet(CFStringRef label
, CFSetRef views
) { 
 414         CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) { 
 415             secnotice("circleChange", "%@ list: %@", label
, description
); 
 418         secnotice("circleChange", "No %@ list provided.", label
); 
 422 static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account
, CFMutableSetRef viewSet
, SOSViewActionCode actionCode
) { 
 424     if(viewSet 
&& CFSetContainsValue(viewSet
, kSOSViewKeychainV0
)) { 
 425         retval 
= SOSAccountVirtualV0Behavior(account
, actionCode
) != kSOSCCGeneralViewError
; 
 426         CFSetRemoveValue(viewSet
, kSOSViewKeychainV0
); 
 431 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef origEnabledViews
, CFSetRef origDisabledViews
) { 
 433     bool updateCircle 
= false; 
 434     SOSPeerInfoRef  pi 
= NULL
; 
 435     CFMutableSetRef enabledViews 
= NULL
; 
 436     CFMutableSetRef disabledViews 
= NULL
; 
 437     if(origEnabledViews
) enabledViews 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, origEnabledViews
); 
 438     if(origDisabledViews
) disabledViews 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, origDisabledViews
); 
 439     dumpViewSet(CFSTR("Enabled"), enabledViews
); 
 440     dumpViewSet(CFSTR("Disabled"), disabledViews
); 
 442     require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle")); 
 444     // Make sure we have a peerInfo capable of supporting views. 
 445     SOSFullPeerInfoRef fpi 
= SOSAccountGetMyFullPeerInfo(account
); 
 446     require_action_quiet(fpi
, errOut
, secnotice("views", "Attempt to set viewsets with no fullPeerInfo")); 
 447     require_action_quiet(enabledViews 
|| disabledViews
, errOut
, secnotice("views", "No work to do")); 
 449     pi 
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
); 
 451     require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef")); 
 453     if(!SOSPeerInfoVersionIsCurrent(pi
)) { 
 454         CFErrorRef updateFailure 
= NULL
; 
 455         require_action_quiet(SOSPeerInfoUpdateToV2(pi
, &updateFailure
), errOut
, 
 456                              (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure
), (void) CFReleaseNull(updateFailure
))); 
 457         secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets"); 
 461     CFStringSetPerformWithDescription(enabledViews
, ^(CFStringRef description
) { 
 462         secnotice("viewChange", "Enabling %@", description
); 
 465     CFStringSetPerformWithDescription(disabledViews
, ^(CFStringRef description
) { 
 466         secnotice("viewChange", "Disabling %@", description
); 
 469     require_action_quiet(SOSAccountScreenViewListForValidV0(account
, enabledViews
, kSOSCCViewEnable
), errOut
, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0")); 
 470     require_action_quiet(SOSAccountScreenViewListForValidV0(account
, disabledViews
, kSOSCCViewDisable
), errOut
, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0")); 
 472     if(SOSAccountHasCompletedInitialSync(account
)) { 
 473         if(enabledViews
) updateCircle 
|= SOSViewSetEnable(pi
, enabledViews
); 
 474         if(disabledViews
) updateCircle 
|= SOSViewSetDisable(pi
, disabledViews
); 
 477         //hold on to the views and enable them later 
 478         if(enabledViews
) SOSAccountPendEnableViewSet(account
, enabledViews
); 
 479         if(disabledViews
) SOSAccountPendDisableViewSet(account
, disabledViews
); 
 484         /* UPDATE FULLPEERINFO VIEWS */ 
 485         require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
), errOut
); 
 487         require_quiet(SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) { 
 488             secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change"); 
 489             return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
 492         // Make sure we update the engine 
 493         account
->circle_rings_retirements_need_attention 
= true; 
 497     CFReleaseNull(enabledViews
); 
 498     CFReleaseNull(disabledViews
); 
 504 SOSAccountRef 
SOSAccountCreate(CFAllocatorRef allocator
, 
 505                                CFDictionaryRef gestalt
, 
 506                                SOSDataSourceFactoryRef factory
) { 
 507     SOSAccountRef a 
= SOSAccountCreateBasic(allocator
, gestalt
, factory
); 
 509     SOSAccountEnsureFactoryCircles(a
); 
 511     SOSAccountEnsureUUID(a
); 
 513     a
->key_interests_need_updating 
= true; 
 518 static void SOSAccountDestroy(CFTypeRef aObj
) { 
 519     SOSAccountRef a 
= (SOSAccountRef
) aObj
; 
 521     // We don't own the factory, merely have a reference to the singleton 
 525     SOSAccountCancelSyncChecking(a
); 
 527     SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
); 
 530         SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
); 
 532     dispatch_sync(a
->queue
, ^{ 
 533         CFReleaseNull(a
->gestalt
); 
 535         CFReleaseNull(a
->my_identity
); 
 536         CFReleaseNull(a
->trusted_circle
); 
 537         CFReleaseNull(a
->backups
); 
 538         CFReleaseNull(a
->retirees
); 
 540         a
->user_public_trusted 
= false; 
 541         CFReleaseNull(a
->user_public
); 
 542         CFReleaseNull(a
->user_key_parameters
); 
 544         SOSAccountPurgePrivateCredential(a
); 
 545         CFReleaseNull(a
->previous_public
); 
 546         CFReleaseNull(a
->_user_private
); 
 547         CFReleaseNull(a
->_password_tmp
); 
 549         a
->departure_code 
= kSOSNeverAppliedToCircle
; 
 550         CFReleaseNull(a
->kvs_message_transport
); 
 551         CFReleaseNull(a
->ids_message_transport
); 
 552         CFReleaseNull(a
->key_transport
); 
 553         CFReleaseNull(a
->circle_transport
); 
 554         dispatch_release(a
->queue
); 
 556         dispatch_release(a
->user_private_timer
); 
 557         CFReleaseNull(a
->change_blocks
); 
 558         CFReleaseNull(a
->waitForInitialSync_blocks
); 
 559         CFReleaseNull(a
->expansion
); 
 561         CFReleaseNull(a
->saveBlock
); 
 562         CFReleaseNull(a
->deviceID
); 
 566 static OSStatus 
do_delete(CFDictionaryRef query
) { 
 569     result 
= SecItemDelete(query
); 
 571         secerror("SecItemDelete: %d", (int)result
); 
 577 do_keychain_delete_aks_bags() 
 580     CFDictionaryRef item 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 581                                  kSecClass
,           kSecClassGenericPassword
, 
 582                                  kSecAttrAccessGroup
, CFSTR("com.apple.sbd"), 
 583                                  kSecAttrAccount
,     CFSTR("SecureBackupPublicKeybag"), 
 584                                  kSecAttrService
,     CFSTR("SecureBackupService"), 
 585                                  kSecAttrSynchronizable
, kCFBooleanTrue
, 
 586                                  kSecUseTombstones
,     kCFBooleanFalse
, 
 589     result 
= do_delete(item
); 
 596 do_keychain_delete_identities() 
 599     CFDictionaryRef item 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 600                                 kSecClass
, kSecClassKey
, 
 601                                 kSecAttrSynchronizable
, kCFBooleanTrue
, 
 602                                 kSecUseTombstones
, kCFBooleanFalse
, 
 603                                 kSecAttrAccessGroup
, CFSTR("com.apple.security.sos"), 
 606     result 
= do_delete(item
); 
 613 do_keychain_delete_lakitu() 
 616     CFDictionaryRef item 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 617                                                         kSecClass
, kSecClassGenericPassword
, 
 618                                                         kSecAttrSynchronizable
, kCFBooleanTrue
, 
 619                                                         kSecUseTombstones
, kCFBooleanFalse
, 
 620                                                         kSecAttrAccessGroup
, CFSTR("com.apple.lakitu"), 
 621                                                         kSecAttrAccount
, CFSTR("EscrowServiceBypassToken"), 
 622                                                         kSecAttrService
, CFSTR("EscrowService"), 
 625     result 
= do_delete(item
); 
 632 do_keychain_delete_sbd() 
 635     CFDictionaryRef item 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 636                                                         kSecClass
, kSecClassGenericPassword
, 
 637                                                         kSecAttrSynchronizable
, kCFBooleanTrue
, 
 638                                                         kSecUseTombstones
, kCFBooleanFalse
, 
 639                                                         kSecAttrAccessGroup
, CFSTR("com.apple.sbd"), 
 642     result 
= do_delete(item
); 
 648 void SOSAccountSetToNew(SOSAccountRef a
) { 
 649     secnotice("accountChange", "Setting Account to New"); 
 652     CFReleaseNull(a
->my_identity
); 
 653     CFReleaseNull(a
->trusted_circle
); 
 654     CFReleaseNull(a
->backups
); 
 655     CFReleaseNull(a
->retirees
); 
 657     CFReleaseNull(a
->user_key_parameters
); 
 658     CFReleaseNull(a
->user_public
); 
 659     CFReleaseNull(a
->previous_public
); 
 660     CFReleaseNull(a
->_user_private
); 
 661     CFReleaseNull(a
->_password_tmp
); 
 663     CFReleaseNull(a
->key_transport
); 
 664     CFReleaseNull(a
->circle_transport
); 
 665     CFReleaseNull(a
->kvs_message_transport
); 
 666     CFReleaseNull(a
->ids_message_transport
); 
 667     CFReleaseNull(a
->expansion
); 
 668     CFReleaseNull(a
->deviceID
); 
 670     /* remove all syncable items */ 
 671     result 
= do_keychain_delete_aks_bags(); (void) result
; 
 672     secdebug("set to new", "result for deleting aks bags: %d", result
); 
 674     result 
= do_keychain_delete_identities(); (void) result
; 
 675     secdebug("set to new", "result for deleting identities: %d", result
); 
 677     result 
= do_keychain_delete_lakitu(); (void) result
; 
 678     secdebug("set to new", "result for deleting lakitu: %d", result
); 
 680     result 
= do_keychain_delete_sbd(); (void) result
; 
 681     secdebug("set to new", "result for deleting sbd: %d", result
); 
 683     a
->user_public_trusted 
= false; 
 684     a
->departure_code 
= kSOSNeverAppliedToCircle
; 
 686     if (a
->user_private_timer
) { 
 687         dispatch_source_cancel(a
->user_private_timer
); 
 688         dispatch_release(a
->user_private_timer
); 
 689         a
->user_private_timer 
= NULL
; 
 690         xpc_transaction_end(); 
 693     if (a
->lock_notification_token 
!= NOTIFY_TOKEN_INVALID
) { 
 694         notify_cancel(a
->lock_notification_token
); 
 695         a
->lock_notification_token 
= NOTIFY_TOKEN_INVALID
; 
 702     // update_interest_block; 
 705     a
->key_transport 
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
); 
 706     a
->circle_transport 
= NULL
; 
 707     a
->kvs_message_transport 
= NULL
; 
 708     a
->ids_message_transport 
= NULL
; 
 710     a
->backups 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 712     a
->retirees 
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
); 
 713     a
->expansion 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 714     SOSAccountAddRingDictionary(a
); 
 716     SOSAccountEnsureFactoryCircles(a
); // Does rings too 
 718     // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly 
 719     SOSAccountEnsureUUID(a
); 
 721     a
->key_interests_need_updating 
= true; 
 724 bool SOSAccountIsNew(SOSAccountRef account
, CFErrorRef 
*error
){ 
 726     require_quiet(account
->user_public_trusted 
== false, exit
); 
 727     require_quiet(account
->departure_code 
== kSOSNeverAppliedToCircle
, exit
); 
 728     require_quiet(account
->user_private_timer 
== NULL
, exit
); 
 729     require_quiet(account
->lock_notification_token 
== NOTIFY_TOKEN_INVALID
, exit
); 
 730     require_quiet (CFDictionaryGetCount(account
->backups
) == 0, exit
); 
 731     require_quiet(CFSetGetCount(account
->retirees
) == 0, exit
); 
 738 static CFStringRef 
SOSAccountCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) { 
 739     SOSAccountRef a 
= (SOSAccountRef
) aObj
; 
 741     CFStringRef gestaltDescription 
= CFDictionaryCopyCompactDescription(a
->gestalt
); 
 743     CFStringRef result 
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SOSAccount@%p: %c%c%c%c%c G: %@ Me: %@ C: %@ >"), a
, 
 744                                                   a
->user_public 
? 'P' : 'p', 
 745                                                   a
->user_public_trusted 
? 'T' : 't', 
 746                                                   a
->isListeningForSync 
? 'L' : 'l', 
 747                                                   SOSAccountHasCompletedInitialSync(a
) ? 'C' : 'c', 
 748                                                   SOSAccountHasCompletedRequiredBackupSync(a
) ? 'B' : 'b', 
 749                                                   gestaltDescription
, a
->my_identity
, a
->trusted_circle
); 
 751     CFReleaseNull(gestaltDescription
); 
 756 CFStringRef 
SOSAccountCreateCompactDescription(SOSAccountRef a
) { 
 758     CFStringRef gestaltDescription 
= CFDictionaryCopySuperCompactDescription(a
->gestalt
); 
 760     CFStringRef result 
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), gestaltDescription
); 
 762     CFReleaseNull(gestaltDescription
); 
 767 static Boolean 
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
) 
 769     SOSAccountRef laccount 
= (SOSAccountRef
) lhs
; 
 770     SOSAccountRef raccount 
= (SOSAccountRef
) rhs
; 
 772     return CFEqualSafe(laccount
->gestalt
, raccount
->gestalt
) 
 773         && CFEqualSafe(laccount
->trusted_circle
, raccount
->trusted_circle
) 
 774         && CFEqualSafe(laccount
->expansion
, raccount
->expansion
) 
 775         && CFEqualSafe(laccount
->my_identity
, raccount
->my_identity
); 
 778 dispatch_queue_t 
SOSAccountGetQueue(SOSAccountRef account
) { 
 779     return account
->queue
; 
 782 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account
){ 
 783     account
->user_public_trusted 
= true; 
 786 SOSFullPeerInfoRef 
SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
) 
 788     return CFRetainSafe(account
->my_identity
); 
 793 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
, 
 794                                 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
) 
 798     SOSPeerInfoRef myPeerInfo 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
 799     require_action_quiet(account
->my_identity 
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer"))); 
 800     require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
); 
 802     CFStringRef cleanupPeerID 
= SOSPeerInfoGetPeerID(cleanupPeer
); 
 804     CFStringRef circle_name 
= SOSCircleGetName(circle
); 
 806     CFMutableDictionaryRef circleToPeerIDs 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 807     CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
); 
 809     CFErrorRef localError 
= NULL
; 
 810     if (!(success 
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) { 
 811         secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
); 
 814     if (account
->ids_message_transport 
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) { 
 815         secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
); 
 818     CFReleaseNull(localError
); 
 820     if((success 
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) { 
 821         if (!(success 
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) { 
 822             secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
); 
 825     CFReleaseNull(localError
); 
 826     CFReleaseNull(circleToPeerIDs
); 
 832 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) { 
 833     CFMutableSetRef retirees_to_remove 
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
); 
 835     __block 
bool success 
= true; 
 837     CFSetForEach(account
->retirees
, ^(const void *value
) { 
 838         SOSPeerInfoRef retiree 
= (SOSPeerInfoRef
) value
; 
 841             // 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. 
 842             if (!SOSPeerInfoIsRetirementTicket(retiree
) || 
 843                 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) { 
 844                 CFSetAddValue(retirees_to_remove
, retiree
); 
 849     CFMutableArrayRef retirees_to_cleanup 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 850     CFSetForEach(retirees_to_remove
, ^(const void *value
) { 
 851         CFArrayAppendValue(retirees_to_cleanup
, value
); 
 852         CFSetRemoveValue(account
->retirees
, value
); 
 855     CFReleaseNull(retirees_to_remove
); 
 857     CFDictionaryRef retirements_to_remove 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 858                                                                          SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
, 
 861     CFReleaseNull(retirees_to_cleanup
); 
 863     success 
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
); 
 865     CFReleaseNull(retirements_to_remove
); 
 870 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef 
*error
) { 
 871     SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) { 
 872         CFSetSetValue(account
->retirees
, peer
); 
 873         CFErrorRef cleanupError 
= NULL
; 
 874         if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) { 
 875             secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
); 
 877         CFReleaseSafe(cleanupError
); 
 882 SOSCircleRef 
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef 
*error
) { 
 883     SOSCircleRef new_circle 
= SOSCircleCopyCircle(NULL
, starting_circle
, error
); 
 884     SOSFullPeerInfoRef meFull 
= SOSAccountGetMyFullPeerInfo(account
); 
 885     SOSPeerInfoRef me 
= SOSFullPeerInfoGetPeerInfo(meFull
); 
 886     bool iAmApplicant 
= me 
&& SOSCircleHasApplicant(new_circle
, me
, NULL
); 
 888     if(!new_circle
) return NULL
; 
 889     __block 
bool workDone 
= false; 
 890     if (account
->retirees
) { 
 891         CFSetForEach(account
->retirees
, ^(const void* value
) { 
 892             SOSPeerInfoRef pi 
= (SOSPeerInfoRef
) value
; 
 893             if (isSOSPeerInfo(pi
)) { 
 894                 SOSCircleUpdatePeerInfo(new_circle
, pi
); 
 900     if(workDone 
&& SOSCircleCountPeers(new_circle
) == 0) { 
 901         SecKeyRef userPrivKey 
= SOSAccountGetPrivateCredential(account
, error
); 
 905                 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant"); 
 906                 if(!SOSCircleResetToOffering(new_circle
, userPrivKey
, meFull
, error
) || 
 907                    !SOSAccountAddiCloudIdentity(account
, new_circle
, userPrivKey
, error
)) { 
 908                     CFReleaseNull(new_circle
); 
 912                 // Do nothing.  We can't resetToOffering without a userPrivKey.  If we were to resetToEmpty 
 913                 // we won't push the result later in handleUpdateCircle.  If we leave the circle as it is 
 914                 // we have a chance to set things right with a SetCreds/Join sequence.  This will cause 
 915                 // handleUpdateCircle to return false. 
 916                 CFReleaseNull(new_circle
); 
 920             // This case is when we aren't an applicant and the circle is retirement-empty. 
 921             secnotice("resetToEmpty", "Reset to empty with last retirement"); 
 922             SOSCircleResetToEmpty(new_circle
, NULL
); 
 930 // MARK: Circle Membership change notificaion 
 933 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) { 
 934     SOSAccountCircleMembershipChangeBlock copy 
= Block_copy(changeBlock
); 
 935     CFArrayAppendValue(a
->change_blocks
, copy
); 
 939 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) { 
 940     CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
); 
 943 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) { 
 944     if (!changeBlock
) return; 
 946     CFRetainSafe(ds_name
); 
 947     SOSAccountCircleMembershipChangeBlock block_to_register 
= ^void (SOSCircleRef new_circle
, 
 948                                                                      CFSetRef added_peers
, CFSetRef removed_peers
, 
 949                                                                      CFSetRef added_applicants
, CFSetRef removed_applicants
) { 
 951         if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
)) 
 954         SOSPeerInfoRef myPi 
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
); 
 955         CFStringRef myPi_id 
= myPi 
? SOSPeerInfoGetPeerID(myPi
) : NULL
; 
 957         CFMutableArrayRef peer_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 958         CFMutableArrayRef added_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 959         CFMutableArrayRef removed_ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 961         if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) { 
 962             SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) { 
 963                 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
); 
 966             CFSetForEach(added_peers
, ^(const void *value
) { 
 967                 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
); 
 970             CFSetForEach(removed_peers
, ^(const void *value
) { 
 971                 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
); 
 975         if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
)) 
 976             changeBlock(peer_ids
, added_ids
, removed_ids
); 
 978         CFReleaseSafe(peer_ids
); 
 979         CFReleaseSafe(added_ids
); 
 980         CFReleaseSafe(removed_ids
); 
 983     CFRetainSafe(changeBlock
); 
 984     SOSAccountAddChangeBlock(a
, block_to_register
); 
 986     CFSetRef empty 
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
); 
 987     if (a
->trusted_circle 
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) { 
 988         block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
); 
 990     CFReleaseSafe(empty
); 
 993 void SOSAccountPurgeIdentity(SOSAccountRef account
) { 
 994     if (account
->my_identity
) { 
 995         // Purge private key but don't return error if we can't. 
 996         CFErrorRef purgeError 
= NULL
; 
 997         if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) { 
 998             secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
); 
1000         CFReleaseNull(purgeError
); 
1002         CFReleaseNull(account
->my_identity
); 
1006 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) { 
1007     SOSFullPeerInfoRef fpi 
= account
->my_identity
; 
1008     if(!fpi
) return false; 
1010         CFErrorRef localError 
= NULL
; 
1012     bool retval 
= false; 
1014     SOSPeerInfoRef retire_peer 
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
); 
1016         secerror("Create ticket failed for peer %@: %@", fpi
, localError
); 
1018         // See if we need to repost the circle we could either be an applicant or a peer already in the circle 
1019         if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) { 
1020             // Remove our application if we have one. 
1021             SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
); 
1022         } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) { 
1023             if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) { 
1024                 CFErrorRef cleanupError 
= NULL
; 
1025                 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) { 
1026                     secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
); 
1028                 CFReleaseSafe(cleanupError
); 
1032         // Store the retirement record locally. 
1033         CFSetAddValue(account
->retirees
, retire_peer
); 
1035         // Write retirement to Transport 
1036         CFErrorRef postError 
= NULL
; 
1037         if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){ 
1038             secwarning("Couldn't post retirement (%@)", postError
); 
1040         if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){ 
1041             secwarning("Couldn't flush retirement data (%@)", postError
); 
1043         CFReleaseNull(postError
); 
1046     SOSAccountPurgeIdentity(account
); 
1050     CFReleaseNull(localError
); 
1051     CFReleaseNull(retire_peer
); 
1055 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) { 
1056     SOSFullPeerInfoRef fpi 
= account
->my_identity
; 
1057     if(!fpi
) return false; 
1058     SOSPeerInfoRef     pi 
= SOSFullPeerInfoGetPeerInfo(fpi
); 
1059     CFStringRef        peerID 
= SOSPeerInfoGetPeerID(pi
); 
1061     CFErrorRef localError 
= NULL
; 
1063     bool retval 
= false; 
1064     bool writeRing 
= false; 
1065     bool writePeerInfo 
= false; 
1067     if(SOSRingHasPeerID(ring
, peerID
)) { 
1068         writePeerInfo 
= true; 
1072     // this was circle behavior - at some point 
1073     if(SOSRingHasApplicant(ring
, peerID
)) { 
1078     if(writePeerInfo 
|| writeRing
) { 
1079         SOSRingWithdraw(ring
, NULL
, fpi
, error
); 
1082     // Write leave thing to Transport 
1083     CFDataRef peerInfoData 
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
); 
1084     SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors? 
1087         CFDataRef ring_data 
= SOSRingCopyEncodedData(ring
, error
); 
1090             SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors? 
1092         CFReleaseNull(ring_data
); 
1095     CFReleaseNull(localError
); 
1099 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef 
*error
) { 
1100     bool result 
= false; 
1101     SOSTransportCircleRef transport 
= account
->circle_transport
; 
1103         result 
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
); 
1109  NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any 
1110  local value that has been overwritten by a distant value. If there is no 
1111  conflict between the local and the distant values when doing the initial 
1112  sync (e.g. if the cloud has no data stored or the client has not stored 
1113  any data yet), you'll never see that notification. 
1115  NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip 
1116  with server but initial round trip with server does not imply 
1117  NSUbiquitousKeyValueStoreInitialSyncChange. 
1122 // MARK: Status summary 
1125 static SOSCCStatus 
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) { 
1127         return kSOSCCNotInCircle
; 
1129     if (circle 
&& SOSCircleCountPeers(circle
) == 0) 
1130         return kSOSCCCircleAbsent
; 
1134         if(SOSPeerInfoIsRetirementTicket(this_peer
)) 
1135             return kSOSCCNotInCircle
; 
1137         if (SOSCircleHasPeer(circle
, this_peer
, NULL
)) 
1138             return kSOSCCInCircle
; 
1140         if (SOSCircleHasApplicant(circle
, this_peer
, NULL
)) 
1141             return kSOSCCRequestPending
; 
1144     return kSOSCCNotInCircle
; 
1147 CFStringRef 
SOSAccountGetSOSCCStatusString(SOSCCStatus status
) { 
1149         case kSOSCCInCircle
: return CFSTR("kSOSCCInCircle"); 
1150         case kSOSCCNotInCircle
: return CFSTR("kSOSCCNotInCircle"); 
1151         case kSOSCCRequestPending
: return CFSTR("kSOSCCRequestPending"); 
1152         case kSOSCCCircleAbsent
: return CFSTR("kSOSCCCircleAbsent"); 
1153         case kSOSCCError
: return CFSTR("kSOSCCError"); 
1155     return CFSTR("kSOSCCError"); 
1158 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef 
*error
) { 
1159     SOSCCStatus result 
= SOSAccountGetCircleStatus(account
, error
); 
1161     if (result 
!= kSOSCCInCircle
) { 
1162         SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle")); 
1169 SOSCCStatus 
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) { 
1170     if (!SOSAccountHasPublicKey(account
, error
)) { 
1174     return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
)); 
1178 // MARK: Account Reset Circles 
1181 // This needs to be called within a SOSAccountModifyCircle() block 
1183 bool SOSAccountAddiCloudIdentity(SOSAccountRef account
, SOSCircleRef circle
, SecKeyRef user_key
, CFErrorRef 
*error
) { 
1184     bool result 
= false; 
1185     SOSFullPeerInfoRef cloud_identity 
= NULL
; 
1186     SOSPeerInfoRef cloud_peer 
= GenerateNewCloudIdentityPeerInfo(error
); 
1187     require_quiet(cloud_peer
, err_out
); 
1188     cloud_identity 
= CopyCloudKeychainIdentity(cloud_peer
, error
); 
1189     CFReleaseNull(cloud_peer
); 
1190     require_quiet(cloud_identity
, err_out
); 
1191     require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, error
), err_out
); 
1192     require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), error
), err_out
); 
1198 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account
, SOSCircleRef circle
, SecKeyRef privKey
, CFErrorRef 
*error
) { 
1199     bool retval 
= false; 
1200     CFMutableSetRef iCloud2Remove 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
1202     SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) { 
1203         if(SOSPeerInfoIsCloudIdentity(peer
)) { 
1204             SOSFullPeerInfoRef icfpi 
= SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, peer
, NULL
); 
1206                 CFSetAddValue(iCloud2Remove
, peer
); 
1208             CFReleaseNull(icfpi
); 
1212     if(CFSetGetCount(iCloud2Remove
) > 0) { 
1214         SOSCircleRemovePeers(circle
, privKey
, account
->my_identity
, iCloud2Remove
, error
); 
1216     CFReleaseNull(iCloud2Remove
); 
1220 static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn
, SecKeyRef user_key
, CFErrorRef 
*error
) { 
1221     SOSAccountRef account 
= aTxn
->account
; 
1222     bool result 
= false; 
1224     require(SOSAccountHasCircle(account
, error
), fail
); 
1225     require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
); 
1227     (void) SOSAccountResetAllRings(account
, error
); 
1229     SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1230         bool result 
= false; 
1231         SOSFullPeerInfoRef cloud_identity 
= NULL
; 
1232         CFErrorRef localError 
= NULL
; 
1234         require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
); 
1236         account
->departure_code 
= kSOSNeverLeftCircle
; 
1237         require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
); 
1239         require_quiet(SOSAccountAddiCloudIdentity(account
, circle
, user_key
, error
), err_out
); 
1241         SOSAccountPublishCloudParameters(account
, NULL
); 
1244         if (result 
== false) 
1245             secerror("error resetting circle (%@) to offering: %@", circle
, localError
); 
1246         if (localError 
&& error 
&& *error 
== NULL
) { 
1247             *error 
= localError
; 
1250         CFReleaseNull(localError
); 
1251         CFReleaseNull(cloud_identity
); 
1255     SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
); 
1256     SOSAccountUpdateOutOfSyncViews(aTxn
, SOSViewsGetAllCurrent()); 
1265 bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) { 
1266     SOSAccountRef account 
= aTxn
->account
; 
1267     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1271     CFReleaseNull(account
->my_identity
); 
1272     secnotice("resetToOffering", "Resetting circle to offering by request from client"); 
1274     return user_key 
&& SOSAccountResetCircleToOffering(aTxn
, user_key
, error
); 
1277 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) { 
1278     if (!SOSAccountHasPublicKey(account
, error
)) 
1280     __block 
bool result 
= true; 
1282     result 
&= SOSAccountResetAllRings(account
, error
); 
1284     CFReleaseNull(account
->my_identity
); 
1286     account
->departure_code 
= kSOSWithdrewMembership
; 
1287     secnotice("resetToEmpty", "Reset Circle to empty by client request"); 
1288     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1289         result 
= SOSCircleResetToEmpty(circle
, error
); 
1294         secerror("error: %@", error 
? *error 
: NULL
); 
1299 // MARK: start backups 
1302 bool SOSAccountEnsureInBackupRings(SOSAccountRef account
) { 
1303     __block 
bool result 
= false; 
1304     __block CFErrorRef error 
= NULL
; 
1305     secnotice("backup", "Ensuring in rings"); 
1307     CFDataRef backupKey 
= NULL
; 
1309     require_action_quiet(account
->backup_key
, exit
, result 
= true); 
1311     backupKey 
= SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
); 
1313     bool updateBackupKey 
= !CFEqualSafe(backupKey
, account
->backup_key
); 
1315     if(updateBackupKey
) { 
1316         require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), &error
, ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef 
*error
) { 
1317             return SOSFullPeerInfoUpdateBackupKey(fpi
, account
->backup_key
, error
); 
1320     require_quiet(account
->backup_key
, exit
); // If it went null, we're done now. 
1322     require_quiet(SOSBSKBIsGoodBackupPublic(account
->backup_key
, &error
), exit
); 
1324     CFDataRef recoveryKeyBackFromRing 
= SOSAccountCopyRecoveryPublic(kCFAllocatorDefault
, account
, &error
); 
1326     if(updateBackupKey 
|| recoveryKeyBackFromRing
) { 
1327         // It's a good key, we're going with it. Stop backing up the old way. 
1328         CFErrorRef localError 
= NULL
; 
1329         if (!SOSDeleteV0Keybag(&localError
)) { 
1330             secerror("Failed to delete v0 keybag: %@", localError
); 
1332         CFReleaseNull(localError
); 
1336         // Setup backups the new way. 
1337         SOSAccountForEachBackupView(account
, ^(const void *value
) { 
1338             CFStringRef viewName 
= (CFStringRef
)value
; 
1339             if(updateBackupKey 
|| (recoveryKeyBackFromRing 
&& !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account
, viewName
))) { 
1340                 result 
&= SOSAccountNewBKSBForView(account
, viewName
, &error
); 
1347         secnotice("backupkey", "Failed to setup backup public key: %@", error 
? (CFTypeRef
) error 
: (CFTypeRef
) CFSTR("No error space provided")); 
1349     CFReleaseNull(backupKey
); 
1354 // MARK: Recovery Public Key Functions 
1357 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransactionRef txn
, CFDataRef recovery_key
, CFErrorRef 
*error
){ 
1358     bool retval 
= SOSAccountSetRecoveryKey(txn
->account
, recovery_key
, error
); 
1359     if(retval
) secnotice("recovery", "successfully registered recovery public key"); 
1360     else secnotice("recovery", "could not register recovery public key: %@", *error
); 
1361     SOSClearErrorIfTrue(retval
, error
); 
1365 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransactionRef txn
, CFDataRef recovery_key
, CFErrorRef 
*error
){ 
1366     bool retval 
= SOSAccountRemoveRecoveryKey(txn
->account
, error
); 
1367     SOSClearErrorIfTrue(retval
, error
); 
1371 CFDataRef 
SOSAccountCopyRecoveryPublicKey(SOSAccountTransactionRef txn
, CFErrorRef 
*error
){ 
1372     CFDataRef result 
= NULL
; 
1373     result 
= SOSAccountCopyRecoveryPublic(kCFAllocatorDefault
, txn
->account
, error
); 
1374     if(!result
)  secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error
); 
1376     if (!isData(result
)) { 
1377         CFReleaseNull(result
); 
1379     SOSClearErrorIfTrue(result 
!= NULL
, error
); 
1388 static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn
, SecKeyRef user_key
, 
1389                                 bool use_cloud_peer
, CFErrorRef
* error
) { 
1390     SOSAccountRef account 
= aTxn
->account
; 
1392     __block 
bool result 
= false; 
1393     __block SOSFullPeerInfoRef cloud_full_peer 
= NULL
; 
1395     require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???"))); 
1396     require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
); 
1398     SOSFullPeerInfoRef myCirclePeer 
= account
->my_identity
; 
1400     if (SOSCircleCountPeers(account
->trusted_circle
) == 0 || SOSAccountGhostResultsInReset(account
)) { 
1401         secnotice("resetToOffering", "Resetting circle to offering since there are no peers"); 
1402         // this also clears initial sync data 
1403         result 
= SOSAccountResetCircleToOffering(aTxn
, user_key
, error
); 
1405         SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
); 
1407         if (use_cloud_peer
) { 
1408             cloud_full_peer 
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
); 
1411         SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1412             result 
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
); 
1413             result 
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
); 
1414             account
->departure_code 
= kSOSNeverLeftCircle
; 
1415             if(result 
&& cloud_full_peer
) { 
1416                 CFErrorRef localError 
= NULL
; 
1417                 CFStringRef cloudid 
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
)); 
1418                 require_quiet(cloudid
, finish
); 
1419                 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
); 
1420                 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
); 
1424                     secerror("Failed to join with cloud identity: %@", localError
); 
1425                     CFReleaseNull(localError
); 
1431         if (use_cloud_peer
) { 
1432             SOSAccountUpdateOutOfSyncViews(aTxn
, SOSViewsGetAllCurrent()); 
1437     CFReleaseNull(cloud_full_peer
); 
1441 static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn
, bool use_cloud_identity
, CFErrorRef
* error
) { 
1442     SOSAccountRef account 
= aTxn
->account
; 
1443     bool success 
= false; 
1445     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1446     require_quiet(user_key
, done
); // Fail if we don't get one. 
1448     require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join"))); 
1450     if (account
->my_identity 
!= NULL
) { 
1451         SOSPeerInfoRef myPeer 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
1452         success 
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
); 
1453         require_quiet(!success
, done
); 
1455         SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now. 
1457         if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) { 
1458                 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
)); 
1460                         CFReleaseNull(account
->my_identity
); 
1465     success 
= SOSAccountJoinCircle(aTxn
, user_key
, use_cloud_identity
, error
); 
1467     require_quiet(success
, done
); 
1469     account
->departure_code 
= kSOSNeverLeftCircle
; 
1475 bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) { 
1476         secnotice("circleJoin", "Normal path circle join (SOSAccountJoinCircles)"); 
1477     return SOSAccountJoinCircles_internal(aTxn
, false, error
); 
1480 CFStringRef 
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef 
*error
){ 
1481     CFStringRef result 
= NULL
; 
1483     require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me"))); 
1485     result 
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
1491 bool SOSAccountSetMyDSID(SOSAccountTransactionRef txn
, CFStringRef IDS
, CFErrorRef
* error
){ 
1493     SOSAccountRef account 
= txn
->account
; 
1495     secdebug("IDS Transport", "We are setting our device ID: %@", IDS
); 
1496     if(IDS 
!= NULL 
&& (CFStringGetLength(IDS
) > 0)){ 
1497         require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me"))); 
1499         result 
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) { 
1501             SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
); 
1502             SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDSV2
, error
); 
1503             SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanFalse
, error
); 
1504             SOSFullPeerInfoUpdateTransportFragmentationPreference(account
->my_identity
, kCFBooleanTrue
, error
); 
1505             SOSFullPeerInfoUpdateTransportAckModelPreference(account
->my_identity
, kCFBooleanTrue
, error
); 
1506             return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
); 
1512     // Initiate sync with all IDS peers, since we just learned we can talk that way. 
1513     SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
1514         if (SOSPeerInfoShouldUseIDSTransport(SOSAccountGetMyPeerInfo(account
), peer
)) { 
1515             SOSAccountTransactionAddSyncRequestForPeerID(txn
, SOSPeerInfoGetPeerID(peer
)); 
1520     CFReleaseNull(account
->deviceID
); 
1521     account
->deviceID 
= CFRetainSafe(IDS
); 
1525 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef 
*error
){ 
1527     //construct message dictionary, circle -> peerID -> message 
1529     CFMutableDictionaryRef peerToMessage 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1531     CFStringRef operationString 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSSendOneMessage
); 
1532     CFDictionaryRef rawMessage 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
1533                                                               kIDSOperationType
, operationString
, 
1534                                                               kIDSMessageToSendKey
, CFSTR("send IDS test message"), 
1537     SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
1538         CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), rawMessage
); 
1541     result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, peerToMessage
, error
); 
1543     CFReleaseNull(peerToMessage
); 
1544     CFReleaseNull(operationString
); 
1545     CFReleaseNull(rawMessage
); 
1549 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef 
*error
){ 
1550     bool result 
= false; 
1551     //construct message dictionary, circle -> peerID -> message 
1553     if(account
->ids_message_transport 
== NULL
) 
1554         account
->ids_message_transport 
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
); 
1556     require_quiet(account
->ids_message_transport
, fail
); 
1557     CFMutableDictionaryRef peerToMessage 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1559     CFStringRef operationString 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSStartPingTestMessage
); 
1560     CFDictionaryRef rawMessage 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
1561                                                               kIDSOperationType
, operationString
, 
1562                                                               kIDSMessageToSendKey
, CFSTR("send IDS test message"), 
1566     SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
1567         CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), rawMessage
); 
1570     result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, peerToMessage
, error
); 
1572     CFReleaseNull(peerToMessage
); 
1573     CFReleaseNull(rawMessage
); 
1574     CFReleaseNull(operationString
); 
1579 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccountRef account
, CFErrorRef 
*error
){ 
1582     __block 
bool success 
= true; 
1583     __block CFErrorRef localError 
= NULL
; 
1584     dispatch_semaphore_t wait_for 
= dispatch_semaphore_create(0); 
1585     dispatch_retain(wait_for
); // Both this scope and the block own it 
1587     SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){ 
1588         success 
= (sync_error 
== NULL
); 
1590             CFRetainAssign(localError
, sync_error
); 
1593         dispatch_semaphore_signal(wait_for
); 
1594         dispatch_release(wait_for
); 
1597     dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
); 
1598     dispatch_release(wait_for
); 
1600     if(!success 
&& localError 
!= NULL 
&& error 
!= NULL
){ 
1601         secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError
); 
1602         *error 
= localError
; 
1606         secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID"); 
1611 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn
, CFErrorRef
* error
) { 
1612         secnotice("circleJoin", "Joining after restore (SOSAccountJoinCirclesAfterRestore)"); 
1613     return SOSAccountJoinCircles_internal(aTxn
, true, error
); 
1617 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
) 
1621     secnotice("leaveCircle", "Leaving circle by client request"); 
1622     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1623         return sosAccountLeaveCircle(account
, circle
, error
); 
1626     account
->departure_code 
= kSOSWithdrewMembership
; 
1631 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
) 
1633     bool result 
= false; 
1634     CFMutableSetRef peersToRemove 
= NULL
; 
1635     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1636     require_action_quiet(user_key
, errOut
, secnotice("removePeers", "Can't remove without userKey")); 
1638     SOSFullPeerInfoRef me_full 
= SOSAccountGetMyFullPeerInfo(account
); 
1639     SOSPeerInfoRef me 
= SOSAccountGetMyPeerInfo(account
); 
1640     require_action_quiet(me_full 
&& me
, errOut
, { 
1641                             SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Can't remove without being active peer")); 
1642                             secnotice("removePeers", "Can't remove without being active peer"); 
1645     result 
= true; // beyond this point failures would be rolled up in AccountModifyCircle. 
1647     peersToRemove 
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
); 
1648     require_action_quiet(peersToRemove
, errOut
, secnotice("removePeers", "No peerSet to remove")); 
1650     // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling). 
1651     bool leaveCircle 
= CFSetContainsValue(peersToRemove
, me
); 
1652     CFSetRemoveValue(peersToRemove
, me
); 
1654     result 
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1655         bool success 
= false; 
1657         if(CFSetGetCount(peersToRemove
) != 0) { 
1658             require_quiet(SOSCircleRemovePeers(circle
, user_key
, me_full
, peersToRemove
, error
), done
); 
1659             success 
= SOSAccountGenerationSignatureUpdate(account
, error
); 
1660         } else success 
= true; 
1662         if (success 
&& leaveCircle
) { 
1663             secnotice("leaveCircle", "Leaving circle by client request"); 
1664             success 
= sosAccountLeaveCircle(account
, circle
, error
); 
1673     CFReleaseNull(peersToRemove
); 
1678 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) { 
1679     dispatch_queue_t queue 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0); 
1680     dispatch_group_t group 
= dispatch_group_create(); 
1681     __block 
bool result 
= false; 
1682     secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
); 
1683     // Add a task to the group 
1684     dispatch_group_async(group
, queue
, ^{ 
1685         SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) { 
1686             secnotice("leaveCircle", "Leaving circle by client request"); 
1687             return sosAccountLeaveCircle(account
, circle
, error
); 
1690     dispatch_time_t milestone 
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds 
* NSEC_PER_SEC
); 
1691     dispatch_group_wait(group
, milestone
); 
1693     account
->departure_code 
= kSOSWithdrewMembership
; 
1695     dispatch_release(group
); 
1701 // MARK: Application 
1704 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
, 
1705                                               bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) { 
1706         SOSPeerInfoRef me 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
1707         CFErrorRef peer_error 
= NULL
; 
1708         if (account
->trusted_circle 
&& me 
&& 
1709             SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) { 
1710             SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) { 
1711                 __block 
bool modified 
= false; 
1712                 CFArrayForEach(peer_infos
, ^(const void *value
) { 
1713                     SOSPeerInfoRef peer 
= (SOSPeerInfoRef
) value
; 
1714                     if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) { 
1715                         if (action(circle
, account
->my_identity
, peer
)) { 
1724             secerror("Got error in SOSCircleHasPeer: %@", peer_error
); 
1725         CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here. 
1728 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) { 
1729     SecKeyRef user_key 
= SOSAccountGetPrivateCredential(account
, error
); 
1733     __block 
bool success 
= true; 
1734     __block 
int64_t num_peers 
= 0; 
1736     for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) { 
1737         bool accepted 
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
); 
1741                         num_peers 
= MAX(num_peers
, SOSCircleCountPeers(circle
)); 
1748 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) { 
1749     __block 
bool success 
= true; 
1750     __block 
int64_t num_peers 
= 0; 
1752     for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) { 
1753         bool rejected 
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
); 
1757                         num_peers 
= MAX(num_peers
, SOSCircleCountPeers(circle
)); 
1765 CFStringRef 
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) { 
1766     return CFSTR("We're compatible, go away"); 
1769 enum DepartureReason 
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) { 
1770     return account
->departure_code
; 
1773 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) { 
1774         account
->departure_code 
= reason
; 
1778 CFArrayRef 
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef 
*error
) { 
1779     CFArrayRef result 
= NULL
; 
1780     CFNumberRef generation 
= NULL
; 
1782     require_quiet(SOSAccountHasPublicKey(account
, error
), fail
); 
1783     require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle"))); 
1785     generation 
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
); 
1786     result 
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
); 
1792 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef 
*error
) { 
1793     if (!SOSAccountHasPublicKey(account
, error
)) 
1796     return account
->user_public_trusted
; 
1799 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef 
*error
) { 
1800     // TODO: this result is never set or used 
1803     secnotice("updates", "Ensuring peer registration."); 
1805     require_quiet(account
->trusted_circle
, done
); 
1806     require_quiet(account
->my_identity
, done
); 
1807     require_quiet(account
->user_public_trusted
, done
); 
1809     // If we are not in the circle, there is no point in setting up peers 
1810     require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
); 
1812     // This code only uses the SOSFullPeerInfoRef for two things: 
1813     //  - Finding out if this device is in the trusted circle 
1814     //  - Using the peerID for this device to see if the current peer is "me" 
1815     //  - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder 
1817     CFStringRef my_id 
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)); 
1819     SOSCircleForEachValidSyncingPeer(account
->trusted_circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
1820         if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) { 
1821             CFErrorRef localError 
= NULL
; 
1822             SOSTransportMessageRef messageTransport 
= NULL
; 
1824             messageTransport 
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport 
: account
->kvs_message_transport
; 
1826             SOSEngineInitializePeerCoder(messageTransport
->engine
, account
->my_identity
, peer
, &localError
); 
1828                 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
); 
1829             CFReleaseSafe(localError
); 
1833     //Initialize our device ID 
1834     SOSTransportMessageIDSGetIDSDeviceID(account
);     
1841 bool SOSAccountAddEscrowRecords(SOSAccountRef account
, CFStringRef dsid
, CFDictionaryRef record
, CFErrorRef 
*error
){ 
1842     CFMutableDictionaryRef escrowRecords 
= (CFMutableDictionaryRef
)SOSAccountGetValue(account
, kSOSEscrowRecord
, error
); 
1843     CFMutableDictionaryRef escrowCopied 
= NULL
; 
1844     bool success 
= false; 
1846     if(isDictionary(escrowRecords
) && escrowRecords 
!= NULL
) 
1847         escrowCopied 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
); 
1849         escrowCopied 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1851     CFDictionaryAddValue(escrowCopied
, dsid
, record
); 
1852     SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
); 
1857     CFReleaseNull(escrowCopied
); 
1863 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef 
*error
){ 
1864     bool success 
= false; 
1866     CFDictionaryRef escrowRecords 
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
); 
1867     success 
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
); 
1872 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef 
*error
) 
1874     CFStringRef operationString 
= NULL
; 
1875     CFDictionaryRef rawMessage 
= NULL
; 
1876     CFMutableSetRef peers 
= NULL
; 
1877     CFMutableDictionaryRef peerList 
= NULL
; 
1878     char* message 
= NULL
; 
1879     bool result 
= false; 
1881     if(account
->ids_message_transport 
== NULL
) 
1882         account
->ids_message_transport 
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
); 
1884     require_quiet(account
->ids_message_transport
, fail
); 
1886     //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item 
1888     operationString 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSPeerAvailability
); 
1889     rawMessage 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
1890                                               kIDSOperationType
, operationString
, 
1891                                               kIDSMessageToSendKey
, CFSTR("checking peers"), 
1894     peerList 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1895     SOSCircleRef circle 
= account
->trusted_circle
; 
1897     //check each peer to make sure they have the right view set enabled 
1898     CFSetRef mySubSet 
= SOSViewsGetV0SubviewSet(); 
1899     SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
1900         if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))){ 
1901             CFMutableSetRef peerViews 
= SOSPeerInfoCopyEnabledViews(peer
); 
1902             CFSetRef intersectSets 
= CFSetCreateIntersection(kCFAllocatorDefault
, mySubSet
, peerViews
); 
1903             if(CFEqualSafe(intersectSets
, mySubSet
)){ 
1904                 CFStringRef deviceID 
= SOSPeerInfoCopyDeviceID(peer
); 
1905                 if(deviceID 
!= NULL
) 
1906                     CFDictionaryAddValue(peerList
, SOSPeerInfoGetPeerID(peer
), rawMessage
); 
1907                 CFReleaseNull(deviceID
); 
1909             CFReleaseNull(peerViews
); 
1910             CFReleaseNull(intersectSets
); 
1914     require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
); 
1915     result 
= SOSTransportMessageSendMessages(account
->ids_message_transport
, peerList
, error
); 
1918     CFReleaseNull(rawMessage
); 
1919     CFReleaseNull(operationString
); 
1920     CFReleaseNull(peerList
); 
1921     CFReleaseNull(peers
); 
1927 void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) { 
1928     if (!SOSAccountIsInCircle(account
, NULL
)) 
1931     SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) { 
1932         __block 
bool updated 
= false; 
1933         CFSetForEach(account
->retirees
, ^(CFTypeRef element
){ 
1934             SOSPeerInfoRef retiree 
= asSOSPeerInfo(element
); 
1936             if (retiree 
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) { 
1938                 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
); 
1939                 CFErrorRef cleanupError 
= NULL
; 
1940                 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
)) 
1941                     secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
); 
1942                 CFReleaseSafe(cleanupError
); 
1950 static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFErrorRef 
*error
) { 
1951     size_t total_payload 
= 0; 
1953     CFDataRef publicBytes 
= NULL
; 
1954     OSStatus result 
= SecKeyCopyPublicBytes(pubKey
, &publicBytes
); 
1956     if (result 
!= errSecSuccess
) { 
1957         SOSCreateError(kSOSErrorBadKey
, CFSTR("Failed to export public bytes"), NULL
, error
); 
1961     require_quiet(accumulate_size(&total_payload
, der_sizeof_number(gencount
, error
)), errOut
); 
1962     require_quiet(accumulate_size(&total_payload
, der_sizeof_data_or_null(publicBytes
, error
)), errOut
); 
1963     require_quiet(accumulate_size(&total_payload
, der_sizeof_data_or_null(signature
, error
)), errOut
); 
1964     return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, total_payload
); 
1967     SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
); 
1971 static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) { 
1972     CFDataRef publicBytes 
= NULL
; 
1974     OSStatus result 
= SecKeyCopyPublicBytes(pubKey
, &publicBytes
); 
1976     if (result 
!= errSecSuccess
) { 
1977         SOSCreateError(kSOSErrorBadKey
, CFSTR("Failed to export public bytes"), NULL
, error
); 
1982     der_end 
=  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
, 
1983             der_encode_number(gencount
, error
, der
, 
1984             der_encode_data_or_null(publicBytes
, error
, der
, 
1985             der_encode_data_or_null(signature
, error
, der
, der_end
)))); 
1989 static CFDataRef 
SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount
, SecKeyRef pubKey
, CFDataRef signature
, CFAllocatorRef allocator
, CFErrorRef 
*error
) 
1991     return CFDataCreateWithDER(kCFAllocatorDefault
, SOSPiggyBackBlobGetDEREncodedSize(gencount
, pubKey
, signature
, error
), ^uint8_t*(size_t size
, uint8_t *buffer
) { 
1992         return SOSPiggyBackBlobEncodeToDER(gencount
, pubKey
, signature
, error
, buffer
, (uint8_t *) buffer 
+ size
); 
1996 struct piggyBackBlob 
{ 
1997     SOSGenCountRef gencount
; 
1999     CFDataRef signature
; 
2002 static struct piggyBackBlob 
*SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator
, CFErrorRef 
*error
, 
2003                                                            const uint8_t** der_p
, const uint8_t *der_end
) { 
2004     const uint8_t *sequence_end
; 
2005     struct piggyBackBlob 
*retval 
= NULL
; 
2006     SOSGenCountRef gencount 
= NULL
; 
2007     CFDataRef signature 
= NULL
; 
2008     CFDataRef publicBytes 
= NULL
; 
2010     *der_p 
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, *der_p
, der_end
); 
2011     require_action_quiet(sequence_end 
!= NULL
, errOut
, 
2012                          SOSCreateError(kSOSErrorBadFormat
, CFSTR("Bad Blob DER"), (error 
!= NULL
) ? *error 
: NULL
, error
)); 
2013     *der_p 
= der_decode_number(allocator
, 0, &gencount
, error
, *der_p
, sequence_end
); 
2014     *der_p 
= der_decode_data_or_null(kCFAllocatorDefault
, &publicBytes
, error
, *der_p
, der_end
); 
2015     *der_p 
= der_decode_data_or_null(kCFAllocatorDefault
, &signature
, error
, *der_p
, der_end
); 
2016     require_action_quiet(*der_p 
&& *der_p 
== der_end
, errOut
, 
2017                          SOSCreateError(kSOSErrorBadFormat
, CFSTR("Didn't consume all bytes for pbblob"), (error 
!= NULL
) ? *error 
: NULL
, error
)); 
2018     retval 
= malloc(sizeof(struct piggyBackBlob
)); 
2019     retval
->gencount 
= gencount
; 
2020     retval
->signature 
= signature
; 
2021     retval
->pubKey 
= SecKeyCreateFromPublicData(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, publicBytes
); 
2025         CFReleaseNull(gencount
); 
2026         CFReleaseNull(publicBytes
); 
2027         CFReleaseNull(signature
); 
2032 static struct piggyBackBlob 
*SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator
, CFDataRef blobData
, CFErrorRef 
*error
) 
2034     size_t size 
= CFDataGetLength(blobData
); 
2035     const uint8_t *der 
= CFDataGetBytePtr(blobData
); 
2036     struct piggyBackBlob 
*inflated 
= SOSPiggyBackBlobCreateFromDER(allocator
, error
, &der
, der 
+ size
); 
2042 SOSPeerInfoRef 
SOSAccountCopyApplication(SOSAccountRef account
, CFErrorRef
* error
) { 
2043     SOSPeerInfoRef applicant 
= NULL
; 
2044     SecKeyRef userKey 
= SOSAccountGetPrivateCredential(account
, error
); 
2045     if(!userKey
) return false; 
2046     require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), errOut
); 
2047     require(SOSFullPeerInfoPromoteToApplication(account
->my_identity
, userKey
, error
), errOut
); 
2048     applicant 
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
,  (SOSFullPeerInfoGetPeerInfo(account
->my_identity
)), error
); 
2054 CFDataRef 
SOSAccountCopyCircleJoiningBlob(SOSAccountRef account
, SOSPeerInfoRef applicant
, CFErrorRef 
*error
) { 
2055     SOSGenCountRef gencount 
= NULL
; 
2056     CFDataRef signature 
= NULL
; 
2057     SecKeyRef ourKey 
= NULL
; 
2059     CFDataRef pbblob 
= NULL
; 
2061         secnotice("circleJoin", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)"); 
2063     SecKeyRef userKey 
= SOSAccountGetTrustedPublicCredential(account
, error
); 
2064     require_quiet(userKey
, errOut
); 
2066     require_action_quiet(applicant
, errOut
, SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("No applicant provided"), (error 
!= NULL
) ? *error 
: NULL
, error
)); 
2067     require_quiet(SOSPeerInfoApplicationVerify(applicant
, userKey
, error
), errOut
); 
2070         SOSFullPeerInfoRef fpi 
= SOSAccountGetMyFullPeerInfo(account
); 
2071         ourKey 
= SOSFullPeerInfoCopyDeviceKey(fpi
, error
); 
2072         require_quiet(ourKey
, errOut
); 
2075     SOSCircleRef currentCircle 
= SOSAccountGetCircle(account
, error
); 
2076     require_quiet(currentCircle
, errOut
); 
2078     SOSCircleRef prunedCircle 
= SOSCircleCopyCircle(NULL
, currentCircle
, error
); 
2079     require_quiet(prunedCircle
, errOut
); 
2080     require_quiet(SOSCirclePreGenerationSign(prunedCircle
, userKey
, error
), errOut
); 
2082     gencount 
= SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle
)); 
2084     signature 
= SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle
, applicant
, ourKey
, error
); 
2085     require_quiet(signature
, errOut
); 
2087     pbblob 
= SOSPiggyBackBlobCopyEncodedData(gencount
, ourKey
, signature
, kCFAllocatorDefault
, error
); 
2090     CFReleaseNull(gencount
); 
2091     CFReleaseNull(signature
); 
2092     CFReleaseNull(ourKey
); 
2095                 secnotice("circleJoin", "Failed to make circle joining blob as sponsor %@", *error
); 
2101 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account
, CFDataRef joiningBlob
, CFErrorRef 
*error
) { 
2102     bool retval 
= false; 
2103     SecKeyRef userKey 
= NULL
; 
2104     struct piggyBackBlob 
*pbb 
= NULL
; 
2106         secnotice("circleJoin", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)"); 
2108     userKey 
= SOSAccountGetPrivateCredential(account
, error
); 
2109     require_quiet(userKey
, errOut
); 
2110     pbb 
= SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault
, joiningBlob
, error
); 
2111     require_quiet(pbb
, errOut
); 
2113     SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
); 
2115     retval 
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef copyOfCurrent
) { 
2116         return SOSCircleAcceptPeerFromHSA2(copyOfCurrent
, userKey
, 
2120                                            account
->my_identity
, error
);; 
2126         CFReleaseNull(pbb
->gencount
); 
2127         CFReleaseNull(pbb
->pubKey
); 
2128         CFReleaseNull(pbb
->signature
); 
2134 static char boolToChars(bool val
, char truechar
, char falsechar
) { 
2135     return val
? truechar
: falsechar
; 
2138 #define ACCOUNTLOGSTATE "accountLogState" 
2139 void SOSAccountLogState(SOSAccountRef account
) { 
2140     bool hasPubKey 
= account
->user_public 
!= NULL
; 
2141     bool pubTrusted 
= account
->user_public_trusted
; 
2142     bool hasPriv 
= account
->_user_private 
!= NULL
; 
2143     SOSCCStatus stat 
= SOSAccountGetCircleStatus(account
, NULL
); 
2144     CFStringRef userPubKeyID 
=  (account
->user_public
) ? SOSCopyIDOfKeyWithLength(account
->user_public
, 8, NULL
): 
2145             CFStringCreateCopy(kCFAllocatorDefault
, CFSTR("*No Key*")); 
2147     secnotice(ACCOUNTLOGSTATE
, "Start"); 
2149     secnotice(ACCOUNTLOGSTATE
, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]", 
2150               boolToChars(hasPubKey
, 'U', 'u'), boolToChars(pubTrusted
, 'T', 't'), boolToChars(hasPriv
, 'I', 'i'), 
2152               SOSAccountGetSOSCCStatusString(stat
) 
2154     CFReleaseNull(userPubKeyID
); 
2155     if(account
->trusted_circle
)  SOSCircleLogState(ACCOUNTLOGSTATE
, account
->trusted_circle
, account
->user_public
, SOSAccountGetMyPeerID(account
)); 
2156     else secnotice(ACCOUNTLOGSTATE
, "ACCOUNT: No Circle"); 
2159 void SOSAccountLogViewState(SOSAccountRef account
) { 
2160     bool isInCircle 
= SOSAccountIsInCircle(account
, NULL
); 
2161     require_quiet(isInCircle
, imOut
); 
2162     SOSPeerInfoRef mpi 
= SOSAccountGetMyPeerInfo(account
); 
2163     bool isInitialComplete 
= SOSAccountHasCompletedInitialSync(account
); 
2164     bool isBackupComplete 
= SOSAccountHasCompletedRequiredBackupSync(account
); 
2166     CFSetRef views 
= mpi 
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
; 
2167     CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) { 
2168         secnotice(ACCOUNTLOGSTATE
, "Sync: %c%c PeerViews: %@", 
2169                   boolToChars(isInitialComplete
, 'I', 'i'), 
2170                   boolToChars(isBackupComplete
, 'B', 'b'), 
2173     CFReleaseNull(views
); 
2174     CFSetRef unsyncedViews 
= SOSAccountCopyOutstandingViews(account
); 
2175     CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) { 
2176         secnotice(ACCOUNTLOGSTATE
, "outstanding views: %@", description
); 
2178     CFReleaseNull(unsyncedViews
); 
2181     secnotice(ACCOUNTLOGSTATE
, "Finish"); 
2187 void SOSAccountSetTestSerialNumber(SOSAccountRef account
, CFStringRef serial
) { 
2188     if(!isString(serial
)) return; 
2189     CFMutableDictionaryRef newv2dict 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
2190     CFDictionarySetValue(newv2dict
, sSerialNumberKey
, serial
); 
2191     SOSAccountUpdateV2Dictionary(account
, newv2dict
);