6 #include "SOSAccountPriv.h" 
   7 #include <Security/SecureObjectSync/SOSAccountHSAJoin.h> 
   8 #include <Security/SecureObjectSync/SOSTransportCircle.h> 
   9 #include <Security/SecureObjectSync/SOSTransport.h> 
  10 #include <Security/SecureObjectSync/SOSViews.h> 
  11 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h> 
  12 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h> 
  13 #include <Security/SecureObjectSync/SOSPeerInfoV2.h> 
  14 #include <Security/SecureObjectSync/SOSPeerInfoDER.h> 
  15 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h> 
  17 static void DifferenceAndCall(CFSetRef old_members
, CFSetRef new_members
, void (^updatedCircle
)(CFSetRef additions
, CFSetRef removals
)) 
  19     CFMutableSetRef additions 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, new_members
); 
  20     CFMutableSetRef removals 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, old_members
); 
  23     CFSetForEach(old_members
, ^(const void * value
) { 
  24         CFSetRemoveValue(additions
, value
); 
  27     CFSetForEach(new_members
, ^(const void * value
) { 
  28         CFSetRemoveValue(removals
, value
); 
  31     updatedCircle(additions
, removals
); 
  33     CFReleaseSafe(additions
); 
  34     CFReleaseSafe(removals
); 
  37 static CFMutableSetRef 
SOSAccountCopyIntersectedViews(CFSetRef peerViews
, CFSetRef myViews
) { 
  38     __block CFMutableSetRef views 
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
  39     if (peerViews 
&& myViews
) CFSetForEach(peerViews
, ^(const void *view
) { 
  40         if (CFSetContainsValue(myViews
, view
)) { 
  41             CFSetAddValue(views
, view
); 
  47 static inline bool isSyncing(SOSPeerInfoRef peer
, SecKeyRef upub
) { 
  48     if(!SOSPeerInfoApplicationVerify(peer
, upub
, NULL
)) return false; 
  49     if(SOSPeerInfoIsRetirementTicket(peer
)) return false; 
  53 static bool isBackupSOSRing(SOSRingRef ring
) 
  55     return isSOSRing(ring
) && (kSOSRingBackup 
== SOSRingGetType(ring
)); 
  58 static bool CFSetIntersectionNotEmpty(CFSetRef set1
, CFSetRef set2
) { 
  59     __block 
bool intersectionEmpty 
= true; 
  60     CFSetForEach(set1
, ^(const void *value
) { 
  61         if (CFSetContainsValue(set2
, value
)) { 
  62             intersectionEmpty 
= false; 
  65     return !intersectionEmpty
; 
  69 static void SOSAccountAppendPeerMetasForViewBackups(SOSAccountRef account
, CFSetRef views
, CFMutableArrayRef appendTo
) 
  71     if (account
->trusted_rings 
== NULL 
|| CFDictionaryGetCount(account
->trusted_rings
) == 0) return; 
  73     CFMutableDictionaryRef ringToViewTable 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
  75     CFSetForEach(views
, ^(const void *value
) { 
  76         CFStringRef viewName 
= value
; 
  77         if (isString(viewName
) && !CFEqualSafe(viewName
, kSOSViewKeychainV0
)) { 
  78             CFStringRef ringName 
= SOSBackupCopyRingNameForView(viewName
); 
  80             SOSRingRef ring 
= (SOSRingRef
) CFDictionaryGetValue(account
->trusted_rings
, ringName
); 
  82             if (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
); 
 101     CFSetRef unsynced 
= asSet(SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
), NULL
); 
 103     CFDictionaryForEach(ringToViewTable
, ^(const void *key
, const void *value
) { 
 104         SOSRingRef ring 
= (SOSRingRef
) key
; 
 105         CFSetRef viewNames 
= (CFSetRef
) value
; 
 106         if (isSOSRing(ring
) && isSet(viewNames
)) { 
 107             if (unsynced 
&& CFSetIntersectionNotEmpty(unsynced
, viewNames
)) { 
 108                 secnotice("engine-notify", "Haven't initially synced views, not making backup peer meta: U: %@ R: %@ Vs: %@", unsynced
, SOSRingGetName(ring
), viewNames
); 
 110                 bool meta_added 
= false; 
 111                 CFErrorRef create_error 
= NULL
; 
 112                 SOSBackupSliceKeyBagRef key_bag 
= NULL
; 
 113                 SOSPeerMetaRef newMeta 
= NULL
; 
 115                 CFDataRef ring_payload 
= SOSRingGetPayload(ring
, NULL
); 
 116                 require_quiet(isData(ring_payload
), skip
); 
 118                 key_bag 
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, ring_payload
, &create_error
); 
 119                 require_quiet(key_bag
, skip
); 
 121                 newMeta 
= SOSPeerMetaCreateWithComponents(SOSRingGetName(ring
), viewNames
, ring_payload
); 
 122                 require_quiet(SecAllocationError(newMeta
, &create_error
, CFSTR("Didn't make peer meta for: %@"), ring
), skip
); 
 123                 CFArrayAppendValue(appendTo
, newMeta
); 
 125                 secnotice("engine-notify", "Backup peer meta: R: %@ Vs: %@ VD: %@", SOSRingGetName(ring
), viewNames
, ring_payload
); 
 131                     secerror("Failed to register backup meta from %@ for views %@. Error (%@)", ring
, viewNames
, create_error
); 
 133                 CFReleaseNull(newMeta
); 
 134                 CFReleaseNull(key_bag
); 
 135                 CFReleaseNull(create_error
); 
 140     CFReleaseNull(ringToViewTable
); 
 143 bool SOSAccountSyncingV0(SOSAccountRef account
) { 
 144     __block 
bool syncingV0 
= false; 
 145     SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
 146         if (SOSPeerInfoIsEnabledView(peer
, kSOSViewKeychainV0
)) { 
 154 void SOSAccountNotifyEngines(SOSAccountRef account
) 
 156     SOSPeerInfoRef myPi 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
 157     CFStringRef myPi_id 
= SOSPeerInfoGetPeerID(myPi
); 
 158     CFMutableArrayRef syncing_peer_metas 
= NULL
; 
 159     CFMutableArrayRef zombie_peer_metas 
= NULL
; 
 160     CFErrorRef localError 
= NULL
; 
 161     SOSPeerMetaRef myMeta 
= NULL
; 
 163     if (myPi_id 
&& isSyncing(myPi
, account
->user_public
) && SOSCircleHasPeer(account
->trusted_circle
, myPi
, NULL
)) { 
 164         CFMutableSetRef myViews 
= SOSPeerInfoCopyEnabledViews(myPi
); 
 166         // We add V0 views to everyone if we see a V0 peer, or a peer with the view explicity enabled 
 167         // V2 peers shouldn't be explicity enabling the uber V0 view, though the seeds did. 
 168         __block 
bool addV0Views 
= SOSAccountSyncingV0(account
); 
 170         syncing_peer_metas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 171         zombie_peer_metas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 172         SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) { 
 173             CFMutableArrayRef arrayToAddTo 
= isSyncing(peer
, account
->user_public
) ? syncing_peer_metas 
: zombie_peer_metas
; 
 175             // Compute views each peer is in that we are also in ourselves 
 176             CFMutableSetRef peerEnabledViews 
= SOSPeerInfoCopyEnabledViews(peer
); 
 177             CFMutableSetRef views 
= SOSAccountCopyIntersectedViews(peerEnabledViews
, myViews
); 
 178             CFReleaseNull(peerEnabledViews
); 
 181                 CFSetAddValue(views
, kSOSViewKeychainV0
); 
 184             SOSPeerMetaRef peerMeta 
= SOSPeerMetaCreateWithComponents(SOSPeerInfoGetPeerID(peer
), views
, NULL
); 
 185             CFReleaseNull(views
); 
 187             CFArrayAppendValue(arrayToAddTo
, peerMeta
); 
 188             CFReleaseNull(peerMeta
); 
 191         // We don't make a backup peer for the magic V0 peer, so do it before we munge the set. 
 192         SOSAccountAppendPeerMetasForViewBackups(account
, myViews
, syncing_peer_metas
); 
 194         // If we saw someone else needing V0, we sync V0, too! 
 196             CFSetAddValue(myViews
, kSOSViewKeychainV0
); 
 199         myMeta 
= SOSPeerMetaCreateWithComponents(myPi_id
, myViews
, NULL
); 
 200         CFReleaseSafe(myViews
); 
 203     SOSEngineRef engine 
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
); 
 205         SOSEngineCircleChanged(engine
, myMeta
, syncing_peer_metas
, zombie_peer_metas
); 
 208     CFReleaseNull(myMeta
); 
 209     CFReleaseSafe(localError
); 
 210     CFReleaseNull(syncing_peer_metas
); 
 211     CFReleaseNull(zombie_peer_metas
); 
 214 // murf Upcoming call to View Changes Here 
 215 static void SOSAccountNotifyOfChange(SOSAccountRef account
, SOSCircleRef oldCircle
, SOSCircleRef newCircle
) 
 217     account
->circle_rings_retirements_need_attention 
= true; 
 219     CFMutableSetRef old_members 
= SOSCircleCopyPeers(oldCircle
, kCFAllocatorDefault
); 
 220     CFMutableSetRef new_members 
= SOSCircleCopyPeers(newCircle
, kCFAllocatorDefault
); 
 222     CFMutableSetRef old_applicants 
= SOSCircleCopyApplicants(oldCircle
, kCFAllocatorDefault
); 
 223     CFMutableSetRef new_applicants 
= SOSCircleCopyApplicants(newCircle
, kCFAllocatorDefault
); 
 225     SOSPeerInfoRef me 
= SOSAccountGetMyPeerInfo(account
); 
 226     if(me 
&& CFSetContainsValue(new_members
, me
)) 
 227         SOSAccountSetValue(account
, kSOSEscrowRecord
, kCFNull
, NULL
); //removing the escrow records from the account object 
 229     DifferenceAndCall(old_members
, new_members
, ^(CFSetRef added_members
, CFSetRef removed_members
) { 
 230         DifferenceAndCall(old_applicants
, new_applicants
, ^(CFSetRef added_applicants
, CFSetRef removed_applicants
) { 
 231             CFArrayForEach(account
->change_blocks
, ^(const void * notificationBlock
) { 
 232                 secnotice("updates", "calling change block"); 
 233                 ((SOSAccountCircleMembershipChangeBlock
) notificationBlock
)(newCircle
, added_members
, removed_members
, added_applicants
, removed_applicants
); 
 238     CFReleaseNull(old_applicants
); 
 239     CFReleaseNull(new_applicants
); 
 241     CFReleaseNull(old_members
); 
 242     CFReleaseNull(new_members
); 
 246 CFDictionaryRef 
SOSAccountHandleRetirementMessages(SOSAccountRef account
, CFDictionaryRef circle_retirement_messages
, CFErrorRef 
*error
) { 
 247     CFStringRef circle_name 
= SOSCircleGetName(account
->trusted_circle
); 
 248     CFMutableArrayRef handledRetirementIDs 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 249     // We only handle one circle, look it up: 
 251     require_quiet(account
->trusted_circle
, finish
); // We don't fail, we intentionally handle nothing. 
 252     CFDictionaryRef retirment_dictionary 
= CFDictionaryGetValue(circle_retirement_messages
, circle_name
); 
 254     CFDictionaryForEach(retirment_dictionary
, ^(const void *key
, const void *value
) { 
 256             SOSPeerInfoRef pi 
= SOSPeerInfoCreateFromData(NULL
, error
, (CFDataRef
) value
); 
 257             if(pi 
&& CFEqual(key
, SOSPeerInfoGetPeerID(pi
)) && SOSPeerInfoInspectRetirementTicket(pi
, error
)) { 
 258                 CFSetAddValue(account
->retirees
, pi
); 
 260                 account
->circle_rings_retirements_need_attention 
= true; // Have to handle retirements. 
 262                 CFArrayAppendValue(handledRetirementIDs
, key
); 
 268     // If we are in the retiree list, we somehow got resurrected 
 269     // clearly we took care of proper departure before so leave 
 270     // and delcare that we withdrew this time. 
 271     SOSPeerInfoRef me 
= SOSAccountGetMyPeerInfo(account
); 
 272     if (me 
&& CFSetContainsValue(account
->retirees
, me
)) { 
 273         SOSAccountPurgeIdentity(account
); 
 274         account
->departure_code 
= kSOSDiscoveredRetirement
; 
 279     CFDictionaryRef result 
= (CFArrayGetCount(handledRetirementIDs
) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
) 
 280                                                                           : CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, circle_name
, handledRetirementIDs
, NULL
); 
 282     CFReleaseNull(handledRetirementIDs
); 
 287 static SOSCircleRef 
SOSAccountCreateCircleFrom(CFStringRef circleName
, CFTypeRef value
, CFErrorRef 
*error
) { 
 288     if (value 
&& !isData(value
) && !isNull(value
)) { 
 289         secnotice("circleCreat", "Value provided not appropriate for a circle"); 
 290         CFStringRef description 
= CFCopyTypeIDDescription(CFGetTypeID(value
)); 
 291         SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, NULL
, 
 292                                  CFSTR("Expected data or NULL got %@"), description
); 
 293         CFReleaseSafe(description
); 
 297     SOSCircleRef circle 
= NULL
; 
 298     if (!value 
|| isNull(value
)) { 
 299         secnotice("circleCreat", "No circle found in data: %@", value
); 
 302         circle 
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, error
); 
 304             CFStringRef name 
= SOSCircleGetName(circle
); 
 305             if (!CFEqualSafe(name
, circleName
)) { 
 306                 secnotice("circleCreat", "Expected circle named %@, got %@", circleName
, name
); 
 307                 SOSCreateErrorWithFormat(kSOSErrorNameMismatch
, NULL
, error
, NULL
, 
 308                                          CFSTR("Expected circle named %@, got %@"), circleName
, name
); 
 309                 CFReleaseNull(circle
); 
 312             secnotice("circleCreat", "SOSCircleCreateFromData returned NULL."); 
 318 bool SOSAccountHandleCircleMessage(SOSAccountRef account
, 
 319                                    CFStringRef circleName
, CFDataRef encodedCircleMessage
, CFErrorRef 
*error
) { 
 320     bool success 
= false; 
 321     CFErrorRef localError 
= NULL
; 
 322     SOSCircleRef circle 
= SOSAccountCreateCircleFrom(circleName
, encodedCircleMessage
, &localError
); 
 324         success 
= SOSAccountUpdateCircleFromRemote(account
, circle
, &localError
); 
 325         CFReleaseSafe(circle
); 
 327         secerror("NULL circle found, ignoring ..."); 
 328         success 
= true;  // don't pend this NULL thing. 
 332         if (isSOSErrorCoded(localError
, kSOSErrorIncompatibleCircle
)) { 
 333             secerror("Incompatible circle found, abandoning membership: %@", circleName
); 
 334             CFReleaseNull(account
->my_identity
); 
 335             CFReleaseNull(account
->trusted_circle
); 
 345     CFReleaseNull(localError
); 
 350 bool SOSAccountHandleParametersChange(SOSAccountRef account
, CFDataRef parameters
, CFErrorRef 
*error
){ 
 352     SecKeyRef newKey 
= NULL
; 
 353     CFDataRef newParameters 
= NULL
; 
 354     bool success 
= false; 
 356     if(SOSAccountRetrieveCloudParameters(account
, &newKey
, parameters
, &newParameters
, error
)) { 
 357         if (CFEqualSafe(account
->user_public
, newKey
)) { 
 358             secnotice("updates", "Got same public key sent our way. Ignoring."); 
 360         } else if (CFEqualSafe(account
->previous_public
, newKey
)) { 
 361             secnotice("updates", "Got previous public key repeated. Ignoring."); 
 364             SOSAccountSetUnTrustedUserPublicKey(account
, newKey
); 
 365             SOSAccountSetParameters(account
, newParameters
); 
 368             if(SOSAccountRetryUserCredentials(account
)) { 
 369                 secnotice("keygen", "Successfully used cached password with new parameters: %@", account
->user_public
); 
 370                 SOSAccountGenerationSignatureUpdate(account
, error
); 
 372                 SOSAccountPurgePrivateCredential(account
); 
 373                 secnotice("keygen", "Got new parameters for public key - failed with cached password: %@", account
->user_public
); 
 374                 debugDumpUserParameters(CFSTR("params"), account
->user_key_parameters
); 
 377             account
->circle_rings_retirements_need_attention 
= true; 
 378             SOSUpdateKeyInterest(account
); 
 384     CFReleaseNull(newKey
); 
 385     CFReleaseNull(newParameters
); 
 390 static inline bool SOSAccountHasLeft(SOSAccountRef account
) { 
 391     switch(account
->departure_code
) { 
 392         case kSOSDiscoveredRetirement
: /* Fallthrough */ 
 393         case kSOSLostPrivateKey
: /* Fallthrough */ 
 394         case kSOSWithdrewMembership
: /* Fallthrough */ 
 395         case kSOSMembershipRevoked
: /* Fallthrough */ 
 396         case kSOSLeftUntrustedCircle
: 
 398         case kSOSNeverAppliedToCircle
: /* Fallthrough */ 
 399         case kSOSNeverLeftCircle
: /* Fallthrough */ 
 405 static const char *concordstring
[] = { 
 406     "kSOSConcordanceTrusted", 
 407     "kSOSConcordanceGenOld",     // kSOSErrorReplay 
 408     "kSOSConcordanceNoUserSig",  // kSOSErrorBadSignature 
 409     "kSOSConcordanceNoUserKey",  // kSOSErrorNoKey 
 410     "kSOSConcordanceNoPeer",     // kSOSErrorPeerNotFound 
 411     "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature 
 412     "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature 
 413     "kSOSConcordanceNoPeerSig", 
 414     "kSOSConcordanceWeSigned", 
 417 bool SOSAccountHandleUpdateCircle(SOSAccountRef account
, SOSCircleRef prospective_circle
, bool writeUpdate
, CFErrorRef 
*error
) 
 420     bool haveOldCircle 
= true; 
 421     const char *local_remote 
= writeUpdate 
? "local": "remote"; 
 423     secnotice("signing", "start:[%s] %@", local_remote
, prospective_circle
); 
 424     if (!account
->user_public 
|| !account
->user_public_trusted
) { 
 425         SOSCreateError(kSOSErrorPublicKeyAbsent
, CFSTR("Can't handle updates with no trusted public key here"), NULL
, error
); 
 429     if (!prospective_circle
) { 
 430         secerror("##### Can't update to a NULL circle ######"); 
 431         return false; // Can't update one we don't have. 
 434     CFStringRef newCircleName 
= SOSCircleGetName(prospective_circle
); 
 435     SOSCircleRef oldCircle 
= account
->trusted_circle
; 
 436     SOSCircleRef emptyCircle 
= NULL
; 
 438     if(oldCircle 
== NULL
) { 
 439         SOSCreateErrorWithFormat(kSOSErrorIncompatibleCircle
, NULL
, error
, NULL
, CFSTR("Current Entry is NULL; rejecting %@"), prospective_circle
); 
 440         secerror("##### Can't replace circle - we don't care about %@ ######", prospective_circle
); 
 443     if (CFGetTypeID(oldCircle
) != SOSCircleGetTypeID()) { 
 444         secdebug("signing", ">>>>>>>>>>>>>>>  Non-Circle Circle found <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); 
 445         // We don't know what is in our table, likely it was kCFNull indicating we didn't 
 446         // understand a circle that came by. We seem to like this one lets make our entry be empty circle 
 447         emptyCircle 
= SOSCircleCreate(kCFAllocatorDefault
, newCircleName
, NULL
); 
 448         oldCircle 
= emptyCircle
; 
 449         haveOldCircle 
= false; 
 450         // And we're paranoid, drop our old peer info if for some reason we didn't before. 
 451         // SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL); 
 454     SOSFullPeerInfoRef me_full 
= account
->my_identity
; 
 455     SOSPeerInfoRef     me 
= SOSFullPeerInfoGetPeerInfo(me_full
); 
 457     SOSTransportCircleRef transport 
= account
->circle_transport
; 
 459     SOSAccountScanForRetired(account
, prospective_circle
, error
); 
 460     SOSCircleRef newCircle 
= SOSAccountCloneCircleWithRetirement(account
, prospective_circle
, error
); 
 461     if(!newCircle
) return false; 
 463     if (me 
&& SOSCircleUpdatePeerInfo(newCircle
, me
)) { 
 464         writeUpdate 
= true; // If we update our peer in the new circle we should write it if we accept it. 
 475     static const char *actionstring
[] = { 
 476         "accept", "countersign", "leave", "revert", "ignore", 
 479     circle_action_t circle_action 
= ignore
; 
 480     enum DepartureReason leave_reason 
= kSOSNeverLeftCircle
; 
 482     SecKeyRef old_circle_key 
= NULL
; 
 483     if(SOSCircleVerify(oldCircle
, account
->user_public
, NULL
)) old_circle_key 
= account
->user_public
; 
 484     else if(account
->previous_public 
&& SOSCircleVerify(oldCircle
, account
->previous_public
, NULL
)) old_circle_key 
= account
->previous_public
; 
 485     bool userTrustedOldCircle 
= (old_circle_key 
!= NULL
) && haveOldCircle
; 
 487     SOSConcordanceStatus concstat 
= 
 488     SOSCircleConcordanceTrust(oldCircle
, newCircle
, 
 489                               old_circle_key
, account
->user_public
, 
 492     CFStringRef concStr 
= NULL
; 
 494         case kSOSConcordanceTrusted
: 
 495             circle_action 
= countersign
; 
 496             concStr 
= CFSTR("Trusted"); 
 498         case kSOSConcordanceGenOld
: 
 499             circle_action 
= userTrustedOldCircle 
? revert 
: ignore
; 
 500             concStr 
= CFSTR("Generation Old"); 
 502         case kSOSConcordanceBadUserSig
: 
 503         case kSOSConcordanceBadPeerSig
: 
 504             circle_action 
= userTrustedOldCircle 
? revert 
: accept
; 
 505             concStr 
= CFSTR("Bad Signature"); 
 507         case kSOSConcordanceNoUserSig
: 
 508             circle_action 
= userTrustedOldCircle 
? revert 
: accept
; 
 509             concStr 
= CFSTR("No User Signature"); 
 511         case kSOSConcordanceNoPeerSig
: 
 512             circle_action 
= accept
; // We might like this one eventually but don't countersign. 
 513             concStr 
= CFSTR("No trusted peer signature"); 
 514             secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle
); 
 516         case kSOSConcordanceNoPeer
: 
 517             circle_action 
= leave
; 
 518             leave_reason 
= kSOSLeftUntrustedCircle
; 
 519             concStr 
= CFSTR("No trusted peer left"); 
 521         case kSOSConcordanceNoUserKey
: 
 522             secerror("##### No User Public Key Available, this shouldn't ever happen!!!"); 
 526             secerror("##### Bad Error Return from ConcordanceTrust"); 
 531     secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring
[circle_action
], concordstring
[concstat
], userTrustedOldCircle 
? "trusted" : "untrusted"); 
 533     SOSCircleRef circleToPush 
= NULL
; 
 535     if (circle_action 
== leave
) { 
 536         circle_action 
= ignore
; (void) circle_action
; // Acknowledge this is a dead store. 
 538         if (me 
&& SOSCircleHasPeer(oldCircle
, me
, NULL
)) { 
 539             secnotice("account", "Leaving circle with peer %@", me
); 
 540             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 541             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 542             debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
); 
 543             secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@", 
 544                       account
->user_public
, account
->previous_public
, old_circle_key
); 
 546             if (sosAccountLeaveCircle(account
, newCircle
, error
)) { 
 547                 circleToPush 
= newCircle
; 
 549                 secnotice("signing", "Can't leave circle %@, but dumping identities", oldCircle
); 
 552             account
->departure_code 
= leave_reason
; 
 553             circle_action 
= accept
; 
 557             // We are not in this circle, but we need to update account with it, since we got it from cloud 
 558             secnotice("signing", "We are not in this circle, but we need to update account with it"); 
 559             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 560             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 561             debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
); 
 562             circle_action 
= accept
; 
 566     if (circle_action 
== countersign
) { 
 567         if (me 
&& SOSCircleHasPeer(newCircle
, me
, NULL
)) { 
 568             if (SOSCircleVerifyPeerSigned(newCircle
, me
, NULL
)) { 
 569                 secnotice("signing", "Already concur with: %@", newCircle
); 
 571                 CFErrorRef signing_error 
= NULL
; 
 573                 if (me_full 
&& SOSCircleConcordanceSign(newCircle
, me_full
, &signing_error
)) { 
 574                     circleToPush 
= newCircle
; 
 575                     secnotice("signing", "Concurred with: %@", newCircle
); 
 577                     secerror("Failed to concurrence sign, error: %@  Old: %@ New: %@", signing_error
, oldCircle
, newCircle
); 
 580                 CFReleaseSafe(signing_error
); 
 583             if(SOSAccountVerifyAndAcceptHSAApplicants(account
, newCircle
, error
)) { 
 584                 circleToPush 
= newCircle
; 
 588             secnotice("signing", "Not countersigning, not in circle: %@", newCircle
); 
 589             debugDumpCircle(CFSTR("circle to countersign"), newCircle
); 
 591         circle_action 
= accept
; 
 594     if (circle_action 
== accept
) { 
 595         if (me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
) && !SOSCircleHasPeer(newCircle
, me
, NULL
)) { 
 596             //  Don't destroy evidence of other code determining reason for leaving. 
 597             if(!SOSAccountHasLeft(account
)) account
->departure_code 
= kSOSMembershipRevoked
; 
 598             secnotice("account", "Member of old circle but not of new circle"); 
 599             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 600             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 604             && SOSCircleHasActivePeer(oldCircle
, me
, NULL
) 
 605             && !(SOSCircleCountPeers(oldCircle
) == 1 && SOSCircleHasPeer(oldCircle
, me
, NULL
)) // If it was our offering, don't change ID to avoid ghosts 
 606             && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) { 
 607             secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 608             if (account
->my_identity
) 
 609                 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
); 
 610             CFReleaseNull(account
->my_identity
); 
 615         if (me 
&& SOSCircleHasRejectedApplicant(newCircle
, me
, NULL
)) { 
 616             SOSPeerInfoRef  reject 
= SOSCircleCopyRejectedApplicant(newCircle
, me
, NULL
); 
 617             if(CFEqualSafe(reject
, me
) && SOSPeerInfoApplicationVerify(me
, account
->user_public
, NULL
)) { 
 618                 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 619                 debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 620                 debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 621                 if (account
->my_identity
) 
 622                     SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
); 
 623                 CFReleaseNull(account
->my_identity
); 
 627                 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
)); 
 628                 debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 629                 debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 630                 SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
); 
 635         CFRetainSafe(oldCircle
); 
 636         CFRetainAssign(account
->trusted_circle
, newCircle
); 
 637         SOSAccountSetPreviousPublic(account
); 
 639         secnotice("signing", "%@, Accepting circle: %@", concStr
, newCircle
); 
 641         if (me 
&& account
->user_public_trusted
 
 642             && SOSCircleHasApplicant(oldCircle
, me
, NULL
) 
 643             && SOSCircleCountPeers(newCircle
) > 0 
 644             && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) { 
 645             // We weren't rejected (above would have set me to NULL. 
 646             // We were applying and we weren't accepted. 
 647             // Our application is declared lost, let us reapply. 
 649             secnotice("signing", "requesting readmission to circle %@", newCircle
); 
 650             if (SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
)) 
 654         if (me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) { 
 655             SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
); 
 658         SOSAccountNotifyOfChange(account
, oldCircle
, newCircle
); 
 660         CFReleaseNull(oldCircle
); 
 663             circleToPush 
= newCircle
; 
 664         SOSUpdateKeyInterest(account
); 
 668      * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles 
 669      * and pushing our current view of the circle (oldCircle).  We'll only do this if we actually 
 670      * are a member of oldCircle - never for an empty circle. 
 673     if (circle_action 
== revert
) { 
 674         if(haveOldCircle 
&& me 
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) { 
 675             secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr
, newCircle
, oldCircle
); 
 676             debugDumpCircle(CFSTR("oldCircle"), oldCircle
); 
 677             debugDumpCircle(CFSTR("newCircle"), newCircle
); 
 678             circleToPush 
= oldCircle
; 
 680             secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr
, newCircle
); 
 685     if (circleToPush 
!= NULL
) { 
 686         secnotice("signing", "Pushing:[%s] %@", local_remote
, circleToPush
); 
 687         CFDataRef circle_data 
= SOSCircleCopyEncodedData(circleToPush
, kCFAllocatorDefault
, error
); 
 690             //recording circle we are pushing in KVS 
 691             success 
&= SOSTransportCircleRecordLastCirclePushedInKVS(transport
, SOSCircleGetName(circleToPush
), circle_data
); 
 692             //posting new circle to peers 
 693             success 
&= SOSTransportCirclePostCircle(transport
, SOSCircleGetName(circleToPush
), circle_data
, error
); 
 697         CFReleaseNull(circle_data
); 
 700     CFReleaseSafe(newCircle
); 
 701     CFReleaseNull(emptyCircle
);