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 peerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
52 CFSetForEach(peers
, ^(const void *value
) {
53 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
54 if(SOSPeerInfoIsRetirementTicket(peer
))
55 CFArrayAppendValue(peerIDs
, peer
);
57 if(CFArrayGetCount(peerIDs
) > 0){
58 if(!SOSAccountRemoveBackupPeers(account
, peerIDs
, NULL
))
59 secerror("Could not remove peers: %@, from the backup", peerIDs
);
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 neverWrite
= !(fpi
&& pi
&& peerID
&& SOSAccountIsInCircle(account
, NULL
));
105 secinfo("signing", "start:[%s] %@", localRemote
, prospectiveRing
);
107 require_action_quiet(!(writeUpdate
&& neverWrite
), errOut
, SOSCreateError(kSOSErrorNotReady
, CFSTR("Can't update from local if FullPeerInfo not present"), NULL
, error
));
109 require_quiet(SOSAccountHasPublicKey(account
, error
), errOut
);
111 require_action_quiet(prospectiveRing
, errOut
,
112 SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("No Ring to work with"), NULL
, error
));
114 // We should at least have a sane ring system in the account object
115 require_quiet(SOSAccountCheckForRings(account
, error
), errOut
);
117 CFStringRef ringName
= SOSRingGetName(prospectiveRing
);
118 SOSRingRef oldRing
= SOSAccountGetRing(account
, ringName
, NULL
);
120 SOSTransportCircleRef transport
= account
->circle_transport
;
122 // SOSAccountScanForRetired(account, prospectiveRing, error);
124 SOSRingRef newRing
= CFRetainSafe(prospectiveRing
); // TODO: SOSAccountCloneRingWithRetirement(account, prospectiveRing, error);
135 static const char *actionstring
[] = {
136 "accept", "countersign", "leave", "revert", "modify", "ignore",
139 ringAction_t ringAction
= ignore
;
140 enum DepartureReason leaveReason
= kSOSNeverLeftCircle
;
142 bool userTrustedoldRing
= true;
144 SOSCircleRef circle
= SOSAccountGetCircle(account
, NULL
);
145 CFSetRef peers
= SOSCircleCopyPeers(circle
, kCFAllocatorDefault
);
147 SecKeyRef oldKey
= account
->user_public
;
150 // for now user keys aren't explored.
151 // we should ask the ring if it cares about it and then do the magic to find the right user keys.
152 SecKeyRef oldKey
= account
->user_public
;
154 if(SOSRingPKTrusted(oldRing
, account
->user_public
, NULL
)) oldKey
= account
->user_public
;
155 else if(account
->previous_public
&& SOSRingPKTrusted(oldRing
, account
->previous_public
, NULL
)) oldKey
= account
->previous_public
;
156 bool userTrustedoldRing
= (oldKey
!= NULL
) && haveOldRing
;
164 SOSConcordanceStatus concstat
= SOSRingConcordanceTrust(fpi
, peers
, oldRing
, newRing
, oldKey
, account
->user_public
, peerID
, error
);
165 CFReleaseNull(peers
);
167 CFStringRef concStr
= NULL
;
169 case kSOSConcordanceTrusted
:
170 ringAction
= countersign
;
171 concStr
= CFSTR("Trusted");
173 case kSOSConcordanceGenOld
:
174 ringAction
= userTrustedoldRing
? revert
: ignore
;
175 concStr
= CFSTR("Generation Old");
177 case kSOSConcordanceBadUserSig
:
178 case kSOSConcordanceBadPeerSig
:
179 ringAction
= userTrustedoldRing
? revert
: accept
;
180 concStr
= CFSTR("Bad Signature");
182 case kSOSConcordanceNoUserSig
:
183 ringAction
= userTrustedoldRing
? revert
: accept
;
184 concStr
= CFSTR("No User Signature");
186 case kSOSConcordanceNoPeerSig
:
187 ringAction
= accept
; // We might like this one eventually but don't countersign.
188 concStr
= CFSTR("No trusted peer signature");
189 secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newRing
);
191 case kSOSConcordanceNoPeer
:
193 leaveReason
= kSOSLeftUntrustedCircle
;
194 concStr
= CFSTR("No trusted peer left");
196 case kSOSConcordanceNoUserKey
:
197 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
201 case kSOSConcordanceMissingMe
:
202 case kSOSConcordanceImNotWorthy
:
204 concStr
= CFSTR("Incorrect membership for me");
206 case kSOSConcordanceInvalidMembership
:
207 ringAction
= userTrustedoldRing
? revert
: ignore
;
208 concStr
= CFSTR("Invalid Ring Membership");
211 secerror("##### Bad Error Return from ConcordanceTrust");
216 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring
[ringAction
], concordstring
[concstat
], userTrustedoldRing
? "trusted" : "untrusted");
218 SOSRingRef ringToPush
= NULL
;
219 bool iWasInOldRing
= peerID
&& SOSRingHasPeerID(oldRing
, peerID
);
220 bool iAmInNewRing
= peerID
&& SOSRingHasPeerID(newRing
, peerID
);
221 bool ringIsBackup
= SOSRingGetType(newRing
) == kSOSRingBackup
;
223 if (ringIsBackup
&& !neverWrite
) {
224 if (ringAction
== accept
|| ringAction
== countersign
) {
225 CFErrorRef localError
= NULL
;
226 SOSBackupSliceKeyBagRef bskb
= SOSRingCopyBackupSliceKeyBag(newRing
, &localError
);
229 secnotice("signing", "Backup ring with no backup slice keybag (%@)", localError
);
230 } else if (SOSAccountBackupSliceKeyBagNeedsFix(account
, bskb
)) {
233 CFReleaseSafe(localError
);
237 if (ringAction
== modify
) {
238 CFErrorRef updateError
= NULL
;
239 CFDictionarySetValue(account
->trusted_rings
, ringName
, newRing
);
241 if(SOSAccountUpdateOurPeerInBackup(account
, newRing
, &updateError
)) {
242 secdebug("signing", "Modified backup ring to include us");
244 secerror("Could not add ourselves to the backup: (%@)", updateError
);
246 CFReleaseSafe(updateError
);
248 // Fall through to normal modify handling.
252 if (ringAction
== modify
) {
256 if (ringAction
== leave
) {
258 if (sosAccountLeaveRing(account
, newRing
, error
)) {
259 ringToPush
= newRing
;
261 secnotice("signing", "Can't leave ring %@", oldRing
);
264 account
->departure_code
= leaveReason
;
267 // We are not in this ring, but we need to update account with it, since we got it from cloud
268 secnotice("signing", "We are not in this ring, but we need to update account with it");
273 if (ringAction
== countersign
) {
275 if (SOSRingPeerTrusted(newRing
, fpi
, NULL
)) {
276 secinfo("signing", "Already concur with: %@", newRing
);
278 CFErrorRef signingError
= NULL
;
280 if (fpi
&& SOSRingConcordanceSign(newRing
, fpi
, &signingError
)) {
281 ringToPush
= newRing
;
282 secinfo("signing", "Concurred with: %@", newRing
);
284 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signingError
, oldRing
, newRing
);
287 CFReleaseSafe(signingError
);
290 secnotice("signing", "Not countersigning, not in ring: %@", newRing
);
295 if (ringAction
== accept
) {
296 if (iWasInOldRing
&& !iAmInNewRing
) {
298 // Don't destroy evidence of other code determining reason for leaving.
299 //if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
300 // TODO: LeaveReason for rings
303 if (pi
&& SOSRingHasRejection(newRing
, peerID
)) {
304 // TODO: ReasonForLeaving for rings
305 SOSRingRemoveRejection(newRing
, peerID
);
308 CFRetainSafe(oldRing
);
309 CFDictionarySetValue(account
->trusted_rings
, ringName
, newRing
);
310 // TODO: Why was this? SOSAccountSetPreviousPublic(account);
312 secnotice("signing", "%@, Accepting ring: %@", concStr
, newRing
);
314 if (pi
&& account
->user_public_trusted
315 && SOSRingHasApplicant(oldRing
, peerID
)
316 && SOSRingCountPeers(newRing
) > 0
317 && !iAmInNewRing
&& !SOSRingHasApplicant(newRing
, peerID
)) {
318 // We weren't rejected (above would have set me to NULL.
319 // We were applying and we weren't accepted.
320 // Our application is declared lost, let us reapply.
322 if (SOSRingApply(newRing
, account
->user_public
, fpi
, NULL
))
326 if (pi
&& SOSRingHasPeerID(oldRing
, peerID
)) {
327 SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
);
330 CFReleaseNull(oldRing
);
332 account
->circle_rings_retirements_need_attention
= true;
334 if (writeUpdate
&& !neverWrite
)
335 ringToPush
= newRing
;
336 SOSUpdateKeyInterest();
340 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new rings
341 * and pushing our current view of the ring (oldRing). We'll only do this if we actually
342 * are a member of oldRing - never for an empty ring.
345 if (ringAction
== revert
) {
346 if(haveOldRing
&& !neverWrite
&& SOSRingHasPeerID(oldRing
, peerID
)) {
347 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr
, newRing
, oldRing
);
348 ringToPush
= oldRing
;
350 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr
, newRing
);
355 if (ringToPush
!= NULL
) {
356 secnotice("signing", "Pushing:[%s] %@", localRemote
, ringToPush
);
357 CFDataRef ringData
= SOSRingCopyEncodedData(ringToPush
, error
);
359 success
&= SOSTransportCircleRingPostRing(transport
, SOSRingGetName(ringToPush
), ringData
, error
);
363 CFReleaseNull(ringData
);
366 CFReleaseSafe(newRing
);