2 // SOSAccountRingUpdate.c
9 #include "SOSAccountPriv.h"
10 #include <Security/SecureObjectSync/SOSTransportCircle.h>
11 #include <Security/SecureObjectSync/SOSTransport.h>
12 #include <Security/SecureObjectSync/SOSViews.h>
13 #include <Security/SecureObjectSync/SOSRing.h>
14 #include <Security/SecureObjectSync/SOSRingUtils.h>
15 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
18 static inline bool SOSAccountHasLeft(SOSAccountRef account
) {
19 switch(account
->departure_code
) {
20 case kSOSWithdrewMembership
: /* Fallthrough */
21 case kSOSMembershipRevoked
: /* Fallthrough */
22 case kSOSLeftUntrustedCircle
:
24 case kSOSNeverAppliedToCircle
: /* Fallthrough */
25 case kSOSNeverLeftCircle
: /* Fallthrough */
32 static const char *concordstring
[] = {
33 "kSOSConcordanceTrusted",
34 "kSOSConcordanceGenOld", // kSOSErrorReplay
35 "kSOSConcordanceNoUserSig", // kSOSErrorBadSignature
36 "kSOSConcordanceNoUserKey", // kSOSErrorNoKey
37 "kSOSConcordanceNoPeer", // kSOSErrorPeerNotFound
38 "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature
39 "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature
40 "kSOSConcordanceNoPeerSig",
41 "kSOSConcordanceWeSigned",
42 "kSOSConcordanceInvalidMembership",
43 "kSOSConcordanceMissingMe",
44 "kSOSConcordanceImNotWorthy",
48 static bool SOSAccountIsPeerRetired(SOSAccountRef account
, CFSetRef peers
){
49 CFMutableArrayRef peerInfos
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
52 CFSetForEach(peers
, ^(const void *value
) {
53 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
54 if(SOSPeerInfoIsRetirementTicket(peer
))
55 CFArrayAppendValue(peerInfos
, peer
);
57 if(CFArrayGetCount(peerInfos
) > 0){
58 if(!SOSAccountRemoveBackupPeers(account
, peerInfos
, NULL
))
59 secerror("Could not remove peers: %@, from the backup", peerInfos
);
69 static bool SOSAccountBackupSliceKeyBagNeedsFix(SOSAccountRef account
, SOSBackupSliceKeyBagRef bskb
) {
71 if (SOSBSKBIsDirect(bskb
) || account
->backup_key
== NULL
)
74 CFSetRef peers
= SOSBSKBGetPeers(bskb
);
76 /* first scan for retired peers, and kick'em out!*/
77 SOSAccountIsPeerRetired(account
, peers
);
79 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
83 SOSPeerInfoRef meInBag
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
84 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
85 CFDataRef meInBagBK
= SOSPeerInfoCopyBackupKey(meInBag
);
86 needsFix
= !(meInBag
&& CFEqualSafe(myBK
,
89 CFReleaseNull(meInBagBK
);
96 bool SOSAccountHandleUpdateRing(SOSAccountRef account
, SOSRingRef prospectiveRing
, bool writeUpdate
, CFErrorRef
*error
) {
98 bool haveOldRing
= true;
99 const char *localRemote
= writeUpdate
? "local": "remote";
100 SOSFullPeerInfoRef fpi
= account
->my_identity
;
101 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
102 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
103 bool peerActive
= (fpi
&& pi
&& peerID
&& SOSAccountIsInCircle(account
, NULL
));
106 secnotice("signing", "start:[%s] %@", localRemote
, prospectiveRing
);
108 require_quiet(SOSAccountHasPublicKey(account
, error
), errOut
);
110 require_action_quiet(prospectiveRing
, errOut
,
111 SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("No Ring to work with"), NULL
, error
));
113 // We should at least have a sane ring system in the account object
114 require_quiet(SOSAccountCheckForRings(account
, error
), errOut
);
116 CFStringRef ringName
= SOSRingGetName(prospectiveRing
);
117 SOSRingRef oldRing
= SOSAccountGetRing(account
, ringName
, NULL
);
119 SOSTransportCircleRef transport
= account
->circle_transport
;
121 SOSRingRef newRing
= CFRetainSafe(prospectiveRing
); // TODO: SOSAccountCloneRingWithRetirement(account, prospectiveRing, error);
132 static const char *actionstring
[] = {
133 "accept", "countersign", "leave", "revert", "modify", "ignore",
136 ringAction_t ringAction
= ignore
;
138 bool userTrustedoldRing
= true;
140 SOSCircleRef circle
= SOSAccountGetCircle(account
, NULL
);
141 CFSetRef peers
= SOSCircleCopyPeers(circle
, kCFAllocatorDefault
);
143 SecKeyRef oldKey
= account
->user_public
;
146 // for now user keys aren't explored.
147 // we should ask the ring if it cares about it and then do the magic to find the right user keys.
148 SecKeyRef oldKey
= account
->user_public
;
150 if(SOSRingPKTrusted(oldRing
, account
->user_public
, NULL
)) oldKey
= account
->user_public
;
151 else if(account
->previous_public
&& SOSRingPKTrusted(oldRing
, account
->previous_public
, NULL
)) oldKey
= account
->previous_public
;
152 bool userTrustedoldRing
= (oldKey
!= NULL
) && haveOldRing
;
160 SOSConcordanceStatus concstat
= SOSRingConcordanceTrust(fpi
, peers
, oldRing
, newRing
, oldKey
, account
->user_public
, peerID
, error
);
161 CFReleaseNull(peers
);
163 CFStringRef concStr
= NULL
;
165 case kSOSConcordanceTrusted
:
166 ringAction
= countersign
;
167 concStr
= CFSTR("Trusted");
169 case kSOSConcordanceGenOld
:
170 ringAction
= userTrustedoldRing
? revert
: ignore
;
171 concStr
= CFSTR("Generation Old");
173 case kSOSConcordanceBadUserSig
:
174 case kSOSConcordanceBadPeerSig
:
175 ringAction
= userTrustedoldRing
? revert
: accept
;
176 concStr
= CFSTR("Bad Signature");
178 case kSOSConcordanceNoUserSig
:
179 ringAction
= userTrustedoldRing
? revert
: accept
;
180 concStr
= CFSTR("No User Signature");
182 case kSOSConcordanceNoPeerSig
:
183 ringAction
= accept
; // We might like this one eventually but don't countersign.
184 concStr
= CFSTR("No trusted peer signature");
185 secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newRing
);
187 case kSOSConcordanceNoPeer
:
189 concStr
= CFSTR("No trusted peer left");
191 case kSOSConcordanceNoUserKey
:
192 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
196 case kSOSConcordanceMissingMe
:
197 case kSOSConcordanceImNotWorthy
:
199 concStr
= CFSTR("Incorrect membership for me");
201 case kSOSConcordanceInvalidMembership
:
202 ringAction
= userTrustedoldRing
? revert
: ignore
;
203 concStr
= CFSTR("Invalid Ring Membership");
206 secerror("##### Bad Error Return from ConcordanceTrust");
211 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring
[ringAction
], concordstring
[concstat
], userTrustedoldRing
? "trusted" : "untrusted");
213 SOSRingRef ringToPush
= NULL
;
214 bool iWasInOldRing
= peerID
&& SOSRingHasPeerID(oldRing
, peerID
);
215 bool iAmInNewRing
= peerID
&& SOSRingHasPeerID(newRing
, peerID
);
216 bool ringIsBackup
= SOSRingGetType(newRing
) == kSOSRingBackup
;
218 if (ringIsBackup
&& peerActive
) {
219 if (ringAction
== accept
|| ringAction
== countersign
) {
220 CFErrorRef localError
= NULL
;
221 SOSBackupSliceKeyBagRef bskb
= SOSRingCopyBackupSliceKeyBag(newRing
, &localError
);
224 secnotice("signing", "Backup ring with no backup slice keybag (%@)", localError
);
225 } else if (SOSAccountBackupSliceKeyBagNeedsFix(account
, bskb
)) {
228 CFReleaseSafe(localError
);
232 if (ringAction
== modify
) {
233 CFErrorRef updateError
= NULL
;
234 CFDictionarySetValue(account
->trusted_rings
, ringName
, newRing
);
236 if(SOSAccountUpdateOurPeerInBackup(account
, newRing
, &updateError
)) {
237 secdebug("signing", "Modified backup ring to include us");
239 secerror("Could not add ourselves to the backup: (%@)", updateError
);
241 CFReleaseSafe(updateError
);
243 // Fall through to normal modify handling.
247 if (ringAction
== modify
) {
251 if (ringAction
== leave
) {
253 if (sosAccountLeaveRing(account
, newRing
, error
)) {
254 ringToPush
= newRing
;
256 secnotice("signing", "Can't leave ring %@", oldRing
);
261 // We are not in this ring, but we need to update account with it, since we got it from cloud
262 secnotice("signing", "We are not in this ring, but we need to update account with it");
267 if (ringAction
== countersign
) {
269 if (SOSRingPeerTrusted(newRing
, fpi
, NULL
)) {
270 secinfo("signing", "Already concur with: %@", newRing
);
272 CFErrorRef signingError
= NULL
;
274 if (fpi
&& SOSRingConcordanceSign(newRing
, fpi
, &signingError
)) {
275 ringToPush
= newRing
;
276 secinfo("signing", "Concurred with: %@", newRing
);
278 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signingError
, oldRing
, newRing
);
281 CFReleaseSafe(signingError
);
284 secnotice("signing", "Not countersigning, not in ring: %@", newRing
);
289 if (ringAction
== accept
) {
290 if (iWasInOldRing
&& !iAmInNewRing
) {
292 // Don't destroy evidence of other code determining reason for leaving.
293 //if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
294 // TODO: LeaveReason for rings
297 if (pi
&& SOSRingHasRejection(newRing
, peerID
)) {
298 // TODO: ReasonForLeaving for rings
299 SOSRingRemoveRejection(newRing
, peerID
);
302 CFRetainSafe(oldRing
);
303 CFDictionarySetValue(account
->trusted_rings
, ringName
, newRing
);
304 // TODO: Why was this? SOSAccountSetPreviousPublic(account);
306 secnotice("signing", "%@, Accepting ring: %@", concStr
, newRing
);
308 if (pi
&& account
->user_public_trusted
309 && SOSRingHasApplicant(oldRing
, peerID
)
310 && SOSRingCountPeers(newRing
) > 0
311 && !iAmInNewRing
&& !SOSRingHasApplicant(newRing
, peerID
)) {
312 // We weren't rejected (above would have set me to NULL.
313 // We were applying and we weren't accepted.
314 // Our application is declared lost, let us reapply.
316 if (SOSRingApply(newRing
, account
->user_public
, fpi
, NULL
))
317 if(peerActive
) writeUpdate
= true;
320 if (pi
&& SOSRingHasPeerID(oldRing
, peerID
)) {
321 SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
);
324 CFReleaseNull(oldRing
);
326 account
->circle_rings_retirements_need_attention
= true;
329 ringToPush
= newRing
;
330 SOSUpdateKeyInterest(account
);
334 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new rings
335 * and pushing our current view of the ring (oldRing). We'll only do this if we actually
336 * are a member of oldRing - never for an empty ring.
339 if (ringAction
== revert
) {
340 if(haveOldRing
&& peerActive
&& SOSRingHasPeerID(oldRing
, peerID
)) {
341 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr
, newRing
, oldRing
);
342 ringToPush
= oldRing
;
344 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr
, newRing
);
349 if (ringToPush
!= NULL
) {
350 secnotice("signing", "Pushing:[%s] %@", localRemote
, ringToPush
);
351 CFDataRef ringData
= SOSRingCopyEncodedData(ringToPush
, error
);
353 success
&= SOSTransportCircleRingPostRing(transport
, SOSRingGetName(ringToPush
), ringData
, error
);
357 CFReleaseNull(ringData
);
360 CFReleaseSafe(newRing
);