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