]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ProjectHeaders/Security/SecureObjectSync/SOSAccountRingUpdate.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / ProjectHeaders / Security / SecureObjectSync / SOSAccountRingUpdate.c
1 //
2 // SOSAccountRingUpdate.c
3 // sec
4 //
5 //
6
7 #include <stdio.h>
8
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>
16
17 #if 0
18 static inline bool SOSAccountHasLeft(SOSAccountRef account) {
19 switch(account->departure_code) {
20 case kSOSWithdrewMembership: /* Fallthrough */
21 case kSOSMembershipRevoked: /* Fallthrough */
22 case kSOSLeftUntrustedCircle:
23 return true;
24 case kSOSNeverAppliedToCircle: /* Fallthrough */
25 case kSOSNeverLeftCircle: /* Fallthrough */
26 default:
27 return false;
28 }
29 }
30 #endif
31
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",
45 };
46
47
48 static bool SOSAccountIsPeerRetired(SOSAccountRef account, CFSetRef peers){
49 CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
50 bool result = false;
51
52 CFSetForEach(peers, ^(const void *value) {
53 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
54 if(SOSPeerInfoIsRetirementTicket(peer))
55 CFArrayAppendValue(peerIDs, peer);
56 });
57 if(CFArrayGetCount(peerIDs) > 0){
58 if(!SOSAccountRemoveBackupPeers(account, peerIDs, NULL))
59 secerror("Could not remove peers: %@, from the backup", peerIDs);
60 else
61 return true;
62 }
63 else
64 result = true;
65
66 return result;
67 }
68
69 static bool SOSAccountBackupSliceKeyBagNeedsFix(SOSAccountRef account, SOSBackupSliceKeyBagRef bskb) {
70
71 if (SOSBSKBIsDirect(bskb) || account->backup_key == NULL)
72 return false;
73
74 CFSetRef peers = SOSBSKBGetPeers(bskb);
75
76 /* first scan for retired peers, and kick'em out!*/
77 SOSAccountIsPeerRetired(account, peers);
78
79 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
80 bool needsFix = true;
81
82 if (myPeer) {
83 SOSPeerInfoRef meInBag = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
84 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
85 CFDataRef meInBagBK = SOSPeerInfoCopyBackupKey(meInBag);
86 needsFix = !(meInBag && CFEqualSafe(myBK,
87 meInBagBK));
88 CFReleaseNull(myBK);
89 CFReleaseNull(meInBagBK);
90 }
91
92 return needsFix;
93 }
94
95
96 bool SOSAccountHandleUpdateRing(SOSAccountRef account, SOSRingRef prospectiveRing, bool writeUpdate, CFErrorRef *error) {
97 bool success = true;
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));
104
105 secinfo("signing", "start:[%s] %@", localRemote, prospectiveRing);
106
107 require_action_quiet(!(writeUpdate && neverWrite), errOut, SOSCreateError(kSOSErrorNotReady, CFSTR("Can't update from local if FullPeerInfo not present"), NULL, error));
108
109 require_quiet(SOSAccountHasPublicKey(account, error), errOut);
110
111 require_action_quiet(prospectiveRing, errOut,
112 SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("No Ring to work with"), NULL, error));
113
114 // We should at least have a sane ring system in the account object
115 require_quiet(SOSAccountCheckForRings(account, error), errOut);
116
117 CFStringRef ringName = SOSRingGetName(prospectiveRing);
118 SOSRingRef oldRing = SOSAccountGetRing(account, ringName, NULL);
119
120 SOSTransportCircleRef transport = account->circle_transport;
121
122 // SOSAccountScanForRetired(account, prospectiveRing, error);
123
124 SOSRingRef newRing = CFRetainSafe(prospectiveRing); // TODO: SOSAccountCloneRingWithRetirement(account, prospectiveRing, error);
125
126 typedef enum {
127 accept,
128 countersign,
129 leave,
130 revert,
131 modify,
132 ignore
133 } ringAction_t;
134
135 static const char *actionstring[] = {
136 "accept", "countersign", "leave", "revert", "modify", "ignore",
137 };
138
139 ringAction_t ringAction = ignore;
140 enum DepartureReason leaveReason = kSOSNeverLeftCircle;
141
142 bool userTrustedoldRing = true;
143
144 SOSCircleRef circle = SOSAccountGetCircle(account, NULL);
145 CFSetRef peers = SOSCircleCopyPeers(circle, kCFAllocatorDefault);
146
147 SecKeyRef oldKey = account->user_public;
148
149 #if 0
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;
153
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;
157
158 #endif
159
160 if (!oldRing) {
161 oldRing = newRing;
162 }
163
164 SOSConcordanceStatus concstat = SOSRingConcordanceTrust(fpi, peers, oldRing, newRing, oldKey, account->user_public, peerID, error);
165 CFReleaseNull(peers);
166
167 CFStringRef concStr = NULL;
168 switch(concstat) {
169 case kSOSConcordanceTrusted:
170 ringAction = countersign;
171 concStr = CFSTR("Trusted");
172 break;
173 case kSOSConcordanceGenOld:
174 ringAction = userTrustedoldRing ? revert : ignore;
175 concStr = CFSTR("Generation Old");
176 break;
177 case kSOSConcordanceBadUserSig:
178 case kSOSConcordanceBadPeerSig:
179 ringAction = userTrustedoldRing ? revert : accept;
180 concStr = CFSTR("Bad Signature");
181 break;
182 case kSOSConcordanceNoUserSig:
183 ringAction = userTrustedoldRing ? revert : accept;
184 concStr = CFSTR("No User Signature");
185 break;
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);
190 break;
191 case kSOSConcordanceNoPeer:
192 ringAction = leave;
193 leaveReason = kSOSLeftUntrustedCircle;
194 concStr = CFSTR("No trusted peer left");
195 break;
196 case kSOSConcordanceNoUserKey:
197 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
198 ringAction = ignore;
199 break;
200
201 case kSOSConcordanceMissingMe:
202 case kSOSConcordanceImNotWorthy:
203 ringAction = modify;
204 concStr = CFSTR("Incorrect membership for me");
205 break;
206 case kSOSConcordanceInvalidMembership:
207 ringAction = userTrustedoldRing ? revert : ignore;
208 concStr = CFSTR("Invalid Ring Membership");
209 break;
210 default:
211 secerror("##### Bad Error Return from ConcordanceTrust");
212 ringAction = ignore;
213 break;
214 }
215
216 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring[ringAction], concordstring[concstat], userTrustedoldRing ? "trusted" : "untrusted");
217
218 SOSRingRef ringToPush = NULL;
219 bool iWasInOldRing = peerID && SOSRingHasPeerID(oldRing, peerID);
220 bool iAmInNewRing = peerID && SOSRingHasPeerID(newRing, peerID);
221 bool ringIsBackup = SOSRingGetType(newRing) == kSOSRingBackup;
222
223 if (ringIsBackup && !neverWrite) {
224 if (ringAction == accept || ringAction == countersign) {
225 CFErrorRef localError = NULL;
226 SOSBackupSliceKeyBagRef bskb = SOSRingCopyBackupSliceKeyBag(newRing, &localError);
227
228 if(!bskb) {
229 secnotice("signing", "Backup ring with no backup slice keybag (%@)", localError);
230 } else if (SOSAccountBackupSliceKeyBagNeedsFix(account, bskb)) {
231 ringAction = modify;
232 }
233 CFReleaseSafe(localError);
234 CFReleaseSafe(bskb);
235 }
236
237 if (ringAction == modify) {
238 CFErrorRef updateError = NULL;
239 CFDictionarySetValue(account->trusted_rings, ringName, newRing);
240
241 if(SOSAccountUpdateOurPeerInBackup(account, newRing, &updateError)) {
242 secdebug("signing", "Modified backup ring to include us");
243 } else {
244 secerror("Could not add ourselves to the backup: (%@)", updateError);
245 }
246 CFReleaseSafe(updateError);
247
248 // Fall through to normal modify handling.
249 }
250 }
251
252 if (ringAction == modify) {
253 ringAction = ignore;
254 }
255
256 if (ringAction == leave) {
257 if (iWasInOldRing) {
258 if (sosAccountLeaveRing(account, newRing, error)) {
259 ringToPush = newRing;
260 } else {
261 secnotice("signing", "Can't leave ring %@", oldRing);
262 success = false;
263 }
264 account->departure_code = leaveReason;
265 ringAction = accept;
266 } else {
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");
269 ringAction = accept;
270 }
271 }
272
273 if (ringAction == countersign) {
274 if (iAmInNewRing) {
275 if (SOSRingPeerTrusted(newRing, fpi, NULL)) {
276 secinfo("signing", "Already concur with: %@", newRing);
277 } else {
278 CFErrorRef signingError = NULL;
279
280 if (fpi && SOSRingConcordanceSign(newRing, fpi, &signingError)) {
281 ringToPush = newRing;
282 secinfo("signing", "Concurred with: %@", newRing);
283 } else {
284 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signingError, oldRing, newRing);
285 success = false;
286 }
287 CFReleaseSafe(signingError);
288 }
289 } else {
290 secnotice("signing", "Not countersigning, not in ring: %@", newRing);
291 }
292 ringAction = accept;
293 }
294
295 if (ringAction == accept) {
296 if (iWasInOldRing && !iAmInNewRing) {
297
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
301 }
302
303 if (pi && SOSRingHasRejection(newRing, peerID)) {
304 // TODO: ReasonForLeaving for rings
305 SOSRingRemoveRejection(newRing, peerID);
306 }
307
308 CFRetainSafe(oldRing);
309 CFDictionarySetValue(account->trusted_rings, ringName, newRing);
310 // TODO: Why was this? SOSAccountSetPreviousPublic(account);
311
312 secnotice("signing", "%@, Accepting ring: %@", concStr, newRing);
313
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.
321
322 if (SOSRingApply(newRing, account->user_public, fpi, NULL))
323 writeUpdate = true;
324 }
325
326 if (pi && SOSRingHasPeerID(oldRing, peerID)) {
327 SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
328 }
329
330 CFReleaseNull(oldRing);
331
332 account->circle_rings_retirements_need_attention = true;
333
334 if (writeUpdate && !neverWrite)
335 ringToPush = newRing;
336 SOSUpdateKeyInterest();
337 }
338
339 /*
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.
343 */
344
345 if (ringAction == revert) {
346 if(haveOldRing && !neverWrite && SOSRingHasPeerID(oldRing, peerID)) {
347 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr, newRing, oldRing);
348 ringToPush = oldRing;
349 } else {
350 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr, newRing);
351 }
352 }
353
354
355 if (ringToPush != NULL) {
356 secnotice("signing", "Pushing:[%s] %@", localRemote, ringToPush);
357 CFDataRef ringData = SOSRingCopyEncodedData(ringToPush, error);
358 if (ringData) {
359 success &= SOSTransportCircleRingPostRing(transport, SOSRingGetName(ringToPush), ringData, error);
360 } else {
361 success = false;
362 }
363 CFReleaseNull(ringData);
364 }
365
366 CFReleaseSafe(newRing);
367 return success;
368 errOut:
369 return false;
370 }