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
);
192 // We don't make a backup peer for the magic V0 peer, so do it before we munge the set.
193 SOSAccountAppendPeerMetasForViewBackups(account
, myViews
, syncing_peer_metas
);
196 // If we saw someone else needing V0, we sync V0, too!
198 CFSetAddValue(myViews
, kSOSViewKeychainV0
);
201 myMeta
= SOSPeerMetaCreateWithComponents(myPi_id
, myViews
, NULL
);
202 CFReleaseSafe(myViews
);
205 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
207 SOSEngineCircleChanged(engine
, myMeta
, syncing_peer_metas
, zombie_peer_metas
);
210 CFReleaseNull(myMeta
);
211 CFReleaseSafe(localError
);
212 CFReleaseNull(syncing_peer_metas
);
213 CFReleaseNull(zombie_peer_metas
);
216 // murf Upcoming call to View Changes Here
217 static void SOSAccountNotifyOfChange(SOSAccountRef account
, SOSCircleRef oldCircle
, SOSCircleRef newCircle
)
219 account
->circle_rings_retirements_need_attention
= true;
221 CFMutableSetRef old_members
= SOSCircleCopyPeers(oldCircle
, kCFAllocatorDefault
);
222 CFMutableSetRef new_members
= SOSCircleCopyPeers(newCircle
, kCFAllocatorDefault
);
224 CFMutableSetRef old_applicants
= SOSCircleCopyApplicants(oldCircle
, kCFAllocatorDefault
);
225 CFMutableSetRef new_applicants
= SOSCircleCopyApplicants(newCircle
, kCFAllocatorDefault
);
227 DifferenceAndCall(old_members
, new_members
, ^(CFSetRef added_members
, CFSetRef removed_members
) {
228 DifferenceAndCall(old_applicants
, new_applicants
, ^(CFSetRef added_applicants
, CFSetRef removed_applicants
) {
229 CFArrayForEach(account
->change_blocks
, ^(const void * notificationBlock
) {
230 secnotice("updates", "calling change block");
231 ((SOSAccountCircleMembershipChangeBlock
) notificationBlock
)(newCircle
, added_members
, removed_members
, added_applicants
, removed_applicants
);
236 CFReleaseNull(old_applicants
);
237 CFReleaseNull(new_applicants
);
239 CFReleaseNull(old_members
);
240 CFReleaseNull(new_members
);
243 static void SOSAccountRecordRetiredPeerInCircle(SOSAccountRef account
, SOSPeerInfoRef retiree
)
245 // Replace Peer with RetiredPeer, if were a peer.
246 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) {
247 bool updated
= SOSCircleUpdatePeerInfo(circle
, retiree
);
249 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
);
250 CFErrorRef cleanupError
= NULL
;
251 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
))
252 secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
);
253 CFReleaseSafe(cleanupError
);
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 retirment_dictionary
= CFDictionaryGetValue(circle_retirement_messages
, circle_name
);
268 CFDictionaryForEach(retirment_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 SOSAccountRecordRetiredPeerInCircle(account
, pi
);
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 if (CFEqualSafe(account
->user_public
, newKey
)) {
372 secnotice("updates", "Got same public key sent our way. Ignoring.");
374 } else if (CFEqualSafe(account
->previous_public
, newKey
)) {
375 secnotice("updates", "Got previous public key repeated. Ignoring.");
378 SOSAccountSetUnTrustedUserPublicKey(account
, newKey
);
379 CFReleaseNull(account
->user_key_parameters
);
380 account
->user_key_parameters
= newParameters
;
382 newParameters
= NULL
;
384 if(SOSAccountRetryUserCredentials(account
)) {
385 secnotice("keygen", "Successfully used cached password with new parameters: %@", account
->user_public
);
386 SOSAccountGenerationSignatureUpdate(account
, error
);
388 SOSAccountPurgePrivateCredential(account
);
389 secnotice("keygen", "Got new parameters for public key - failed with cached password: %@", account
->user_public
);
390 debugDumpUserParameters(CFSTR("params"), account
->user_key_parameters
);
393 SOSUpdateKeyInterest();
399 CFReleaseNull(newKey
);
400 CFReleaseNull(newParameters
);
405 static inline bool SOSAccountHasLeft(SOSAccountRef account
) {
406 switch(account
->departure_code
) {
407 case kSOSDiscoveredRetirement
: /* Fallthrough */
408 case kSOSLostPrivateKey
: /* Fallthrough */
409 case kSOSWithdrewMembership
: /* Fallthrough */
410 case kSOSMembershipRevoked
: /* Fallthrough */
411 case kSOSLeftUntrustedCircle
:
413 case kSOSNeverAppliedToCircle
: /* Fallthrough */
414 case kSOSNeverLeftCircle
: /* Fallthrough */
420 static const char *concordstring
[] = {
421 "kSOSConcordanceTrusted",
422 "kSOSConcordanceGenOld", // kSOSErrorReplay
423 "kSOSConcordanceNoUserSig", // kSOSErrorBadSignature
424 "kSOSConcordanceNoUserKey", // kSOSErrorNoKey
425 "kSOSConcordanceNoPeer", // kSOSErrorPeerNotFound
426 "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature
427 "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature
428 "kSOSConcordanceNoPeerSig",
429 "kSOSConcordanceWeSigned",
432 bool SOSAccountHandleUpdateCircle(SOSAccountRef account
, SOSCircleRef prospective_circle
, bool writeUpdate
, CFErrorRef
*error
)
435 bool haveOldCircle
= true;
436 const char *local_remote
= writeUpdate
? "local": "remote";
438 secnotice("signing", "start:[%s] %@", local_remote
, prospective_circle
);
439 if (!account
->user_public
|| !account
->user_public_trusted
) {
440 SOSCreateError(kSOSErrorPublicKeyAbsent
, CFSTR("Can't handle updates with no trusted public key here"), NULL
, error
);
444 if (!prospective_circle
) {
445 secerror("##### Can't update to a NULL circle ######");
446 return false; // Can't update one we don't have.
449 CFStringRef newCircleName
= SOSCircleGetName(prospective_circle
);
450 SOSCircleRef oldCircle
= account
->trusted_circle
;
451 SOSCircleRef emptyCircle
= NULL
;
453 if(oldCircle
== NULL
) {
454 SOSCreateErrorWithFormat(kSOSErrorIncompatibleCircle
, NULL
, error
, NULL
, CFSTR("Current Entry is NULL; rejecting %@"), prospective_circle
);
455 secerror("##### Can't replace circle - we don't care about %@ ######", prospective_circle
);
458 if (CFGetTypeID(oldCircle
) != SOSCircleGetTypeID()) {
459 secdebug("signing", ">>>>>>>>>>>>>>> Non-Circle Circle found <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
460 // We don't know what is in our table, likely it was kCFNull indicating we didn't
461 // understand a circle that came by. We seem to like this one lets make our entry be empty circle
462 emptyCircle
= SOSCircleCreate(kCFAllocatorDefault
, newCircleName
, NULL
);
463 oldCircle
= emptyCircle
;
464 haveOldCircle
= false;
465 // And we're paranoid, drop our old peer info if for some reason we didn't before.
466 // SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
469 SOSFullPeerInfoRef me_full
= account
->my_identity
;
470 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(me_full
);
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 if (me
&& SOSCircleUpdatePeerInfo(newCircle
, me
)) {
479 writeUpdate
= true; // If we update our peer in the new circle we should write it if we accept it.
490 static const char *actionstring
[] = {
491 "accept", "countersign", "leave", "revert", "ignore",
494 circle_action_t circle_action
= ignore
;
495 enum DepartureReason leave_reason
= kSOSNeverLeftCircle
;
497 SecKeyRef old_circle_key
= NULL
;
498 if(SOSCircleVerify(oldCircle
, account
->user_public
, NULL
)) old_circle_key
= account
->user_public
;
499 else if(account
->previous_public
&& SOSCircleVerify(oldCircle
, account
->previous_public
, NULL
)) old_circle_key
= account
->previous_public
;
500 bool userTrustedOldCircle
= (old_circle_key
!= NULL
) && haveOldCircle
;
502 SOSConcordanceStatus concstat
=
503 SOSCircleConcordanceTrust(oldCircle
, newCircle
,
504 old_circle_key
, account
->user_public
,
507 CFStringRef concStr
= NULL
;
509 case kSOSConcordanceTrusted
:
510 circle_action
= countersign
;
511 concStr
= CFSTR("Trusted");
513 case kSOSConcordanceGenOld
:
514 circle_action
= userTrustedOldCircle
? revert
: ignore
;
515 concStr
= CFSTR("Generation Old");
517 case kSOSConcordanceBadUserSig
:
518 case kSOSConcordanceBadPeerSig
:
519 circle_action
= userTrustedOldCircle
? revert
: accept
;
520 concStr
= CFSTR("Bad Signature");
522 case kSOSConcordanceNoUserSig
:
523 circle_action
= userTrustedOldCircle
? revert
: accept
;
524 concStr
= CFSTR("No User Signature");
526 case kSOSConcordanceNoPeerSig
:
527 circle_action
= accept
; // We might like this one eventually but don't countersign.
528 concStr
= CFSTR("No trusted peer signature");
529 secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle
);
531 case kSOSConcordanceNoPeer
:
532 circle_action
= leave
;
533 leave_reason
= kSOSLeftUntrustedCircle
;
534 concStr
= CFSTR("No trusted peer left");
536 case kSOSConcordanceNoUserKey
:
537 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
541 secerror("##### Bad Error Return from ConcordanceTrust");
546 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring
[circle_action
], concordstring
[concstat
], userTrustedOldCircle
? "trusted" : "untrusted");
548 SOSCircleRef circleToPush
= NULL
;
550 if (circle_action
== leave
) {
551 circle_action
= ignore
; (void) circle_action
; // Acknowledge this is a dead store.
553 if (me
&& SOSCircleHasPeer(oldCircle
, me
, NULL
)) {
554 secnotice("account", "Leaving circle with peer %@", me
);
555 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
556 debugDumpCircle(CFSTR("newCircle"), newCircle
);
557 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
);
558 secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@",
559 account
->user_public
, account
->previous_public
, old_circle_key
);
561 if (sosAccountLeaveCircle(account
, newCircle
, error
)) {
562 circleToPush
= newCircle
;
564 secnotice("signing", "Can't leave circle %@, but dumping identities", oldCircle
);
567 account
->departure_code
= leave_reason
;
568 circle_action
= accept
;
572 // We are not in this circle, but we need to update account with it, since we got it from cloud
573 secnotice("signing", "We are not in this circle, but we need to update account with it");
574 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
575 debugDumpCircle(CFSTR("newCircle"), newCircle
);
576 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
);
577 circle_action
= accept
;
581 if (circle_action
== countersign
) {
582 if (me
&& SOSCircleHasPeer(newCircle
, me
, NULL
)) {
583 if (SOSCircleVerifyPeerSigned(newCircle
, me
, NULL
)) {
584 secnotice("signing", "Already concur with: %@", newCircle
);
586 CFErrorRef signing_error
= NULL
;
588 if (me_full
&& SOSCircleConcordanceSign(newCircle
, me_full
, &signing_error
)) {
589 circleToPush
= newCircle
;
590 secnotice("signing", "Concurred with: %@", newCircle
);
592 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error
, oldCircle
, newCircle
);
595 CFReleaseSafe(signing_error
);
598 if(SOSAccountVerifyAndAcceptHSAApplicants(account
, newCircle
, error
)) {
599 circleToPush
= newCircle
;
603 secnotice("signing", "Not countersigning, not in circle: %@", newCircle
);
604 debugDumpCircle(CFSTR("circle to countersign"), newCircle
);
606 circle_action
= accept
;
609 if (circle_action
== accept
) {
610 if (me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
) && !SOSCircleHasPeer(newCircle
, me
, NULL
)) {
611 // Don't destroy evidence of other code determining reason for leaving.
612 if(!SOSAccountHasLeft(account
)) account
->departure_code
= kSOSMembershipRevoked
;
613 secnotice("account", "Member of old circle but not of new circle");
614 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
615 debugDumpCircle(CFSTR("newCircle"), newCircle
);
619 && SOSCircleHasActivePeer(oldCircle
, me
, NULL
)
620 && !(SOSCircleCountPeers(oldCircle
) == 1 && SOSCircleHasPeer(oldCircle
, me
, NULL
)) // If it was our offering, don't change ID to avoid ghosts
621 && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) {
622 secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
623 if (account
->my_identity
)
624 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
);
625 CFReleaseNull(account
->my_identity
);
630 if (me
&& SOSCircleHasRejectedApplicant(newCircle
, me
, NULL
)) {
631 SOSPeerInfoRef reject
= SOSCircleCopyRejectedApplicant(newCircle
, me
, NULL
);
632 if(CFEqualSafe(reject
, me
) && SOSPeerInfoApplicationVerify(me
, account
->user_public
, NULL
)) {
633 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
634 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
635 debugDumpCircle(CFSTR("newCircle"), newCircle
);
636 if (account
->my_identity
)
637 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
);
638 CFReleaseNull(account
->my_identity
);
642 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
643 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
644 debugDumpCircle(CFSTR("newCircle"), newCircle
);
645 SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
);
650 CFRetainSafe(oldCircle
);
651 CFRetainAssign(account
->trusted_circle
, newCircle
);
652 SOSAccountSetPreviousPublic(account
);
654 secnotice("signing", "%@, Accepting circle: %@", concStr
, newCircle
);
656 if (me
&& account
->user_public_trusted
657 && SOSCircleHasApplicant(oldCircle
, me
, NULL
)
658 && SOSCircleCountPeers(newCircle
) > 0
659 && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) {
660 // We weren't rejected (above would have set me to NULL.
661 // We were applying and we weren't accepted.
662 // Our application is declared lost, let us reapply.
664 secnotice("signing", "requesting readmission to circle %@", newCircle
);
665 if (SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
))
669 if (me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) {
670 SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
);
673 SOSAccountNotifyOfChange(account
, oldCircle
, newCircle
);
675 CFReleaseNull(oldCircle
);
678 circleToPush
= newCircle
;
679 SOSUpdateKeyInterest();
683 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles
684 * and pushing our current view of the circle (oldCircle). We'll only do this if we actually
685 * are a member of oldCircle - never for an empty circle.
688 if (circle_action
== revert
) {
689 if(haveOldCircle
&& me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) {
690 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr
, newCircle
, oldCircle
);
691 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
692 debugDumpCircle(CFSTR("newCircle"), newCircle
);
693 circleToPush
= oldCircle
;
695 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr
, newCircle
);
700 if (circleToPush
!= NULL
) {
701 secnotice("signing", "Pushing:[%s] %@", local_remote
, circleToPush
);
702 CFDataRef circle_data
= SOSCircleCopyEncodedData(circleToPush
, kCFAllocatorDefault
, error
);
705 //recording circle we are pushing in KVS
706 success
&= SOSTransportCircleRecordLastCirclePushedInKVS(transport
, SOSCircleGetName(circleToPush
), circle_data
);
707 //posting new circle to peers
708 success
&= SOSTransportCirclePostCircle(transport
, SOSCircleGetName(circleToPush
), circle_data
, error
);
712 CFReleaseNull(circle_data
);
715 CFReleaseSafe(newCircle
);
716 CFReleaseNull(emptyCircle
);