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>
19 static void DifferenceAndCall(CFSetRef old_members
, CFSetRef new_members
, void (^updatedCircle
)(CFSetRef additions
, CFSetRef removals
))
21 CFMutableSetRef additions
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, new_members
);
22 CFMutableSetRef removals
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, old_members
);
25 CFSetForEach(old_members
, ^(const void * value
) {
26 CFSetRemoveValue(additions
, value
);
29 CFSetForEach(new_members
, ^(const void * value
) {
30 CFSetRemoveValue(removals
, value
);
33 updatedCircle(additions
, removals
);
35 CFReleaseSafe(additions
);
36 CFReleaseSafe(removals
);
39 static CFMutableSetRef
SOSAccountCopyIntersectedViews(CFSetRef peerViews
, CFSetRef myViews
) {
40 __block CFMutableSetRef views
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
41 if (peerViews
&& myViews
) CFSetForEach(peerViews
, ^(const void *view
) {
42 if (CFSetContainsValue(myViews
, view
)) {
43 CFSetAddValue(views
, view
);
49 static inline bool isSyncing(SOSPeerInfoRef peer
, SecKeyRef upub
) {
50 if(!SOSPeerInfoApplicationVerify(peer
, upub
, NULL
)) return false;
51 if(SOSPeerInfoIsRetirementTicket(peer
)) return false;
55 static bool isBackupSOSRing(SOSRingRef ring
)
57 return isSOSRing(ring
) && (kSOSRingBackup
== SOSRingGetType(ring
));
60 static void SOSAccountAppendPeerMetasForViewBackups(SOSAccountRef account
, CFSetRef views
, CFMutableArrayRef appendTo
)
62 CFMutableDictionaryRef ringToViewTable
= NULL
;
64 require_quiet(SOSAccountIsInCircle(account
, NULL
), done
);
66 require_action_quiet(SOSAccountHasCompletedRequiredBackupSync(account
), done
,
67 secnotice("backup", "Haven't finished initial backup syncing, not registering backup metas with engine"));
69 require_action_quiet(SOSPeerInfoV2DictionaryHasData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
), done
,
70 secnotice("backup", "No key to backup to, we don't enable individual view backups"));
72 ringToViewTable
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
74 CFSetForEach(views
, ^(const void *value
) {
75 CFStringRef viewName
= value
;
76 if (isString(viewName
) && !CFEqualSafe(viewName
, kSOSViewKeychainV0
)) {
77 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
79 SOSRingRef ring
= SOSAccountCopyRing(account
, ringName
, NULL
);
80 if (ring
&& isBackupSOSRing(ring
)) {
81 CFTypeRef currentValue
= (CFTypeRef
) CFDictionaryGetValue(ringToViewTable
, ring
);
83 if (isSet(currentValue
)) {
84 CFSetAddValue((CFMutableSetRef
)currentValue
, viewName
);
86 CFMutableSetRef viewNameSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
87 CFSetAddValue(viewNameSet
, viewName
);
89 CFDictionarySetValue(ringToViewTable
, ring
, viewNameSet
);
90 CFReleaseNull(viewNameSet
);
93 secwarning("View '%@' not being backed up – ring %@:%@ not backup ring.", viewName
, ringName
, ring
);
95 CFReleaseNull(ringName
);
100 CFDictionaryForEach(ringToViewTable
, ^(const void *key
, const void *value
) {
101 SOSRingRef ring
= (SOSRingRef
) key
;
102 CFSetRef viewNames
= asSet(value
, NULL
);
103 if (isSOSRing(ring
) && viewNames
) {
104 if (SOSAccountIntersectsWithOutstanding(account
, viewNames
)) {
105 CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) {
106 secnotice("engine-notify", "Not ready, no peer meta: R: %@ Vs: %@", SOSRingGetName(ring
), ringViews
);
109 bool meta_added
= false;
110 CFErrorRef create_error
= NULL
;
111 SOSBackupSliceKeyBagRef key_bag
= NULL
;
112 SOSPeerMetaRef newMeta
= NULL
;
114 CFDataRef ring_payload
= SOSRingGetPayload(ring
, NULL
);
115 require_quiet(isData(ring_payload
), skip
);
117 key_bag
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, ring_payload
, &create_error
);
118 require_quiet(key_bag
, skip
);
120 newMeta
= SOSPeerMetaCreateWithComponents(SOSRingGetName(ring
), viewNames
, ring_payload
);
121 require_quiet(SecAllocationError(newMeta
, &create_error
, CFSTR("Didn't make peer meta for: %@"), ring
), skip
);
122 CFArrayAppendValue(appendTo
, newMeta
);
124 CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) {
125 secnotice("engine-notify", "Backup peer meta: R: %@ Vs: %@ VD: %@", SOSRingGetName(ring
), ringViews
, ring_payload
);
132 CFStringSetPerformWithDescription(viewNames
, ^(CFStringRef ringViews
) {
133 secerror("Failed to register backup meta from %@ for views %@. Error (%@)", ring
, ringViews
, create_error
);
136 CFReleaseNull(newMeta
);
137 CFReleaseNull(key_bag
);
138 CFReleaseNull(create_error
);
144 CFReleaseNull(ringToViewTable
);
147 bool SOSAccountSyncingV0(SOSAccountRef account
) {
148 __block
bool syncingV0
= false;
149 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
150 if (SOSPeerInfoIsEnabledView(peer
, kSOSViewKeychainV0
)) {
158 void SOSAccountNotifyEngines(SOSAccountRef account
)
160 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
161 CFStringRef myPi_id
= SOSPeerInfoGetPeerID(myPi
);
162 CFMutableArrayRef syncing_peer_metas
= NULL
;
163 CFMutableArrayRef zombie_peer_metas
= NULL
;
164 CFErrorRef localError
= NULL
;
165 SOSPeerMetaRef myMeta
= NULL
;
167 if (myPi_id
&& isSyncing(myPi
, account
->user_public
) && SOSCircleHasPeer(account
->trusted_circle
, myPi
, NULL
)) {
168 CFMutableSetRef myViews
= SOSPeerInfoCopyEnabledViews(myPi
);
170 // We add V0 views to everyone if we see a V0 peer, or a peer with the view explicity enabled
171 // V2 peers shouldn't be explicity enabling the uber V0 view, though the seeds did.
172 __block
bool addV0Views
= SOSAccountSyncingV0(account
);
174 syncing_peer_metas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
175 zombie_peer_metas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
176 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
177 CFMutableArrayRef arrayToAddTo
= isSyncing(peer
, account
->user_public
) ? syncing_peer_metas
: zombie_peer_metas
;
179 // Compute views each peer is in that we are also in ourselves
180 CFMutableSetRef peerEnabledViews
= SOSPeerInfoCopyEnabledViews(peer
);
181 CFMutableSetRef views
= SOSAccountCopyIntersectedViews(peerEnabledViews
, myViews
);
182 CFReleaseNull(peerEnabledViews
);
185 CFSetAddValue(views
, kSOSViewKeychainV0
);
188 CFStringSetPerformWithDescription(views
, ^(CFStringRef viewsDescription
) {
189 secnotice("engine-notify", "Meta: %@: %@", SOSPeerInfoGetPeerID(peer
), viewsDescription
);
192 SOSPeerMetaRef peerMeta
= SOSPeerMetaCreateWithComponents(SOSPeerInfoGetPeerID(peer
), views
, NULL
);
193 CFReleaseNull(views
);
195 CFArrayAppendValue(arrayToAddTo
, peerMeta
);
196 CFReleaseNull(peerMeta
);
199 // We don't make a backup peer meta for the magic V0 peer
200 // Set up all the rest before we munge the set
201 SOSAccountAppendPeerMetasForViewBackups(account
, myViews
, syncing_peer_metas
);
203 // If we saw someone else needing V0, we sync V0, too!
205 CFSetAddValue(myViews
, kSOSViewKeychainV0
);
208 CFStringSetPerformWithDescription(myViews
, ^(CFStringRef viewsDescription
) {
209 secnotice("engine-notify", "My Meta: %@: %@", myPi_id
, viewsDescription
);
211 myMeta
= SOSPeerMetaCreateWithComponents(myPi_id
, myViews
, NULL
);
212 CFReleaseSafe(myViews
);
215 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
217 SOSEngineCircleChanged(engine
, myMeta
, syncing_peer_metas
, zombie_peer_metas
);
220 CFReleaseNull(myMeta
);
221 CFReleaseSafe(localError
);
222 CFReleaseNull(syncing_peer_metas
);
223 CFReleaseNull(zombie_peer_metas
);
226 // Upcoming call to View Changes Here
227 static void SOSAccountNotifyOfChange(SOSAccountRef account
, SOSCircleRef oldCircle
, SOSCircleRef newCircle
)
229 account
->circle_rings_retirements_need_attention
= true;
231 CFMutableSetRef old_members
= SOSCircleCopyPeers(oldCircle
, kCFAllocatorDefault
);
232 CFMutableSetRef new_members
= SOSCircleCopyPeers(newCircle
, kCFAllocatorDefault
);
234 CFMutableSetRef old_applicants
= SOSCircleCopyApplicants(oldCircle
, kCFAllocatorDefault
);
235 CFMutableSetRef new_applicants
= SOSCircleCopyApplicants(newCircle
, kCFAllocatorDefault
);
237 SOSPeerInfoRef me
= SOSAccountGetMyPeerInfo(account
);
238 if(me
&& CFSetContainsValue(new_members
, me
))
239 SOSAccountSetValue(account
, kSOSEscrowRecord
, kCFNull
, NULL
); //removing the escrow records from the account object
241 DifferenceAndCall(old_members
, new_members
, ^(CFSetRef added_members
, CFSetRef removed_members
) {
242 DifferenceAndCall(old_applicants
, new_applicants
, ^(CFSetRef added_applicants
, CFSetRef removed_applicants
) {
243 CFArrayForEach(account
->change_blocks
, ^(const void * notificationBlock
) {
244 secnotice("updates", "calling change block");
245 ((SOSAccountCircleMembershipChangeBlock
) notificationBlock
)(newCircle
, added_members
, removed_members
, added_applicants
, removed_applicants
);
250 CFReleaseNull(old_applicants
);
251 CFReleaseNull(new_applicants
);
253 CFReleaseNull(old_members
);
254 CFReleaseNull(new_members
);
258 CFDictionaryRef
SOSAccountHandleRetirementMessages(SOSAccountRef account
, CFDictionaryRef circle_retirement_messages
, CFErrorRef
*error
) {
259 CFStringRef circle_name
= SOSCircleGetName(account
->trusted_circle
);
260 CFMutableArrayRef handledRetirementIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
261 // We only handle one circle, look it up:
263 require_quiet(account
->trusted_circle
, finish
); // We don't fail, we intentionally handle nothing.
264 CFDictionaryRef retirement_dictionary
= asDictionary(CFDictionaryGetValue(circle_retirement_messages
, circle_name
), error
);
265 require_quiet(retirement_dictionary
, finish
);
266 CFDictionaryForEach(retirement_dictionary
, ^(const void *key
, const void *value
) {
268 SOSPeerInfoRef pi
= SOSPeerInfoCreateFromData(NULL
, error
, (CFDataRef
) value
);
269 if(pi
&& CFEqual(key
, SOSPeerInfoGetPeerID(pi
)) && SOSPeerInfoInspectRetirementTicket(pi
, error
)) {
270 CFSetAddValue(account
->retirees
, pi
);
272 account
->circle_rings_retirements_need_attention
= true; // Have to handle retirements.
274 CFArrayAppendValue(handledRetirementIDs
, key
);
280 // If we are in the retiree list, we somehow got resurrected
281 // clearly we took care of proper departure before so leave
282 // and delcare that we withdrew this time.
283 SOSPeerInfoRef me
= SOSAccountGetMyPeerInfo(account
);
284 if (me
&& CFSetContainsValue(account
->retirees
, me
)) {
285 SOSAccountPurgeIdentity(account
);
286 account
->departure_code
= kSOSDiscoveredRetirement
;
291 CFDictionaryRef result
= (CFArrayGetCount(handledRetirementIDs
) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
)
292 : CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, circle_name
, handledRetirementIDs
, NULL
);
294 CFReleaseNull(handledRetirementIDs
);
299 static SOSCircleRef
SOSAccountCreateCircleFrom(CFStringRef circleName
, CFTypeRef value
, CFErrorRef
*error
) {
300 if (value
&& !isData(value
) && !isNull(value
)) {
301 secnotice("circleCreat", "Value provided not appropriate for a circle");
302 CFStringRef description
= CFCopyTypeIDDescription(CFGetTypeID(value
));
303 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, NULL
,
304 CFSTR("Expected data or NULL got %@"), description
);
305 CFReleaseSafe(description
);
309 SOSCircleRef circle
= NULL
;
310 if (!value
|| isNull(value
)) {
311 secnotice("circleCreat", "No circle found in data: %@", value
);
314 circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, error
);
316 CFStringRef name
= SOSCircleGetName(circle
);
317 if (!CFEqualSafe(name
, circleName
)) {
318 secnotice("circleCreat", "Expected circle named %@, got %@", circleName
, name
);
319 SOSCreateErrorWithFormat(kSOSErrorNameMismatch
, NULL
, error
, NULL
,
320 CFSTR("Expected circle named %@, got %@"), circleName
, name
);
321 CFReleaseNull(circle
);
324 secnotice("circleCreat", "SOSCircleCreateFromData returned NULL.");
330 bool SOSAccountHandleCircleMessage(SOSAccountRef account
,
331 CFStringRef circleName
, CFDataRef encodedCircleMessage
, CFErrorRef
*error
) {
332 bool success
= false;
333 CFErrorRef localError
= NULL
;
334 SOSCircleRef circle
= SOSAccountCreateCircleFrom(circleName
, encodedCircleMessage
, &localError
);
336 success
= SOSAccountUpdateCircleFromRemote(account
, circle
, &localError
);
337 CFReleaseSafe(circle
);
339 secerror("NULL circle found, ignoring ...");
340 success
= true; // don't pend this NULL thing.
344 if (isSOSErrorCoded(localError
, kSOSErrorIncompatibleCircle
)) {
345 secerror("Incompatible circle found, abandoning membership: %@", circleName
);
346 CFReleaseNull(account
->my_identity
);
347 CFReleaseNull(account
->trusted_circle
);
357 CFReleaseNull(localError
);
362 bool SOSAccountHandleParametersChange(SOSAccountRef account
, CFDataRef parameters
, CFErrorRef
*error
){
364 SecKeyRef newKey
= NULL
;
365 CFDataRef newParameters
= NULL
;
366 bool success
= false;
368 if(SOSAccountRetrieveCloudParameters(account
, &newKey
, parameters
, &newParameters
, error
)) {
369 debugDumpUserParameters(CFSTR("SOSAccountHandleParametersChange got new user key parameters:"), parameters
);
370 secnotice("keygen", "SOSAccountHandleParametersChange got new public key: %@", newKey
);
372 if (CFEqualSafe(account
->user_public
, newKey
)) {
373 secnotice("updates", "Got same public key sent our way. Ignoring.");
375 } else if (CFEqualSafe(account
->previous_public
, newKey
)) {
376 secnotice("updates", "Got previous public key repeated. Ignoring.");
379 SOSAccountSetUnTrustedUserPublicKey(account
, newKey
);
380 SOSAccountSetParameters(account
, newParameters
);
383 if(SOSAccountRetryUserCredentials(account
)) {
384 secnotice("keygen", "Successfully used cached password with new parameters");
385 SOSAccountGenerationSignatureUpdate(account
, error
);
387 SOSAccountPurgePrivateCredential(account
);
388 secnotice("keygen", "Got new parameters for public key - could not find or use cached password");
391 account
->circle_rings_retirements_need_attention
= true;
392 account
->key_interests_need_updating
= true;
398 CFReleaseNull(newKey
);
399 CFReleaseNull(newParameters
);
404 static inline bool SOSAccountHasLeft(SOSAccountRef account
) {
405 switch(account
->departure_code
) {
406 case kSOSDiscoveredRetirement
: /* Fallthrough */
407 case kSOSLostPrivateKey
: /* Fallthrough */
408 case kSOSWithdrewMembership
: /* Fallthrough */
409 case kSOSMembershipRevoked
: /* Fallthrough */
410 case kSOSLeftUntrustedCircle
:
412 case kSOSNeverAppliedToCircle
: /* Fallthrough */
413 case kSOSNeverLeftCircle
: /* Fallthrough */
419 static const char *concordstring
[] = {
420 "kSOSConcordanceTrusted",
421 "kSOSConcordanceGenOld", // kSOSErrorReplay
422 "kSOSConcordanceNoUserSig", // kSOSErrorBadSignature
423 "kSOSConcordanceNoUserKey", // kSOSErrorNoKey
424 "kSOSConcordanceNoPeer", // kSOSErrorPeerNotFound
425 "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature
426 "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature
427 "kSOSConcordanceNoPeerSig",
428 "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
);
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 it ######");
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);
470 SOSTransportCircleRef transport
= account
->circle_transport
;
472 SOSAccountScanForRetired(account
, prospective_circle
, error
);
473 SOSCircleRef newCircle
= SOSAccountCloneCircleWithRetirement(account
, prospective_circle
, error
);
474 if(!newCircle
) return false;
476 SOSFullPeerInfoRef me_full
= account
->my_identity
;
477 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(me_full
);
478 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(me
);
479 myPeerID
= (myPeerID
) ? myPeerID
: CFSTR("No Peer");
481 if (me
&& SOSCircleUpdatePeerInfo(newCircle
, me
)) {
482 writeUpdate
= true; // If we update our peer in the new circle we should write it if we accept it.
493 static const char *actionstring
[] = {
494 "accept", "countersign", "leave", "revert", "ignore",
497 circle_action_t circle_action
= ignore
;
498 enum DepartureReason leave_reason
= kSOSNeverLeftCircle
;
500 SecKeyRef old_circle_key
= NULL
;
501 if(SOSCircleVerify(oldCircle
, account
->user_public
, NULL
)) old_circle_key
= account
->user_public
;
502 else if(account
->previous_public
&& SOSCircleVerify(oldCircle
, account
->previous_public
, NULL
)) old_circle_key
= account
->previous_public
;
503 bool userTrustedOldCircle
= (old_circle_key
!= NULL
) && haveOldCircle
;
505 SOSConcordanceStatus concstat
=
506 SOSCircleConcordanceTrust(oldCircle
, newCircle
,
507 old_circle_key
, account
->user_public
,
510 CFStringRef concStr
= NULL
;
512 case kSOSConcordanceTrusted
:
513 circle_action
= countersign
;
514 concStr
= CFSTR("Trusted");
516 case kSOSConcordanceGenOld
:
517 circle_action
= userTrustedOldCircle
? revert
: ignore
;
518 concStr
= CFSTR("Generation Old");
520 case kSOSConcordanceBadUserSig
:
521 case kSOSConcordanceBadPeerSig
:
522 circle_action
= userTrustedOldCircle
? revert
: accept
;
523 concStr
= CFSTR("Bad Signature");
525 case kSOSConcordanceNoUserSig
:
526 circle_action
= userTrustedOldCircle
? revert
: accept
;
527 concStr
= CFSTR("No User Signature");
529 case kSOSConcordanceNoPeerSig
:
530 circle_action
= accept
; // We might like this one eventually but don't countersign.
531 concStr
= CFSTR("No trusted peer signature");
532 secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later");
534 case kSOSConcordanceNoPeer
:
535 circle_action
= leave
;
536 leave_reason
= kSOSLeftUntrustedCircle
;
537 concStr
= CFSTR("No trusted peer left");
539 case kSOSConcordanceNoUserKey
:
540 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
544 secerror("##### Bad Error Return from ConcordanceTrust");
549 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
);
551 SOSCircleRef circleToPush
= NULL
;
553 if (circle_action
== leave
) {
554 circle_action
= ignore
; (void) circle_action
; // Acknowledge this is a dead store.
556 if (me
&& SOSCircleHasPeer(oldCircle
, me
, NULL
)) {
557 secnotice("account", "Leaving circle with peer %@", me
);
558 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
559 debugDumpCircle(CFSTR("newCircle"), newCircle
);
560 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
);
561 secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@",
562 account
->user_public
, account
->previous_public
, old_circle_key
);
564 if (sosAccountLeaveCircle(account
, newCircle
, error
)) {
565 secnotice("leaveCircle", "Leaving circle by newcircle state");
566 circleToPush
= newCircle
;
568 secnotice("signing", "Can't leave circle, but dumping identities");
571 account
->departure_code
= leave_reason
;
572 circle_action
= accept
;
576 // We are not in this circle, but we need to update account with it, since we got it from cloud
577 secnotice("signing", "We are not in this circle, but we need to update account with it");
578 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
579 debugDumpCircle(CFSTR("newCircle"), newCircle
);
580 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle
);
581 circle_action
= accept
;
585 if (circle_action
== countersign
) {
586 if (me
&& SOSCircleHasPeer(newCircle
, me
, NULL
)) {
587 if (SOSCircleVerifyPeerSigned(newCircle
, me
, NULL
)) {
588 secnotice("signing", "Already concur with the new circle");
590 CFErrorRef signing_error
= NULL
;
592 if (me_full
&& SOSCircleConcordanceSign(newCircle
, me_full
, &signing_error
)) {
593 circleToPush
= newCircle
;
594 secnotice("signing", "Concurred with new circle");
596 secerror("Failed to concurrence sign, error: %@", signing_error
);
599 CFReleaseSafe(signing_error
);
602 if(SOSAccountVerifyAndAcceptHSAApplicants(account
, newCircle
, error
)) {
603 circleToPush
= newCircle
;
607 secnotice("signing", "Not countersigning, not in new circle");
608 debugDumpCircle(CFSTR("circle to countersign"), newCircle
);
610 circle_action
= accept
;
613 if (circle_action
== accept
) {
614 if (me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
) && !SOSCircleHasPeer(newCircle
, me
, NULL
)) {
615 // Don't destroy evidence of other code determining reason for leaving.
616 if(!SOSAccountHasLeft(account
)) account
->departure_code
= kSOSMembershipRevoked
;
617 secnotice("account", "Member of old circle but not of new circle");
618 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
619 debugDumpCircle(CFSTR("newCircle"), newCircle
);
623 && SOSCircleHasActivePeer(oldCircle
, me
, NULL
)
624 && !(SOSCircleCountPeers(oldCircle
) == 1 && SOSCircleHasPeer(oldCircle
, me
, NULL
)) // If it was our offering, don't change ID to avoid ghosts
625 && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) {
626 secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
627 if (account
->my_identity
)
628 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
);
629 CFReleaseNull(account
->my_identity
);
634 if (me
&& SOSCircleHasRejectedApplicant(newCircle
, me
, NULL
)) {
635 SOSPeerInfoRef reject
= SOSCircleCopyRejectedApplicant(newCircle
, me
, NULL
);
636 if(CFEqualSafe(reject
, me
) && SOSPeerInfoApplicationVerify(me
, account
->user_public
, NULL
)) {
637 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
638 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
639 debugDumpCircle(CFSTR("newCircle"), newCircle
);
640 if (account
->my_identity
)
641 SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, NULL
);
642 CFReleaseNull(account
->my_identity
);
646 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me
), SOSCircleGetName(oldCircle
));
647 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
648 debugDumpCircle(CFSTR("newCircle"), newCircle
);
649 SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
);
654 CFRetainSafe(oldCircle
);
655 CFRetainAssign(account
->trusted_circle
, newCircle
);
656 SOSAccountSetPreviousPublic(account
);
658 secnotice("signing", "%@, Accepting new circle", concStr
);
660 if (me
&& account
->user_public_trusted
661 && SOSCircleHasApplicant(oldCircle
, me
, NULL
)
662 && SOSCircleCountPeers(newCircle
) > 0
663 && !SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleHasApplicant(newCircle
, me
, NULL
)) {
664 // We weren't rejected (above would have set me to NULL.
665 // We were applying and we weren't accepted.
666 // Our application is declared lost, let us reapply.
668 secnotice("signing", "requesting readmission to new circle");
669 if (SOSCircleRequestReadmission(newCircle
, account
->user_public
, me
, NULL
))
673 if (me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) {
674 SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
);
677 SOSAccountNotifyOfChange(account
, oldCircle
, newCircle
);
679 CFReleaseNull(oldCircle
);
682 circleToPush
= newCircle
;
683 account
->key_interests_need_updating
= true;
687 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles
688 * and pushing our current view of the circle (oldCircle). We'll only do this if we actually
689 * are a member of oldCircle - never for an empty circle.
692 if (circle_action
== revert
) {
693 if(haveOldCircle
&& me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) {
694 secnotice("signing", "%@, Rejecting new circle, re-publishing old circle", concStr
);
695 debugDumpCircle(CFSTR("oldCircle"), oldCircle
);
696 debugDumpCircle(CFSTR("newCircle"), newCircle
);
697 circleToPush
= oldCircle
;
699 secnotice("canary", "%@, Rejecting: new circle Have no old circle - would reset", concStr
);
704 if (circleToPush
!= NULL
) {
705 secnotice("signing", "Pushing:[%s]", local_remote
);
706 CFDataRef circle_data
= SOSCircleCopyEncodedData(circleToPush
, kCFAllocatorDefault
, error
);
709 // Ensure we flush changes
710 account
->circle_rings_retirements_need_attention
= true;
712 //recording circle we are pushing in KVS
713 success
&= SOSTransportCircleRecordLastCirclePushedInKVS(transport
, SOSCircleGetName(circleToPush
), circle_data
);
714 //posting new circle to peers
715 success
&= SOSTransportCirclePostCircle(transport
, SOSCircleGetName(circleToPush
), circle_data
, error
);
719 CFReleaseNull(circle_data
);
722 CFReleaseSafe(newCircle
);
723 CFReleaseNull(emptyCircle
);