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
);