6 #include "SOSAccountPriv.h" 
   7 #include "SOSAccountLog.h" 
   9 #include <Security/SecureObjectSync/SOSAccountHSAJoin.h> 
  10 #include <Security/SecureObjectSync/SOSTransportCircle.h> 
  11 #include <Security/SecureObjectSync/SOSTransport.h> 
  12 #include <Security/SecureObjectSync/SOSViews.h> 
  13 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h> 
  14 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h> 
  15 #include <Security/SecureObjectSync/SOSPeerInfoV2.h> 
  16 #include <Security/SecureObjectSync/SOSPeerInfoDER.h> 
  17 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h> 
  18 #include <Security/SecureObjectSync/SOSAccountGhost.h> 
  21 static void DifferenceAndCall(CFSetRef old_members
, CFSetRef new_members
, void (^updatedCircle
)(CFSetRef additions
, CFSetRef removals
)) 
  23     CFMutableSetRef additions 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, new_members
); 
  24     CFMutableSetRef removals 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, old_members
); 
  27     CFSetForEach(old_members
, ^(const void * value
) { 
  28         CFSetRemoveValue(additions
, value
); 
  31     CFSetForEach(new_members
, ^(const void * value
) { 
  32         CFSetRemoveValue(removals
, value
); 
  35     updatedCircle(additions
, removals
); 
  37     CFReleaseSafe(additions
); 
  38     CFReleaseSafe(removals
); 
  41 static CFMutableSetRef 
SOSAccountCopyIntersectedViews(CFSetRef peerViews
, CFSetRef myViews
) { 
  42     __block CFMutableSetRef views 
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
  43     if (peerViews 
&& myViews
) CFSetForEach(peerViews
, ^(const void *view
) { 
  44         if (CFSetContainsValue(myViews
, view
)) { 
  45             CFSetAddValue(views
, view
); 
  51 static inline bool isSyncing(SOSPeerInfoRef peer
, SecKeyRef upub
) { 
  52     if(!SOSPeerInfoApplicationVerify(peer
, upub
, NULL
)) return false; 
  53     if(SOSPeerInfoIsRetirementTicket(peer
)) return false; 
  57 static bool isBackupSOSRing(SOSRingRef ring
) 
  59     return isSOSRing(ring
) && (kSOSRingBackup 
== SOSRingGetType(ring
)); 
  62 static void SOSAccountAppendPeerMetasForViewBackups(SOSAccountRef account
, CFSetRef views
, CFMutableArrayRef appendTo
) 
  64     CFMutableDictionaryRef ringToViewTable 
= NULL
; 
  66     require_quiet(SOSAccountIsInCircle(account
, NULL
), done
); 
  68     require_action_quiet(SOSAccountHasCompletedRequiredBackupSync(account
), done
, 
  69                          secnotice("backup", "Haven't finished initial backup syncing, not registering backup metas with engine")); 
  71     require_action_quiet(SOSPeerInfoV2DictionaryHasData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
), done
, 
  72                          secnotice("backup", "No key to backup to, we don't enable individual view backups")); 
  74     ringToViewTable 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
  76     CFSetForEach(views
, ^(const void *value
) { 
  77         CFStringRef viewName 
= value
; 
  78         if (isString(viewName
) && !CFEqualSafe(viewName
, kSOSViewKeychainV0
)) { 
  79             CFStringRef ringName 
= SOSBackupCopyRingNameForView(viewName
); 
  81             SOSRingRef ring 
= SOSAccountCopyRing(account
, ringName
, NULL
); 
  82             if (ring 
&& isBackupSOSRing(ring
)) { 
  83                 CFTypeRef currentValue 
= (CFTypeRef
) CFDictionaryGetValue(ringToViewTable
, ring
); 
  85                 if (isSet(currentValue
)) { 
  86                     CFSetAddValue((CFMutableSetRef
)currentValue
, viewName
); 
  88                     CFMutableSetRef viewNameSet 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
  89                     CFSetAddValue(viewNameSet
, viewName
); 
  91                     CFDictionarySetValue(ringToViewTable
, ring
, viewNameSet
); 
  92                     CFReleaseNull(viewNameSet
); 
  95                 secwarning("View '%@' not being backed up – ring %@:%@ not backup ring.", viewName
, ringName
, ring
); 
  97             CFReleaseNull(ringName
); 
 102     CFDictionaryForEach(ringToViewTable
, ^(const void *key
, const void *value
) { 
 103         SOSRingRef ring 
= (SOSRingRef
) key
; 
 104         CFSetRef viewNames 
= asSet(value
, NULL
); 
 105         if (isSOSRing(ring
) && viewNames
) { 
 106             if (SOSAccountIntersectsWithOutstanding(account
, viewNames
)) { 
 107                 CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) { 
 108                     secnotice("engine-notify", "Not ready, no peer meta: R: %@ Vs: %@", SOSRingGetName(ring
), ringViews
); 
 111                 bool meta_added 
= false; 
 112                 CFErrorRef create_error 
= NULL
; 
 113                 SOSBackupSliceKeyBagRef key_bag 
= NULL
; 
 114                 SOSPeerMetaRef newMeta 
= NULL
; 
 116                 CFDataRef ring_payload 
= SOSRingGetPayload(ring
, NULL
); 
 117                 require_quiet(isData(ring_payload
), skip
); 
 119                 key_bag 
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, ring_payload
, &create_error
); 
 120                 require_quiet(key_bag
, skip
); 
 122                 newMeta 
= SOSPeerMetaCreateWithComponents(SOSRingGetName(ring
), viewNames
, ring_payload
); 
 123                 require_quiet(SecAllocationError(newMeta
, &create_error
, CFSTR("Didn't make peer meta for: %@"), ring
), skip
); 
 124                 CFArrayAppendValue(appendTo
, newMeta
); 
 126                 CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) { 
 127                     secnotice("engine-notify", "Backup peer meta: R: %@ Vs: %@ VD: %@", SOSRingGetName(ring
), ringViews
, ring_payload
); 
 134                     CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) { 
 135                         secerror("Failed to register backup meta from %@ for views %@. Error (%@)", ring
, ringViews
, create_error
); 
 138                 CFReleaseNull(newMeta
); 
 139                 CFReleaseNull(key_bag
); 
 140                 CFReleaseNull(create_error
); 
 146     CFReleaseNull(ringToViewTable
); 
 149 bool SOSAccountSyncingV0(SOSAccountRef account
) { 
 150     __block 
bool syncingV0 
= false; 
 151     SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
 152         if (SOSPeerInfoIsEnabledView(peer
, kSOSViewKeychainV0
)) { 
 160 void SOSAccountNotifyEngines(SOSAccountRef account
) 
 162     SOSPeerInfoRef myPi 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
 163     CFStringRef myPi_id 
= SOSPeerInfoGetPeerID(myPi
); 
 164     CFMutableArrayRef syncing_peer_metas 
= NULL
; 
 165     CFMutableArrayRef zombie_peer_metas 
= NULL
; 
 166     CFErrorRef localError 
= NULL
; 
 167     SOSPeerMetaRef myMeta 
= NULL
; 
 169     if (myPi_id 
&& isSyncing(myPi
, account
->user_public
) && SOSCircleHasPeer(account
->trusted_circle
, myPi
, NULL
)) { 
 170         CFMutableSetRef myViews 
= SOSPeerInfoCopyEnabledViews(myPi
); 
 172         // We add V0 views to everyone if we see a V0 peer, or a peer with the view explicity enabled 
 173         // V2 peers shouldn't be explicity enabling the uber V0 view, though the seeds did. 
 174         __block 
bool addV0Views 
= SOSAccountSyncingV0(account
); 
 176         syncing_peer_metas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 177         zombie_peer_metas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 178         SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
 179             CFMutableArrayRef arrayToAddTo 
= isSyncing(peer
, account
->user_public
) ? syncing_peer_metas 
: zombie_peer_metas
; 
 181             // Compute views each peer is in that we are also in ourselves 
 182             CFMutableSetRef peerEnabledViews 
= SOSPeerInfoCopyEnabledViews(peer
); 
 183             CFMutableSetRef views 
= SOSAccountCopyIntersectedViews(peerEnabledViews
, myViews
); 
 184             CFReleaseNull(peerEnabledViews
); 
 187                 CFSetAddValue(views
, kSOSViewKeychainV0
); 
 190             CFStringSetPerformWithDescription(views
, ^(CFStringRef viewsDescription
) { 
 191                 secnotice("engine-notify", "Meta: %@: %@", SOSPeerInfoGetPeerID(peer
), viewsDescription
); 
 194             SOSPeerMetaRef peerMeta 
= SOSPeerMetaCreateWithComponents(SOSPeerInfoGetPeerID(peer
), views
, NULL
); 
 195             CFReleaseNull(views
); 
 197             CFArrayAppendValue(arrayToAddTo
, peerMeta
); 
 198             CFReleaseNull(peerMeta
); 
 201         // We don't make a backup peer meta for the magic V0 peer 
 202         // Set up all the rest before we munge the set 
 203         SOSAccountAppendPeerMetasForViewBackups(account
, myViews
, syncing_peer_metas
); 
 205         // If we saw someone else needing V0, we sync V0, too! 
 207             CFSetAddValue(myViews
, kSOSViewKeychainV0
); 
 210         CFStringSetPerformWithDescription(myViews
, ^(CFStringRef viewsDescription
) { 
 211             secnotice("engine-notify", "My Meta: %@: %@", myPi_id
, viewsDescription
); 
 213         myMeta 
= SOSPeerMetaCreateWithComponents(myPi_id
, myViews
, NULL
); 
 214         CFReleaseSafe(myViews
); 
 217     SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
); 
 219         SOSEngineCircleChanged(engine
, myMeta
, syncing_peer_metas
, zombie_peer_metas
); 
 222     CFReleaseNull(myMeta
); 
 223     CFReleaseSafe(localError
); 
 224     CFReleaseNull(syncing_peer_metas
); 
 225     CFReleaseNull(zombie_peer_metas
); 
 228 // Upcoming call to View Changes Here 
 229 static void SOSAccountNotifyOfChange(SOSAccountRef account
, SOSCircleRef oldCircle
, SOSCircleRef newCircle
) 
 231     account
->circle_rings_retirements_need_attention 
= true; 
 233     CFMutableSetRef old_members 
= SOSCircleCopyPeers(oldCircle
, kCFAllocatorDefault
); 
 234     CFMutableSetRef new_members 
= SOSCircleCopyPeers(newCircle
, kCFAllocatorDefault
); 
 236     CFMutableSetRef old_applicants 
= SOSCircleCopyApplicants(oldCircle
, kCFAllocatorDefault
); 
 237     CFMutableSetRef new_applicants 
= SOSCircleCopyApplicants(newCircle
, kCFAllocatorDefault
); 
 239     SOSPeerInfoRef me 
= SOSAccountGetMyPeerInfo(account
); 
 240     if(me 
&& CFSetContainsValue(new_members
, me
)) 
 241         SOSAccountSetValue(account
, kSOSEscrowRecord
, kCFNull
, NULL
); //removing the escrow records from the account object 
 243     DifferenceAndCall(old_members
, new_members
, ^(CFSetRef added_members
, CFSetRef removed_members
) { 
 244         DifferenceAndCall(old_applicants
, new_applicants
, ^(CFSetRef added_applicants
, CFSetRef removed_applicants
) { 
 245             CFArrayForEach(account
->change_blocks
, ^(const void * notificationBlock
) { 
 246                 secnotice("updates", "calling change block"); 
 247                 ((SOSAccountCircleMembershipChangeBlock
) notificationBlock
)(newCircle
, added_members
, removed_members
, added_applicants
, removed_applicants
); 
 252     CFReleaseNull(old_applicants
); 
 253     CFReleaseNull(new_applicants
); 
 255     CFReleaseNull(old_members
); 
 256     CFReleaseNull(new_members
); 
 260 CFDictionaryRef 
SOSAccountHandleRetirementMessages(SOSAccountRef account
, CFDictionaryRef circle_retirement_messages
, CFErrorRef 
*error
) { 
 261     CFStringRef circle_name 
= SOSCircleGetName(account
->trusted_circle
); 
 262     CFMutableArrayRef handledRetirementIDs 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 263     // We only handle one circle, look it up: 
 265     require_quiet(account
->trusted_circle
, finish
); // We don't fail, we intentionally handle nothing. 
 266     CFDictionaryRef retirement_dictionary 
= asDictionary(CFDictionaryGetValue(circle_retirement_messages
, circle_name
), error
); 
 267     require_quiet(retirement_dictionary
, finish
); 
 268     CFDictionaryForEach(retirement_dictionary
, ^(const void *key
, const void *value
) { 
 270             SOSPeerInfoRef pi 
= SOSPeerInfoCreateFromData(NULL
, error
, (CFDataRef
) value
); 
 271             if(pi 
&& CFEqual(key
, SOSPeerInfoGetPeerID(pi
)) && SOSPeerInfoInspectRetirementTicket(pi
, error
)) { 
 272                 CFSetAddValue(account
->retirees
, pi
); 
 274                 account
->circle_rings_retirements_need_attention 
= true; // Have to handle retirements. 
 276                 CFArrayAppendValue(handledRetirementIDs
, key
); 
 282     // If we are in the retiree list, we somehow got resurrected 
 283     // clearly we took care of proper departure before so leave 
 284     // and delcare that we withdrew this time. 
 285     SOSPeerInfoRef me 
= SOSAccountGetMyPeerInfo(account
); 
 286     if (me 
&& CFSetContainsValue(account
->retirees
, me
)) { 
 287         SOSAccountPurgeIdentity(account
); 
 288         account
->departure_code 
= kSOSDiscoveredRetirement
; 
 293     CFDictionaryRef result 
= (CFArrayGetCount(handledRetirementIDs
) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
) 
 294                                                                           : CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, circle_name
, handledRetirementIDs
, NULL
); 
 296     CFReleaseNull(handledRetirementIDs
); 
 301 static SOSCircleRef 
SOSAccountCreateCircleFrom(CFStringRef circleName
, CFTypeRef value
, CFErrorRef 
*error
) { 
 302     if (value 
&& !isData(value
) && !isNull(value
)) { 
 303         secnotice("circleCreat", "Value provided not appropriate for a circle"); 
 304         CFStringRef description 
= CFCopyTypeIDDescription(CFGetTypeID(value
)); 
 305         SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, NULL
, 
 306                                  CFSTR("Expected data or NULL got %@"), description
); 
 307         CFReleaseSafe(description
); 
 311     SOSCircleRef circle 
= NULL
; 
 312     if (!value 
|| isNull(value
)) { 
 313         secnotice("circleCreat", "No circle found in data: %@", value
); 
 316         circle 
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, error
); 
 318             CFStringRef name 
= SOSCircleGetName(circle
); 
 319             if (!CFEqualSafe(name
, circleName
)) { 
 320                 secnotice("circleCreat", "Expected circle named %@, got %@", circleName
, name
); 
 321                 SOSCreateErrorWithFormat(kSOSErrorNameMismatch
, NULL
, error
, NULL
, 
 322                                          CFSTR("Expected circle named %@, got %@"), circleName
, name
); 
 323                 CFReleaseNull(circle
); 
 326             secnotice("circleCreat", "SOSCircleCreateFromData returned NULL."); 
 332 bool SOSAccountHandleCircleMessage(SOSAccountRef account
, 
 333                                    CFStringRef circleName
, CFDataRef encodedCircleMessage
, CFErrorRef 
*error
) { 
 334     bool success 
= false; 
 335     CFErrorRef localError 
= NULL
; 
 336     SOSCircleRef circle 
= SOSAccountCreateCircleFrom(circleName
, encodedCircleMessage
, &localError
); 
 338         success 
= SOSAccountUpdateCircleFromRemote(account
, circle
, &localError
); 
 339         CFReleaseSafe(circle
); 
 341         secerror("NULL circle found, ignoring ..."); 
 342         success 
= true;  // don't pend this NULL thing. 
 346         if (isSOSErrorCoded(localError
, kSOSErrorIncompatibleCircle
)) { 
 347             secerror("Incompatible circle found, abandoning membership: %@", circleName
); 
 348             CFReleaseNull(account
->my_identity
); 
 349             CFReleaseNull(account
->trusted_circle
); 
 359     CFReleaseNull(localError
); 
 364 bool SOSAccountHandleParametersChange(SOSAccountRef account
, CFDataRef parameters
, CFErrorRef 
*error
){ 
 366     SecKeyRef newKey 
= NULL
; 
 367     CFDataRef newParameters 
= NULL
; 
 368     bool success 
= false; 
 370     if(SOSAccountRetrieveCloudParameters(account
, &newKey
, parameters
, &newParameters
, error
)) { 
 371         debugDumpUserParameters(CFSTR("SOSAccountHandleParametersChange got new user key parameters:"), parameters
); 
 372         secnotice("keygen", "SOSAccountHandleParametersChange got new public key: %@", newKey
); 
 374         if (CFEqualSafe(account
->user_public
, newKey
)) { 
 375             secnotice("updates", "Got same public key sent our way. Ignoring."); 
 377         } else if (CFEqualSafe(account
->previous_public
, newKey
)) { 
 378             secnotice("updates", "Got previous public key repeated. Ignoring."); 
 381             SOSAccountSetUnTrustedUserPublicKey(account
, newKey
); 
 382             SOSAccountSetParameters(account
, newParameters
); 
 385             if(SOSAccountRetryUserCredentials(account
)) { 
 386                 secnotice("keygen", "Successfully used cached password with new parameters"); 
 387                 SOSAccountGenerationSignatureUpdate(account
, error
); 
 389                 SOSAccountPurgePrivateCredential(account
); 
 390                 secnotice("keygen", "Got new parameters for public key - could not find or use cached password"); 
 393             account
->circle_rings_retirements_need_attention 
= true; 
 394             account
->key_interests_need_updating 
= true; 
 400     CFReleaseNull(newKey
); 
 401     CFReleaseNull(newParameters
); 
 406 static inline bool SOSAccountHasLeft(SOSAccountRef account
) { 
 407     switch(account
->departure_code
) { 
 408         case kSOSDiscoveredRetirement
: /* Fallthrough */ 
 409         case kSOSLostPrivateKey
: /* Fallthrough */ 
 410         case kSOSWithdrewMembership
: /* Fallthrough */ 
 411         case kSOSMembershipRevoked
: /* Fallthrough */ 
 412         case kSOSLeftUntrustedCircle
: 
 414         case kSOSNeverAppliedToCircle
: /* Fallthrough */ 
 415         case kSOSNeverLeftCircle
: /* Fallthrough */ 
 421 static const char *concordstring
[] = { 
 422     "kSOSConcordanceTrusted", 
 423     "kSOSConcordanceGenOld",     // kSOSErrorReplay 
 424     "kSOSConcordanceNoUserSig",  // kSOSErrorBadSignature 
 425     "kSOSConcordanceNoUserKey",  // kSOSErrorNoKey 
 426     "kSOSConcordanceNoPeer",     // kSOSErrorPeerNotFound 
 427     "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature 
 428     "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature 
 429     "kSOSConcordanceNoPeerSig", 
 430     "kSOSConcordanceWeSigned", 
 434 bool SOSAccountHandleUpdateCircle(SOSAccountRef account
, SOSCircleRef prospective_circle
, bool writeUpdate
, CFErrorRef 
*error
) 
 437     bool haveOldCircle 
= true; 
 438     const char *local_remote 
= writeUpdate 
? "local": "remote"; 
 440     secnotice("signing", "start:[%s]", local_remote
); 
 441     if (!account
->user_public 
|| !account
->user_public_trusted
) { 
 442         SOSCreateError(kSOSErrorPublicKeyAbsent
, CFSTR("Can't handle updates with no trusted public key here"), NULL
, error
); 
 446     if (!prospective_circle
) { 
 447         secerror("##### Can't update to a NULL circle ######"); 
 448         return false; // Can't update one we don't have. 
 451     CFStringRef newCircleName 
= SOSCircleGetName(prospective_circle
); 
 452     SOSCircleRef oldCircle 
= account
->trusted_circle
; 
 453     SOSCircleRef emptyCircle 
= NULL
; 
 455     if(oldCircle 
== NULL
) { 
 456         SOSCreateErrorWithFormat(kSOSErrorIncompatibleCircle
, NULL
, error
, NULL
, CFSTR("Current Entry is NULL; rejecting %@"), prospective_circle
); 
 457         secerror("##### Can't replace circle - we don't care about it ######"); 
 460     if (CFGetTypeID(oldCircle
) != SOSCircleGetTypeID()) { 
 461         secdebug("signing", ">>>>>>>>>>>>>>>  Non-Circle Circle found <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); 
 462         // We don't know what is in our table, likely it was kCFNull indicating we didn't 
 463         // understand a circle that came by. We seem to like this one lets make our entry be empty circle 
 464         emptyCircle 
= SOSCircleCreate(kCFAllocatorDefault
, newCircleName
, NULL
); 
 465         oldCircle 
= emptyCircle
; 
 466         haveOldCircle 
= false; 
 467         // And we're paranoid, drop our old peer info if for some reason we didn't before. 
 468         // SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL); 
 472     SOSTransportCircleRef transport 
= account
->circle_transport
; 
 474     SOSAccountScanForRetired(account
, prospective_circle
, error
); 
 475     SOSCircleRef newCircle 
= SOSAccountCloneCircleWithRetirement(account
, prospective_circle
, error
); 
 476     if(!newCircle
) return false; 
 478     SOSCircleRef ghostCleaned 
= SOSAccountCloneCircleWithoutMyGhosts(account
, newCircle
); 
 480         CFRetainAssign(newCircle
, ghostCleaned
); 
 484     SOSFullPeerInfoRef me_full 
= account
->my_identity
; 
 485     SOSPeerInfoRef     me 
= SOSFullPeerInfoGetPeerInfo(me_full
); 
 486     CFStringRef        myPeerID 
= SOSPeerInfoGetPeerID(me
); 
 487     myPeerID 
= (myPeerID
) ? myPeerID
: CFSTR("No Peer"); 
 489     if (me 
&& SOSCircleUpdatePeerInfo(newCircle
, me
)) { 
 490         writeUpdate 
= true; // If we update our peer in the new circle we should write it if we accept it. 
 501     static const char *actionstring
[] = { 
 502         "accept", "countersign", "leave", "revert", "ignore", 
 505     circle_action_t circle_action 
= ignore
; 
 506     enum DepartureReason leave_reason 
= kSOSNeverLeftCircle
; 
 508     SecKeyRef old_circle_key 
= NULL
; 
 509     if(SOSCircleVerify(oldCircle
, account
->user_public
, NULL
)) old_circle_key 
= account
->user_public
; 
 510     else if(account
->previous_public 
&& SOSCircleVerify(oldCircle
, account
->previous_public
, NULL
)) old_circle_key 
= account
->previous_public
; 
 511     bool userTrustedOldCircle 
= (old_circle_key 
!= NULL
) && haveOldCircle
; 
 513     SOSConcordanceStatus concstat 
= 
 514     SOSCircleConcordanceTrust(oldCircle
, newCircle
, 
 515                               old_circle_key
, account
->user_public
, 
 518     CFStringRef concStr 
= NULL
; 
 520         case kSOSConcordanceTrusted
: 
 521             circle_action 
= countersign
; 
 522             concStr 
= CFSTR("Trusted"); 
 524         case kSOSConcordanceGenOld
: 
 525             circle_action 
= userTrustedOldCircle 
? revert 
: ignore
; 
 526             concStr 
= CFSTR("Generation Old"); 
 528         case kSOSConcordanceBadUserSig
: 
 529         case kSOSConcordanceBadPeerSig
: 
 530             circle_action 
= userTrustedOldCircle 
? revert 
: accept
; 
 531             concStr 
= CFSTR("Bad Signature"); 
 533         case kSOSConcordanceNoUserSig
: 
 534             circle_action 
= userTrustedOldCircle 
? revert 
: accept
; 
 535             concStr 
= CFSTR("No User Signature"); 
 537         case kSOSConcordanceNoPeerSig
: 
 538             circle_action 
= accept
; // We might like this one eventually but don't countersign. 
 539             concStr 
= CFSTR("No trusted peer signature"); 
 540             secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later"); 
 542         case kSOSConcordanceNoPeer
: 
 543             circle_action 
= leave
; 
 544             leave_reason 
= kSOSLeftUntrustedCircle
; 
 545             concStr 
= CFSTR("No trusted peer left"); 
 547         case kSOSConcordanceNoUserKey
: 
 548             secerror("##### No User Public Key Available, this shouldn't ever happen!!!"); 
 552             secerror("##### Bad Error Return from ConcordanceTrust"); 
 557     secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.  My PeerID is %@", actionstring
[circle_action
], concordstring
[concstat
], userTrustedOldCircle 
? "trusted" : "untrusted", myPeerID
); 
 559     SOSCircleRef circleToPush 
= NULL
; 
 561     if (circle_action 
== leave
) { 
 562         circle_action 
= ignore
; (void) circle_action
; // Acknowledge this is a dead store. 
 564         if (me 
&& SOSCircleHasPeer(oldCircle
, me
, NULL
)) { 
 565             secnotice("account", "Leaving circle with peer %@", me
); 
 566             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 567             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 568             debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
); 
 569             secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@", 
 570                       account
->user_public
, account
->previous_public
, old_circle_key
); 
 572             if (sosAccountLeaveCircle(account
, newCircle
, error
)) { 
 573                 secnotice("leaveCircle", "Leaving circle by newcircle state"); 
 574                 circleToPush 
= newCircle
; 
 576                 secnotice("signing", "Can't leave circle, but dumping identities"); 
 579             account
->departure_code 
= leave_reason
; 
 580             circle_action 
= accept
; 
 584             // We are not in this circle, but we need to update account with it, since we got it from cloud 
 585             secnotice("signing", "We are not in this circle, but we need to update account with it"); 
 586             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 587             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 588             debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
); 
 589             circle_action 
= accept
; 
 593     if (circle_action 
== countersign
) { 
 594         if (me 
&& SOSCircleHasPeer(newCircle
, me
, NULL
)) { 
 595             if (SOSCircleVerifyPeerSigned(newCircle
, me
, NULL
)) { 
 596                 secnotice("signing", "Already concur with the new circle"); 
 598                 CFErrorRef signing_error 
= NULL
; 
 600                 if (me_full 
&& SOSCircleConcordanceSign(newCircle
, me_full
, &signing_error
)) { 
 601                     circleToPush 
= newCircle
; 
 602                     secnotice("signing", "Concurred with new circle"); 
 604                     secerror("Failed to concurrence sign, error: %@", signing_error
); 
 607                 CFReleaseSafe(signing_error
); 
 610             if(SOSAccountVerifyAndAcceptHSAApplicants(account
, newCircle
, error
)) { 
 611                 circleToPush 
= newCircle
; 
 615             secnotice("signing", "Not countersigning, not in new circle"); 
 616             debugDumpCircle(CFSTR("circle to countersign"), newCircle
); 
 618         circle_action 
= accept
; 
 621     if (circle_action 
== accept
) { 
 622         if (me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
) && !SOSCircleHasPeer(newCircle
, me
, NULL
)) { 
 623             //  Don't destroy evidence of other code determining reason for leaving. 
 624             if(!SOSAccountHasLeft(account
)) account
->departure_code 
= kSOSMembershipRevoked
; 
 625             secnotice("account", "Member of old circle but not of new circle"); 
 626             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 627             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 631             && SOSCircleHasActivePeer(oldCircle
, me
, NULL
) 
 632             && !(SOSCircleCountPeers(oldCircle
) == 1 && SOSCircleHasPeer(oldCircle
, me
, NULL
)) // If it was our offering, don't change ID to avoid ghosts 
 633             && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) { 
 634             secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 635             if (account
->my_identity
) 
 636                 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
); 
 637             CFReleaseNull(account
->my_identity
); 
 642         if (me 
&& SOSCircleHasRejectedApplicant(newCircle
, me
, NULL
)) { 
 643             SOSPeerInfoRef  reject 
= SOSCircleCopyRejectedApplicant(newCircle
, me
, NULL
); 
 644             if(CFEqualSafe(reject
, me
) && SOSPeerInfoApplicationVerify(me
, account
->user_public
, NULL
)) { 
 645                 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 646                 debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 647                 debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 648                 if (account
->my_identity
) 
 649                     SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
); 
 650                 CFReleaseNull(account
->my_identity
); 
 654                 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 655                 debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 656                 debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 657                 SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
); 
 662         CFRetainSafe(oldCircle
); 
 663         CFRetainAssign(account
->trusted_circle
, newCircle
); 
 664         SOSAccountSetPreviousPublic(account
); 
 666         secnotice("signing", "%@, Accepting new circle", concStr
); 
 668         if (me 
&& account
->user_public_trusted
 
 669             && SOSCircleHasApplicant(oldCircle
, me
, NULL
) 
 670             && SOSCircleCountPeers(newCircle
) > 0 
 671             && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) { 
 672             // We weren't rejected (above would have set me to NULL. 
 673             // We were applying and we weren't accepted. 
 674             // Our application is declared lost, let us reapply. 
 676             secnotice("signing", "requesting readmission to new circle"); 
 677             if (SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
)) 
 681         if (me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) { 
 682             SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
); 
 685         SOSAccountNotifyOfChange(account
, oldCircle
, newCircle
); 
 687         CFReleaseNull(oldCircle
); 
 690             circleToPush 
= newCircle
; 
 691         account
->key_interests_need_updating 
= true; 
 695      * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles 
 696      * and pushing our current view of the circle (oldCircle).  We'll only do this if we actually 
 697      * are a member of oldCircle - never for an empty circle. 
 700     if (circle_action 
== revert
) { 
 701         if(haveOldCircle 
&& me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) { 
 702             secnotice("signing", "%@, Rejecting new circle, re-publishing old circle", concStr
); 
 703             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 704             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 705             circleToPush 
= oldCircle
; 
 707             secnotice("canary", "%@, Rejecting: new circle Have no old circle - would reset", concStr
); 
 712     if (circleToPush 
!= NULL
) { 
 713         secnotice("signing", "Pushing:[%s]", local_remote
); 
 714         CFDataRef circle_data 
= SOSCircleCopyEncodedData(circleToPush
, kCFAllocatorDefault
, error
); 
 717             // Ensure we flush changes 
 718             account
->circle_rings_retirements_need_attention 
= true; 
 720             //recording circle we are pushing in KVS 
 721             success 
&= SOSTransportCircleRecordLastCirclePushedInKVS(transport
, SOSCircleGetName(circleToPush
), circle_data
); 
 722             //posting new circle to peers 
 723             success 
&= SOSTransportCirclePostCircle(transport
, SOSCircleGetName(circleToPush
), circle_data
, error
); 
 727         CFReleaseNull(circle_data
); 
 730     CFReleaseSafe(newCircle
); 
 731     CFReleaseNull(emptyCircle
);