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 * __unused 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
);
66 CFReleaseNull(peerInfos
);
71 static bool SOSAccountBackupSliceKeyBagNeedsFix(SOSAccountRef account
, SOSBackupSliceKeyBagRef bskb
) {
73 if (SOSBSKBIsDirect(bskb
) || account
->backup_key
== NULL
)
76 CFSetRef peers
= SOSBSKBGetPeers(bskb
);
78 /* first scan for retired peers, and kick'em out!*/
79 SOSAccountIsPeerRetired(account
, peers
);
81 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
85 SOSPeerInfoRef meInBag
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
86 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
87 CFDataRef meInBagBK
= SOSPeerInfoCopyBackupKey(meInBag
);
88 needsFix
= !(meInBag
&& CFEqualSafe(myBK
,
91 CFReleaseNull(meInBagBK
);
107 static const char * __unused actionstring
[] = {
108 "accept", "countersign", "leave", "revert", "modify", "ignore",
111 bool SOSAccountHandleUpdateRing(SOSAccountRef account
, SOSRingRef prospectiveRing
, bool writeUpdate
, CFErrorRef
*error
) {
113 bool haveOldRing
= true;
114 const char * __unused localRemote
= writeUpdate
? "local": "remote";
115 SOSFullPeerInfoRef fpi
= account
->my_identity
;
116 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
117 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
118 bool peerActive
= (fpi
&& pi
&& peerID
&& SOSAccountIsInCircle(account
, NULL
));
121 secdebug("ringSigning", "start:[%s] %@", localRemote
, prospectiveRing
);
123 require_quiet(SOSAccountHasPublicKey(account
, error
), errOut
);
125 require_action_quiet(prospectiveRing
, errOut
,
126 SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("No Ring to work with"), NULL
, error
));
128 require_action_quiet(SOSRingIsStable(prospectiveRing
), errOut
, SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("You give rings a bad name"), NULL
, error
));
130 // We should at least have a sane ring system in the account object
131 require_quiet(SOSAccountCheckForRings(account
, error
), errOut
);
133 CFStringRef ringName
= SOSRingGetName(prospectiveRing
);
134 SOSRingRef oldRing
= SOSAccountCopyRing(account
, ringName
, NULL
);
136 SOSTransportCircleRef transport
= account
->circle_transport
;
138 SOSRingRef newRing
= CFRetainSafe(prospectiveRing
); // TODO: SOSAccountCloneRingWithRetirement(account, prospectiveRing, error);
140 ringAction_t ringAction
= ignore
;
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
;
161 oldRing
= CFRetainSafe(newRing
);
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 secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later %@", newRing
);
191 case kSOSConcordanceNoPeer
:
193 concStr
= CFSTR("No trusted peer left");
195 case kSOSConcordanceNoUserKey
:
196 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
200 case kSOSConcordanceMissingMe
:
201 case kSOSConcordanceImNotWorthy
:
203 concStr
= CFSTR("Incorrect membership for me");
205 case kSOSConcordanceInvalidMembership
:
206 ringAction
= userTrustedoldRing
? revert
: ignore
;
207 concStr
= CFSTR("Invalid Ring Membership");
210 secerror("##### Bad Error Return from ConcordanceTrust");
217 secdebug("ringSigning", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring
[ringAction
], concordstring
[concstat
], userTrustedoldRing
? "trusted" : "untrusted");
219 SOSRingRef ringToPush
= NULL
;
220 bool iWasInOldRing
= peerID
&& SOSRingHasPeerID(oldRing
, peerID
);
221 bool iAmInNewRing
= peerID
&& SOSRingHasPeerID(newRing
, peerID
);
222 bool ringIsBackup
= SOSRingGetType(newRing
) == kSOSRingBackup
;
224 if (ringIsBackup
&& peerActive
) {
225 if (ringAction
== accept
|| ringAction
== countersign
) {
226 CFErrorRef localError
= NULL
;
227 SOSBackupSliceKeyBagRef bskb
= SOSRingCopyBackupSliceKeyBag(newRing
, &localError
);
230 secnotice("ringSigning", "Backup ring with no backup slice keybag (%@)", localError
);
231 } else if (SOSAccountBackupSliceKeyBagNeedsFix(account
, bskb
)) {
234 CFReleaseSafe(localError
);
238 if (ringAction
== modify
) {
239 CFErrorRef updateError
= NULL
;
240 SOSAccountSetRing(account
, newRing
, ringName
, error
);
242 if(SOSAccountUpdateOurPeerInBackup(account
, newRing
, &updateError
)) {
243 secdebug("signing", "Modified backup ring to include us");
245 secerror("Could not add ourselves to the backup: (%@)", updateError
);
247 CFReleaseSafe(updateError
);
249 // Fall through to normal modify handling.
253 if (ringAction
== modify
) {
257 if (ringAction
== leave
) {
259 if (sosAccountLeaveRing(account
, newRing
, error
)) {
260 ringToPush
= newRing
;
262 secdebug("ringSigning", "Can't leave ring %@", oldRing
);
267 // We are not in this ring, but we need to update account with it, since we got it from cloud
272 if (ringAction
== countersign
) {
274 if (SOSRingPeerTrusted(newRing
, fpi
, NULL
)) {
275 secdebug("ringSigning", "Already concur with: %@", newRing
);
277 CFErrorRef signingError
= NULL
;
279 if (fpi
&& SOSRingConcordanceSign(newRing
, fpi
, &signingError
)) {
280 ringToPush
= newRing
;
282 secerror("Failed to concordance sign, error: %@ Old: %@ New: %@", signingError
, oldRing
, newRing
);
285 CFReleaseSafe(signingError
);
288 secdebug("ringSigning", "Not countersigning, not in ring: %@", newRing
);
293 if (ringAction
== accept
) {
294 if (iWasInOldRing
&& !iAmInNewRing
) {
296 // Don't destroy evidence of other code determining reason for leaving.
297 //if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
298 // TODO: LeaveReason for rings
301 if (pi
&& SOSRingHasRejection(newRing
, peerID
)) {
302 // TODO: ReasonForLeaving for rings
303 SOSRingRemoveRejection(newRing
, peerID
);
306 SOSAccountSetRing(account
, newRing
, ringName
, error
);
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
);
325 account
->circle_rings_retirements_need_attention
= true;
328 ringToPush
= newRing
;
329 account
->key_interests_need_updating
= true;
333 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new rings
334 * and pushing our current view of the ring (oldRing). We'll only do this if we actually
335 * are a member of oldRing - never for an empty ring.
338 if (ringAction
== revert
) {
339 if(haveOldRing
&& peerActive
&& SOSRingHasPeerID(oldRing
, peerID
)) {
340 secdebug("ringSigning", "%@, Rejecting: %@ re-publishing %@", concStr
, newRing
, oldRing
);
341 ringToPush
= oldRing
;
343 secdebug("ringSigning", "%@, Rejecting: %@ Have no old circle - would reset", concStr
, newRing
);
348 if (ringToPush
!= NULL
) {
349 secdebug("ringSigning", "Pushing:[%s] %@", localRemote
, ringToPush
);
350 CFDataRef ringData
= SOSRingCopyEncodedData(ringToPush
, error
);
352 success
&= SOSTransportCircleRingPostRing(transport
, SOSRingGetName(ringToPush
), ringData
, error
);
356 CFReleaseNull(ringData
);
358 CFReleaseNull(oldRing
);
359 CFReleaseSafe(newRing
);