]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountUpdate.c
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountUpdate.c
1 //
2 // SOSAccountUpdate.c
3 // sec
4 //
5
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>
16
17 static void DifferenceAndCall(CFSetRef old_members, CFSetRef new_members, void (^updatedCircle)(CFSetRef additions, CFSetRef removals))
18 {
19 CFMutableSetRef additions = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, new_members);
20 CFMutableSetRef removals = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, old_members);
21
22
23 CFSetForEach(old_members, ^(const void * value) {
24 CFSetRemoveValue(additions, value);
25 });
26
27 CFSetForEach(new_members, ^(const void * value) {
28 CFSetRemoveValue(removals, value);
29 });
30
31 updatedCircle(additions, removals);
32
33 CFReleaseSafe(additions);
34 CFReleaseSafe(removals);
35 }
36
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);
42 }
43 });
44 return views;
45 }
46
47 static inline bool isSyncing(SOSPeerInfoRef peer, SecKeyRef upub) {
48 if(!SOSPeerInfoApplicationVerify(peer, upub, NULL)) return false;
49 if(SOSPeerInfoIsRetirementTicket(peer)) return false;
50 return true;
51 }
52
53 static bool isBackupSOSRing(SOSRingRef ring)
54 {
55 return isSOSRing(ring) && (kSOSRingBackup == SOSRingGetType(ring));
56 }
57
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;
63 };
64 });
65 return !intersectionEmpty;
66 }
67
68 __unused
69 static void SOSAccountAppendPeerMetasForViewBackups(SOSAccountRef account, CFSetRef views, CFMutableArrayRef appendTo)
70 {
71 if (account->trusted_rings == NULL || CFDictionaryGetCount(account->trusted_rings) == 0) return;
72
73 CFMutableDictionaryRef ringToViewTable = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
74
75 CFSetForEach(views, ^(const void *value) {
76 CFStringRef viewName = value;
77 if (isString(viewName) && !CFEqualSafe(viewName, kSOSViewKeychainV0)) {
78 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
79 viewName = ringName;
80 SOSRingRef ring = (SOSRingRef) CFDictionaryGetValue(account->trusted_rings, ringName);
81
82 if (isBackupSOSRing(ring)) {
83 CFTypeRef currentValue = (CFTypeRef) CFDictionaryGetValue(ringToViewTable, ring);
84
85 if (isSet(currentValue)) {
86 CFSetAddValue((CFMutableSetRef)currentValue, viewName);
87 } else {
88 CFMutableSetRef viewNameSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
89 CFSetAddValue(viewNameSet, viewName);
90
91 CFDictionarySetValue(ringToViewTable, ring, viewNameSet);
92 CFReleaseNull(viewNameSet);
93 }
94 } else {
95 secwarning("View '%@' not being backed up – ring %@:%@ not backup ring.", viewName, ringName, ring);
96 }
97 CFReleaseNull(ringName);
98 }
99 });
100
101 CFSetRef unsynced = asSet(SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL), NULL);
102
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);
109 } else {
110 bool meta_added = false;
111 CFErrorRef create_error = NULL;
112 SOSBackupSliceKeyBagRef key_bag = NULL;
113 SOSPeerMetaRef newMeta = NULL;
114
115 CFDataRef ring_payload = SOSRingGetPayload(ring, NULL);
116 require_quiet(isData(ring_payload), skip);
117
118 key_bag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, ring_payload, &create_error);
119 require_quiet(key_bag, skip);
120
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);
124
125 secnotice("engine-notify", "Backup peer meta: R: %@ Vs: %@ VD: %@", SOSRingGetName(ring), viewNames, ring_payload);
126
127 meta_added = true;
128
129 skip:
130 if (!meta_added) {
131 secerror("Failed to register backup meta from %@ for views %@. Error (%@)", ring, viewNames, create_error);
132 }
133 CFReleaseNull(newMeta);
134 CFReleaseNull(key_bag);
135 CFReleaseNull(create_error);
136 }
137 }
138 });
139
140 CFReleaseNull(ringToViewTable);
141 }
142
143 bool SOSAccountSyncingV0(SOSAccountRef account) {
144 __block bool syncingV0 = false;
145 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
146 if (SOSPeerInfoIsEnabledView(peer, kSOSViewKeychainV0)) {
147 syncingV0 = true;
148 }
149 });
150
151 return syncingV0;
152 }
153
154 void SOSAccountNotifyEngines(SOSAccountRef account)
155 {
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;
162
163 if (myPi_id && isSyncing(myPi, account->user_public) && SOSCircleHasPeer(account->trusted_circle, myPi, NULL)) {
164 CFMutableSetRef myViews = SOSPeerInfoCopyEnabledViews(myPi);
165
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);
169
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;
174
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);
179
180 if(addV0Views) {
181 CFSetAddValue(views, kSOSViewKeychainV0);
182 }
183
184 SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(SOSPeerInfoGetPeerID(peer), views, NULL);
185 CFReleaseNull(views);
186
187 CFArrayAppendValue(arrayToAddTo, peerMeta);
188 CFReleaseNull(peerMeta);
189 });
190
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);
193
194 // If we saw someone else needing V0, we sync V0, too!
195 if (addV0Views) {
196 CFSetAddValue(myViews, kSOSViewKeychainV0);
197 }
198
199 myMeta = SOSPeerMetaCreateWithComponents(myPi_id, myViews, NULL);
200 CFReleaseSafe(myViews);
201 }
202
203 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
204 if (engine) {
205 SOSEngineCircleChanged(engine, myMeta, syncing_peer_metas, zombie_peer_metas);
206 }
207
208 CFReleaseNull(myMeta);
209 CFReleaseSafe(localError);
210 CFReleaseNull(syncing_peer_metas);
211 CFReleaseNull(zombie_peer_metas);
212 }
213
214 // murf Upcoming call to View Changes Here
215 static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle)
216 {
217 account->circle_rings_retirements_need_attention = true;
218
219 CFMutableSetRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault);
220 CFMutableSetRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault);
221
222 CFMutableSetRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault);
223 CFMutableSetRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault);
224
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
228
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);
234 });
235 });
236 });
237
238 CFReleaseNull(old_applicants);
239 CFReleaseNull(new_applicants);
240
241 CFReleaseNull(old_members);
242 CFReleaseNull(new_members);
243 }
244
245 CF_RETURNS_RETAINED
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:
250
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);
253
254 CFDictionaryForEach(retirment_dictionary, ^(const void *key, const void *value) {
255 if(isData(value)) {
256 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
257 if(pi && CFEqual(key, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) {
258 CFSetAddValue(account->retirees, pi);
259
260 account->circle_rings_retirements_need_attention = true; // Have to handle retirements.
261
262 CFArrayAppendValue(handledRetirementIDs, key);
263 }
264 CFReleaseNull(pi);
265 }
266 });
267
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;
275 }
276
277 finish:
278 {
279 CFDictionaryRef result = (CFArrayGetCount(handledRetirementIDs) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL)
280 : CFDictionaryCreateForCFTypes(kCFAllocatorDefault, circle_name, handledRetirementIDs, NULL);
281
282 CFReleaseNull(handledRetirementIDs);
283 return result;
284 }
285 }
286
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);
294 return NULL;
295 }
296
297 SOSCircleRef circle = NULL;
298 if (!value || isNull(value)) {
299 secnotice("circleCreat", "No circle found in data: %@", value);
300 circle = NULL;
301 } else {
302 circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error);
303 if (circle) {
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);
310 }
311 } else {
312 secnotice("circleCreat", "SOSCircleCreateFromData returned NULL.");
313 }
314 }
315 return circle;
316 }
317
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);
323 if (circle) {
324 success = SOSAccountUpdateCircleFromRemote(account, circle, &localError);
325 CFReleaseSafe(circle);
326 } else {
327 secerror("NULL circle found, ignoring ...");
328 success = true; // don't pend this NULL thing.
329 }
330
331 if (!success) {
332 if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) {
333 secerror("Incompatible circle found, abandoning membership: %@", circleName);
334 CFReleaseNull(account->my_identity);
335 CFReleaseNull(account->trusted_circle);
336 }
337
338 if (error) {
339 *error = localError;
340 localError = NULL;
341 }
342
343 }
344
345 CFReleaseNull(localError);
346
347 return success;
348 }
349
350 bool SOSAccountHandleParametersChange(SOSAccountRef account, CFDataRef parameters, CFErrorRef *error){
351
352 SecKeyRef newKey = NULL;
353 CFDataRef newParameters = NULL;
354 bool success = false;
355
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.");
359 success = true;
360 } else if (CFEqualSafe(account->previous_public, newKey)) {
361 secnotice("updates", "Got previous public key repeated. Ignoring.");
362 success = true;
363 } else {
364 SOSAccountSetUnTrustedUserPublicKey(account, newKey);
365 SOSAccountSetParameters(account, newParameters);
366 newKey = NULL;
367
368 if(SOSAccountRetryUserCredentials(account)) {
369 secnotice("keygen", "Successfully used cached password with new parameters: %@", account->user_public);
370 SOSAccountGenerationSignatureUpdate(account, error);
371 } else {
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);
375 }
376
377 account->circle_rings_retirements_need_attention = true;
378 SOSUpdateKeyInterest(account);
379
380 success = true;
381 }
382 }
383
384 CFReleaseNull(newKey);
385 CFReleaseNull(newParameters);
386
387 return success;
388 }
389
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:
397 return true;
398 case kSOSNeverAppliedToCircle: /* Fallthrough */
399 case kSOSNeverLeftCircle: /* Fallthrough */
400 default:
401 return false;
402 }
403 }
404
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",
415 };
416
417 bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef prospective_circle, bool writeUpdate, CFErrorRef *error)
418 {
419 bool success = true;
420 bool haveOldCircle = true;
421 const char *local_remote = writeUpdate ? "local": "remote";
422
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);
426 return false;
427 }
428
429 if (!prospective_circle) {
430 secerror("##### Can't update to a NULL circle ######");
431 return false; // Can't update one we don't have.
432 }
433
434 CFStringRef newCircleName = SOSCircleGetName(prospective_circle);
435 SOSCircleRef oldCircle = account->trusted_circle;
436 SOSCircleRef emptyCircle = NULL;
437
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);
441 return false;
442 }
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);
452 }
453
454 SOSFullPeerInfoRef me_full = account->my_identity;
455 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
456
457 SOSTransportCircleRef transport = account->circle_transport;
458
459 SOSAccountScanForRetired(account, prospective_circle, error);
460 SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error);
461 if(!newCircle) return false;
462
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.
465 }
466
467 typedef enum {
468 accept,
469 countersign,
470 leave,
471 revert,
472 ignore
473 } circle_action_t;
474
475 static const char *actionstring[] = {
476 "accept", "countersign", "leave", "revert", "ignore",
477 };
478
479 circle_action_t circle_action = ignore;
480 enum DepartureReason leave_reason = kSOSNeverLeftCircle;
481
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;
486
487 SOSConcordanceStatus concstat =
488 SOSCircleConcordanceTrust(oldCircle, newCircle,
489 old_circle_key, account->user_public,
490 me, error);
491
492 CFStringRef concStr = NULL;
493 switch(concstat) {
494 case kSOSConcordanceTrusted:
495 circle_action = countersign;
496 concStr = CFSTR("Trusted");
497 break;
498 case kSOSConcordanceGenOld:
499 circle_action = userTrustedOldCircle ? revert : ignore;
500 concStr = CFSTR("Generation Old");
501 break;
502 case kSOSConcordanceBadUserSig:
503 case kSOSConcordanceBadPeerSig:
504 circle_action = userTrustedOldCircle ? revert : accept;
505 concStr = CFSTR("Bad Signature");
506 break;
507 case kSOSConcordanceNoUserSig:
508 circle_action = userTrustedOldCircle ? revert : accept;
509 concStr = CFSTR("No User Signature");
510 break;
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);
515 break;
516 case kSOSConcordanceNoPeer:
517 circle_action = leave;
518 leave_reason = kSOSLeftUntrustedCircle;
519 concStr = CFSTR("No trusted peer left");
520 break;
521 case kSOSConcordanceNoUserKey:
522 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
523 abort();
524 break;
525 default:
526 secerror("##### Bad Error Return from ConcordanceTrust");
527 abort();
528 break;
529 }
530
531 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring[circle_action], concordstring[concstat], userTrustedOldCircle ? "trusted" : "untrusted");
532
533 SOSCircleRef circleToPush = NULL;
534
535 if (circle_action == leave) {
536 circle_action = ignore; (void) circle_action; // Acknowledge this is a dead store.
537
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);
545
546 if (sosAccountLeaveCircle(account, newCircle, error)) {
547 circleToPush = newCircle;
548 } else {
549 secnotice("signing", "Can't leave circle %@, but dumping identities", oldCircle);
550 success = false;
551 }
552 account->departure_code = leave_reason;
553 circle_action = accept;
554 me = NULL;
555 me_full = NULL;
556 } else {
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;
563 }
564 }
565
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);
570 } else {
571 CFErrorRef signing_error = NULL;
572
573 if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) {
574 circleToPush = newCircle;
575 secnotice("signing", "Concurred with: %@", newCircle);
576 } else {
577 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error, oldCircle, newCircle);
578 success = false;
579 }
580 CFReleaseSafe(signing_error);
581 }
582
583 if(SOSAccountVerifyAndAcceptHSAApplicants(account, newCircle, error)) {
584 circleToPush = newCircle;
585 writeUpdate = true;
586 }
587 } else {
588 secnotice("signing", "Not countersigning, not in circle: %@", newCircle);
589 debugDumpCircle(CFSTR("circle to countersign"), newCircle);
590 }
591 circle_action = accept;
592 }
593
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);
601 }
602
603 if (me
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);
611 me = NULL;
612 me_full = NULL;
613 }
614
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);
624 me = NULL;
625 me_full = NULL;
626 } else {
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);
631 writeUpdate = true;
632 }
633 }
634
635 CFRetainSafe(oldCircle);
636 CFRetainAssign(account->trusted_circle, newCircle);
637 SOSAccountSetPreviousPublic(account);
638
639 secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle);
640
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.
648
649 secnotice("signing", "requesting readmission to circle %@", newCircle);
650 if (SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL))
651 writeUpdate = true;
652 }
653
654 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
655 SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
656 }
657
658 SOSAccountNotifyOfChange(account, oldCircle, newCircle);
659
660 CFReleaseNull(oldCircle);
661
662 if (writeUpdate)
663 circleToPush = newCircle;
664 SOSUpdateKeyInterest(account);
665 }
666
667 /*
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.
671 */
672
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;
679 } else {
680 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr, newCircle);
681 }
682 }
683
684
685 if (circleToPush != NULL) {
686 secnotice("signing", "Pushing:[%s] %@", local_remote, circleToPush);
687 CFDataRef circle_data = SOSCircleCopyEncodedData(circleToPush, kCFAllocatorDefault, error);
688
689 if (circle_data) {
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);
694 } else {
695 success = false;
696 }
697 CFReleaseNull(circle_data);
698 }
699
700 CFReleaseSafe(newCircle);
701 CFReleaseNull(emptyCircle);
702 return success;
703 }