]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountUpdate.c
Security-57740.31.2.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 "SOSAccountLog.h"
8
9 #include <Security/SecureObjectSync/SOSAccountHSAJoin.h>
10 #include <Security/SecureObjectSync/SOSTransportCircle.h>
11 #include <Security/SecureObjectSync/SOSTransport.h>
12 #include <Security/SecureObjectSync/SOSViews.h>
13 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
14 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
15 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
16 #include <Security/SecureObjectSync/SOSPeerInfoDER.h>
17 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
18
19 static void DifferenceAndCall(CFSetRef old_members, CFSetRef new_members, void (^updatedCircle)(CFSetRef additions, CFSetRef removals))
20 {
21 CFMutableSetRef additions = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, new_members);
22 CFMutableSetRef removals = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, old_members);
23
24
25 CFSetForEach(old_members, ^(const void * value) {
26 CFSetRemoveValue(additions, value);
27 });
28
29 CFSetForEach(new_members, ^(const void * value) {
30 CFSetRemoveValue(removals, value);
31 });
32
33 updatedCircle(additions, removals);
34
35 CFReleaseSafe(additions);
36 CFReleaseSafe(removals);
37 }
38
39 static CFMutableSetRef SOSAccountCopyIntersectedViews(CFSetRef peerViews, CFSetRef myViews) {
40 __block CFMutableSetRef views = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
41 if (peerViews && myViews) CFSetForEach(peerViews, ^(const void *view) {
42 if (CFSetContainsValue(myViews, view)) {
43 CFSetAddValue(views, view);
44 }
45 });
46 return views;
47 }
48
49 static inline bool isSyncing(SOSPeerInfoRef peer, SecKeyRef upub) {
50 if(!SOSPeerInfoApplicationVerify(peer, upub, NULL)) return false;
51 if(SOSPeerInfoIsRetirementTicket(peer)) return false;
52 return true;
53 }
54
55 static bool isBackupSOSRing(SOSRingRef ring)
56 {
57 return isSOSRing(ring) && (kSOSRingBackup == SOSRingGetType(ring));
58 }
59
60 static void SOSAccountAppendPeerMetasForViewBackups(SOSAccountRef account, CFSetRef views, CFMutableArrayRef appendTo)
61 {
62 CFMutableDictionaryRef ringToViewTable = NULL;
63
64 require_quiet(SOSAccountIsInCircle(account, NULL), done);
65
66 require_action_quiet(SOSAccountHasCompletedRequiredBackupSync(account), done,
67 secnotice("backup", "Haven't finished initial backup syncing, not registering backup metas with engine"));
68
69 require_action_quiet(SOSPeerInfoV2DictionaryHasData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey), done,
70 secnotice("backup", "No key to backup to, we don't enable individual view backups"));
71
72 ringToViewTable = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
73
74 CFSetForEach(views, ^(const void *value) {
75 CFStringRef viewName = value;
76 if (isString(viewName) && !CFEqualSafe(viewName, kSOSViewKeychainV0)) {
77 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
78 viewName = ringName;
79 SOSRingRef ring = SOSAccountCopyRing(account, ringName, NULL);
80 if (ring && isBackupSOSRing(ring)) {
81 CFTypeRef currentValue = (CFTypeRef) CFDictionaryGetValue(ringToViewTable, ring);
82
83 if (isSet(currentValue)) {
84 CFSetAddValue((CFMutableSetRef)currentValue, viewName);
85 } else {
86 CFMutableSetRef viewNameSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
87 CFSetAddValue(viewNameSet, viewName);
88
89 CFDictionarySetValue(ringToViewTable, ring, viewNameSet);
90 CFReleaseNull(viewNameSet);
91 }
92 } else {
93 secwarning("View '%@' not being backed up – ring %@:%@ not backup ring.", viewName, ringName, ring);
94 }
95 CFReleaseNull(ringName);
96 CFReleaseNull(ring);
97 }
98 });
99
100 CFDictionaryForEach(ringToViewTable, ^(const void *key, const void *value) {
101 SOSRingRef ring = (SOSRingRef) key;
102 CFSetRef viewNames = asSet(value, NULL);
103 if (isSOSRing(ring) && viewNames) {
104 if (SOSAccountIntersectsWithOutstanding(account, viewNames)) {
105 CFStringSetPerformWithDescription(viewNames, ^(CFStringRef ringViews) {
106 secnotice("engine-notify", "Not ready, no peer meta: R: %@ Vs: %@", SOSRingGetName(ring), ringViews);
107 });
108 } else {
109 bool meta_added = false;
110 CFErrorRef create_error = NULL;
111 SOSBackupSliceKeyBagRef key_bag = NULL;
112 SOSPeerMetaRef newMeta = NULL;
113
114 CFDataRef ring_payload = SOSRingGetPayload(ring, NULL);
115 require_quiet(isData(ring_payload), skip);
116
117 key_bag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, ring_payload, &create_error);
118 require_quiet(key_bag, skip);
119
120 newMeta = SOSPeerMetaCreateWithComponents(SOSRingGetName(ring), viewNames, ring_payload);
121 require_quiet(SecAllocationError(newMeta, &create_error, CFSTR("Didn't make peer meta for: %@"), ring), skip);
122 CFArrayAppendValue(appendTo, newMeta);
123
124 CFStringSetPerformWithDescription(viewNames, ^(CFStringRef ringViews) {
125 secnotice("engine-notify", "Backup peer meta: R: %@ Vs: %@ VD: %@", SOSRingGetName(ring), ringViews, ring_payload);
126 });
127
128 meta_added = true;
129
130 skip:
131 if (!meta_added) {
132 CFStringSetPerformWithDescription(viewNames, ^(CFStringRef ringViews) {
133 secerror("Failed to register backup meta from %@ for views %@. Error (%@)", ring, ringViews, create_error);
134 });
135 }
136 CFReleaseNull(newMeta);
137 CFReleaseNull(key_bag);
138 CFReleaseNull(create_error);
139 }
140 }
141 });
142
143 done:
144 CFReleaseNull(ringToViewTable);
145 }
146
147 bool SOSAccountSyncingV0(SOSAccountRef account) {
148 __block bool syncingV0 = false;
149 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
150 if (SOSPeerInfoIsEnabledView(peer, kSOSViewKeychainV0)) {
151 syncingV0 = true;
152 }
153 });
154
155 return syncingV0;
156 }
157
158 void SOSAccountNotifyEngines(SOSAccountRef account)
159 {
160 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
161 CFStringRef myPi_id = SOSPeerInfoGetPeerID(myPi);
162 CFMutableArrayRef syncing_peer_metas = NULL;
163 CFMutableArrayRef zombie_peer_metas = NULL;
164 CFErrorRef localError = NULL;
165 SOSPeerMetaRef myMeta = NULL;
166
167 if (myPi_id && isSyncing(myPi, account->user_public) && SOSCircleHasPeer(account->trusted_circle, myPi, NULL)) {
168 CFMutableSetRef myViews = SOSPeerInfoCopyEnabledViews(myPi);
169
170 // We add V0 views to everyone if we see a V0 peer, or a peer with the view explicity enabled
171 // V2 peers shouldn't be explicity enabling the uber V0 view, though the seeds did.
172 __block bool addV0Views = SOSAccountSyncingV0(account);
173
174 syncing_peer_metas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
175 zombie_peer_metas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
176 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
177 CFMutableArrayRef arrayToAddTo = isSyncing(peer, account->user_public) ? syncing_peer_metas : zombie_peer_metas;
178
179 // Compute views each peer is in that we are also in ourselves
180 CFMutableSetRef peerEnabledViews = SOSPeerInfoCopyEnabledViews(peer);
181 CFMutableSetRef views = SOSAccountCopyIntersectedViews(peerEnabledViews, myViews);
182 CFReleaseNull(peerEnabledViews);
183
184 if(addV0Views) {
185 CFSetAddValue(views, kSOSViewKeychainV0);
186 }
187
188 CFStringSetPerformWithDescription(views, ^(CFStringRef viewsDescription) {
189 secnotice("engine-notify", "Meta: %@: %@", SOSPeerInfoGetPeerID(peer), viewsDescription);
190 });
191
192 SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(SOSPeerInfoGetPeerID(peer), views, NULL);
193 CFReleaseNull(views);
194
195 CFArrayAppendValue(arrayToAddTo, peerMeta);
196 CFReleaseNull(peerMeta);
197 });
198
199 // We don't make a backup peer meta for the magic V0 peer
200 // Set up all the rest before we munge the set
201 SOSAccountAppendPeerMetasForViewBackups(account, myViews, syncing_peer_metas);
202
203 // If we saw someone else needing V0, we sync V0, too!
204 if (addV0Views) {
205 CFSetAddValue(myViews, kSOSViewKeychainV0);
206 }
207
208 CFStringSetPerformWithDescription(myViews, ^(CFStringRef viewsDescription) {
209 secnotice("engine-notify", "My Meta: %@: %@", myPi_id, viewsDescription);
210 });
211 myMeta = SOSPeerMetaCreateWithComponents(myPi_id, myViews, NULL);
212 CFReleaseSafe(myViews);
213 }
214
215 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
216 if (engine) {
217 SOSEngineCircleChanged(engine, myMeta, syncing_peer_metas, zombie_peer_metas);
218 }
219
220 CFReleaseNull(myMeta);
221 CFReleaseSafe(localError);
222 CFReleaseNull(syncing_peer_metas);
223 CFReleaseNull(zombie_peer_metas);
224 }
225
226 // Upcoming call to View Changes Here
227 static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle)
228 {
229 account->circle_rings_retirements_need_attention = true;
230
231 CFMutableSetRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault);
232 CFMutableSetRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault);
233
234 CFMutableSetRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault);
235 CFMutableSetRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault);
236
237 SOSPeerInfoRef me = SOSAccountGetMyPeerInfo(account);
238 if(me && CFSetContainsValue(new_members, me))
239 SOSAccountSetValue(account, kSOSEscrowRecord, kCFNull, NULL); //removing the escrow records from the account object
240
241 DifferenceAndCall(old_members, new_members, ^(CFSetRef added_members, CFSetRef removed_members) {
242 DifferenceAndCall(old_applicants, new_applicants, ^(CFSetRef added_applicants, CFSetRef removed_applicants) {
243 CFArrayForEach(account->change_blocks, ^(const void * notificationBlock) {
244 secnotice("updates", "calling change block");
245 ((SOSAccountCircleMembershipChangeBlock) notificationBlock)(newCircle, added_members, removed_members, added_applicants, removed_applicants);
246 });
247 });
248 });
249
250 CFReleaseNull(old_applicants);
251 CFReleaseNull(new_applicants);
252
253 CFReleaseNull(old_members);
254 CFReleaseNull(new_members);
255 }
256
257 CF_RETURNS_RETAINED
258 CFDictionaryRef SOSAccountHandleRetirementMessages(SOSAccountRef account, CFDictionaryRef circle_retirement_messages, CFErrorRef *error) {
259 CFStringRef circle_name = SOSCircleGetName(account->trusted_circle);
260 CFMutableArrayRef handledRetirementIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
261 // We only handle one circle, look it up:
262
263 require_quiet(account->trusted_circle, finish); // We don't fail, we intentionally handle nothing.
264 CFDictionaryRef retirement_dictionary = asDictionary(CFDictionaryGetValue(circle_retirement_messages, circle_name), error);
265 require_quiet(retirement_dictionary, finish);
266 CFDictionaryForEach(retirement_dictionary, ^(const void *key, const void *value) {
267 if(isData(value)) {
268 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
269 if(pi && CFEqual(key, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) {
270 CFSetAddValue(account->retirees, pi);
271
272 account->circle_rings_retirements_need_attention = true; // Have to handle retirements.
273
274 CFArrayAppendValue(handledRetirementIDs, key);
275 }
276 CFReleaseNull(pi);
277 }
278 });
279
280 // If we are in the retiree list, we somehow got resurrected
281 // clearly we took care of proper departure before so leave
282 // and delcare that we withdrew this time.
283 SOSPeerInfoRef me = SOSAccountGetMyPeerInfo(account);
284 if (me && CFSetContainsValue(account->retirees, me)) {
285 SOSAccountPurgeIdentity(account);
286 account->departure_code = kSOSDiscoveredRetirement;
287 }
288
289 finish:
290 {
291 CFDictionaryRef result = (CFArrayGetCount(handledRetirementIDs) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL)
292 : CFDictionaryCreateForCFTypes(kCFAllocatorDefault, circle_name, handledRetirementIDs, NULL);
293
294 CFReleaseNull(handledRetirementIDs);
295 return result;
296 }
297 }
298
299 static SOSCircleRef SOSAccountCreateCircleFrom(CFStringRef circleName, CFTypeRef value, CFErrorRef *error) {
300 if (value && !isData(value) && !isNull(value)) {
301 secnotice("circleCreat", "Value provided not appropriate for a circle");
302 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(value));
303 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
304 CFSTR("Expected data or NULL got %@"), description);
305 CFReleaseSafe(description);
306 return NULL;
307 }
308
309 SOSCircleRef circle = NULL;
310 if (!value || isNull(value)) {
311 secnotice("circleCreat", "No circle found in data: %@", value);
312 circle = NULL;
313 } else {
314 circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error);
315 if (circle) {
316 CFStringRef name = SOSCircleGetName(circle);
317 if (!CFEqualSafe(name, circleName)) {
318 secnotice("circleCreat", "Expected circle named %@, got %@", circleName, name);
319 SOSCreateErrorWithFormat(kSOSErrorNameMismatch, NULL, error, NULL,
320 CFSTR("Expected circle named %@, got %@"), circleName, name);
321 CFReleaseNull(circle);
322 }
323 } else {
324 secnotice("circleCreat", "SOSCircleCreateFromData returned NULL.");
325 }
326 }
327 return circle;
328 }
329
330 bool SOSAccountHandleCircleMessage(SOSAccountRef account,
331 CFStringRef circleName, CFDataRef encodedCircleMessage, CFErrorRef *error) {
332 bool success = false;
333 CFErrorRef localError = NULL;
334 SOSCircleRef circle = SOSAccountCreateCircleFrom(circleName, encodedCircleMessage, &localError);
335 if (circle) {
336 success = SOSAccountUpdateCircleFromRemote(account, circle, &localError);
337 CFReleaseSafe(circle);
338 } else {
339 secerror("NULL circle found, ignoring ...");
340 success = true; // don't pend this NULL thing.
341 }
342
343 if (!success) {
344 if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) {
345 secerror("Incompatible circle found, abandoning membership: %@", circleName);
346 CFReleaseNull(account->my_identity);
347 CFReleaseNull(account->trusted_circle);
348 }
349
350 if (error) {
351 *error = localError;
352 localError = NULL;
353 }
354
355 }
356
357 CFReleaseNull(localError);
358
359 return success;
360 }
361
362 bool SOSAccountHandleParametersChange(SOSAccountRef account, CFDataRef parameters, CFErrorRef *error){
363
364 SecKeyRef newKey = NULL;
365 CFDataRef newParameters = NULL;
366 bool success = false;
367
368 if(SOSAccountRetrieveCloudParameters(account, &newKey, parameters, &newParameters, error)) {
369 debugDumpUserParameters(CFSTR("SOSAccountHandleParametersChange got new user key parameters:"), parameters);
370 secnotice("keygen", "SOSAccountHandleParametersChange got new public key: %@", newKey);
371
372 if (CFEqualSafe(account->user_public, newKey)) {
373 secnotice("updates", "Got same public key sent our way. Ignoring.");
374 success = true;
375 } else if (CFEqualSafe(account->previous_public, newKey)) {
376 secnotice("updates", "Got previous public key repeated. Ignoring.");
377 success = true;
378 } else {
379 SOSAccountSetUnTrustedUserPublicKey(account, newKey);
380 SOSAccountSetParameters(account, newParameters);
381 newKey = NULL;
382
383 if(SOSAccountRetryUserCredentials(account)) {
384 secnotice("keygen", "Successfully used cached password with new parameters");
385 SOSAccountGenerationSignatureUpdate(account, error);
386 } else {
387 SOSAccountPurgePrivateCredential(account);
388 secnotice("keygen", "Got new parameters for public key - could not find or use cached password");
389 }
390
391 account->circle_rings_retirements_need_attention = true;
392 account->key_interests_need_updating = true;
393
394 success = true;
395 }
396 }
397
398 CFReleaseNull(newKey);
399 CFReleaseNull(newParameters);
400
401 return success;
402 }
403
404 static inline bool SOSAccountHasLeft(SOSAccountRef account) {
405 switch(account->departure_code) {
406 case kSOSDiscoveredRetirement: /* Fallthrough */
407 case kSOSLostPrivateKey: /* Fallthrough */
408 case kSOSWithdrewMembership: /* Fallthrough */
409 case kSOSMembershipRevoked: /* Fallthrough */
410 case kSOSLeftUntrustedCircle:
411 return true;
412 case kSOSNeverAppliedToCircle: /* Fallthrough */
413 case kSOSNeverLeftCircle: /* Fallthrough */
414 default:
415 return false;
416 }
417 }
418
419 static const char *concordstring[] = {
420 "kSOSConcordanceTrusted",
421 "kSOSConcordanceGenOld", // kSOSErrorReplay
422 "kSOSConcordanceNoUserSig", // kSOSErrorBadSignature
423 "kSOSConcordanceNoUserKey", // kSOSErrorNoKey
424 "kSOSConcordanceNoPeer", // kSOSErrorPeerNotFound
425 "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature
426 "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature
427 "kSOSConcordanceNoPeerSig",
428 "kSOSConcordanceWeSigned",
429 };
430
431
432 bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef prospective_circle, bool writeUpdate, CFErrorRef *error)
433 {
434 bool success = true;
435 bool haveOldCircle = true;
436 const char *local_remote = writeUpdate ? "local": "remote";
437
438 secnotice("signing", "start:[%s]", local_remote);
439 if (!account->user_public || !account->user_public_trusted) {
440 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Can't handle updates with no trusted public key here"), NULL, error);
441 return false;
442 }
443
444 if (!prospective_circle) {
445 secerror("##### Can't update to a NULL circle ######");
446 return false; // Can't update one we don't have.
447 }
448
449 CFStringRef newCircleName = SOSCircleGetName(prospective_circle);
450 SOSCircleRef oldCircle = account->trusted_circle;
451 SOSCircleRef emptyCircle = NULL;
452
453 if(oldCircle == NULL) {
454 SOSCreateErrorWithFormat(kSOSErrorIncompatibleCircle, NULL, error, NULL, CFSTR("Current Entry is NULL; rejecting %@"), prospective_circle);
455 secerror("##### Can't replace circle - we don't care about it ######");
456 return false;
457 }
458 if (CFGetTypeID(oldCircle) != SOSCircleGetTypeID()) {
459 secdebug("signing", ">>>>>>>>>>>>>>> Non-Circle Circle found <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
460 // We don't know what is in our table, likely it was kCFNull indicating we didn't
461 // understand a circle that came by. We seem to like this one lets make our entry be empty circle
462 emptyCircle = SOSCircleCreate(kCFAllocatorDefault, newCircleName, NULL);
463 oldCircle = emptyCircle;
464 haveOldCircle = false;
465 // And we're paranoid, drop our old peer info if for some reason we didn't before.
466 // SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
467 }
468
469
470 SOSTransportCircleRef transport = account->circle_transport;
471
472 SOSAccountScanForRetired(account, prospective_circle, error);
473 SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error);
474 if(!newCircle) return false;
475
476 SOSFullPeerInfoRef me_full = account->my_identity;
477 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
478 CFStringRef myPeerID = SOSPeerInfoGetPeerID(me);
479 myPeerID = (myPeerID) ? myPeerID: CFSTR("No Peer");
480
481 if (me && SOSCircleUpdatePeerInfo(newCircle, me)) {
482 writeUpdate = true; // If we update our peer in the new circle we should write it if we accept it.
483 }
484
485 typedef enum {
486 accept,
487 countersign,
488 leave,
489 revert,
490 ignore
491 } circle_action_t;
492
493 static const char *actionstring[] = {
494 "accept", "countersign", "leave", "revert", "ignore",
495 };
496
497 circle_action_t circle_action = ignore;
498 enum DepartureReason leave_reason = kSOSNeverLeftCircle;
499
500 SecKeyRef old_circle_key = NULL;
501 if(SOSCircleVerify(oldCircle, account->user_public, NULL)) old_circle_key = account->user_public;
502 else if(account->previous_public && SOSCircleVerify(oldCircle, account->previous_public, NULL)) old_circle_key = account->previous_public;
503 bool userTrustedOldCircle = (old_circle_key != NULL) && haveOldCircle;
504
505 SOSConcordanceStatus concstat =
506 SOSCircleConcordanceTrust(oldCircle, newCircle,
507 old_circle_key, account->user_public,
508 me, error);
509
510 CFStringRef concStr = NULL;
511 switch(concstat) {
512 case kSOSConcordanceTrusted:
513 circle_action = countersign;
514 concStr = CFSTR("Trusted");
515 break;
516 case kSOSConcordanceGenOld:
517 circle_action = userTrustedOldCircle ? revert : ignore;
518 concStr = CFSTR("Generation Old");
519 break;
520 case kSOSConcordanceBadUserSig:
521 case kSOSConcordanceBadPeerSig:
522 circle_action = userTrustedOldCircle ? revert : accept;
523 concStr = CFSTR("Bad Signature");
524 break;
525 case kSOSConcordanceNoUserSig:
526 circle_action = userTrustedOldCircle ? revert : accept;
527 concStr = CFSTR("No User Signature");
528 break;
529 case kSOSConcordanceNoPeerSig:
530 circle_action = accept; // We might like this one eventually but don't countersign.
531 concStr = CFSTR("No trusted peer signature");
532 secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later");
533 break;
534 case kSOSConcordanceNoPeer:
535 circle_action = leave;
536 leave_reason = kSOSLeftUntrustedCircle;
537 concStr = CFSTR("No trusted peer left");
538 break;
539 case kSOSConcordanceNoUserKey:
540 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
541 abort();
542 break;
543 default:
544 secerror("##### Bad Error Return from ConcordanceTrust");
545 abort();
546 break;
547 }
548
549 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle. My PeerID is %@", actionstring[circle_action], concordstring[concstat], userTrustedOldCircle ? "trusted" : "untrusted", myPeerID);
550
551 SOSCircleRef circleToPush = NULL;
552
553 if (circle_action == leave) {
554 circle_action = ignore; (void) circle_action; // Acknowledge this is a dead store.
555
556 if (me && SOSCircleHasPeer(oldCircle, me, NULL)) {
557 secnotice("account", "Leaving circle with peer %@", me);
558 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
559 debugDumpCircle(CFSTR("newCircle"), newCircle);
560 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle);
561 secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@",
562 account->user_public, account->previous_public, old_circle_key);
563
564 if (sosAccountLeaveCircle(account, newCircle, error)) {
565 secnotice("leaveCircle", "Leaving circle by newcircle state");
566 circleToPush = newCircle;
567 } else {
568 secnotice("signing", "Can't leave circle, but dumping identities");
569 success = false;
570 }
571 account->departure_code = leave_reason;
572 circle_action = accept;
573 me = NULL;
574 me_full = NULL;
575 } else {
576 // We are not in this circle, but we need to update account with it, since we got it from cloud
577 secnotice("signing", "We are not in this circle, but we need to update account with it");
578 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
579 debugDumpCircle(CFSTR("newCircle"), newCircle);
580 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle);
581 circle_action = accept;
582 }
583 }
584
585 if (circle_action == countersign) {
586 if (me && SOSCircleHasPeer(newCircle, me, NULL)) {
587 if (SOSCircleVerifyPeerSigned(newCircle, me, NULL)) {
588 secnotice("signing", "Already concur with the new circle");
589 } else {
590 CFErrorRef signing_error = NULL;
591
592 if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) {
593 circleToPush = newCircle;
594 secnotice("signing", "Concurred with new circle");
595 } else {
596 secerror("Failed to concurrence sign, error: %@", signing_error);
597 success = false;
598 }
599 CFReleaseSafe(signing_error);
600 }
601
602 if(SOSAccountVerifyAndAcceptHSAApplicants(account, newCircle, error)) {
603 circleToPush = newCircle;
604 writeUpdate = true;
605 }
606 } else {
607 secnotice("signing", "Not countersigning, not in new circle");
608 debugDumpCircle(CFSTR("circle to countersign"), newCircle);
609 }
610 circle_action = accept;
611 }
612
613 if (circle_action == accept) {
614 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) {
615 // Don't destroy evidence of other code determining reason for leaving.
616 if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
617 secnotice("account", "Member of old circle but not of new circle");
618 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
619 debugDumpCircle(CFSTR("newCircle"), newCircle);
620 }
621
622 if (me
623 && SOSCircleHasActivePeer(oldCircle, me, NULL)
624 && !(SOSCircleCountPeers(oldCircle) == 1 && SOSCircleHasPeer(oldCircle, me, NULL)) // If it was our offering, don't change ID to avoid ghosts
625 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
626 secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
627 if (account->my_identity)
628 SOSFullPeerInfoPurgePersistentKey(account->my_identity, NULL);
629 CFReleaseNull(account->my_identity);
630 me = NULL;
631 me_full = NULL;
632 }
633
634 if (me && SOSCircleHasRejectedApplicant(newCircle, me, NULL)) {
635 SOSPeerInfoRef reject = SOSCircleCopyRejectedApplicant(newCircle, me, NULL);
636 if(CFEqualSafe(reject, me) && SOSPeerInfoApplicationVerify(me, account->user_public, NULL)) {
637 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
638 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
639 debugDumpCircle(CFSTR("newCircle"), newCircle);
640 if (account->my_identity)
641 SOSFullPeerInfoPurgePersistentKey(account->my_identity, NULL);
642 CFReleaseNull(account->my_identity);
643 me = NULL;
644 me_full = NULL;
645 } else {
646 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
647 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
648 debugDumpCircle(CFSTR("newCircle"), newCircle);
649 SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL);
650 writeUpdate = true;
651 }
652 }
653
654 CFRetainSafe(oldCircle);
655 CFRetainAssign(account->trusted_circle, newCircle);
656 SOSAccountSetPreviousPublic(account);
657
658 secnotice("signing", "%@, Accepting new circle", concStr);
659
660 if (me && account->user_public_trusted
661 && SOSCircleHasApplicant(oldCircle, me, NULL)
662 && SOSCircleCountPeers(newCircle) > 0
663 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
664 // We weren't rejected (above would have set me to NULL.
665 // We were applying and we weren't accepted.
666 // Our application is declared lost, let us reapply.
667
668 secnotice("signing", "requesting readmission to new circle");
669 if (SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL))
670 writeUpdate = true;
671 }
672
673 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
674 SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
675 }
676
677 SOSAccountNotifyOfChange(account, oldCircle, newCircle);
678
679 CFReleaseNull(oldCircle);
680
681 if (writeUpdate)
682 circleToPush = newCircle;
683 account->key_interests_need_updating = true;
684 }
685
686 /*
687 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles
688 * and pushing our current view of the circle (oldCircle). We'll only do this if we actually
689 * are a member of oldCircle - never for an empty circle.
690 */
691
692 if (circle_action == revert) {
693 if(haveOldCircle && me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
694 secnotice("signing", "%@, Rejecting new circle, re-publishing old circle", concStr);
695 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
696 debugDumpCircle(CFSTR("newCircle"), newCircle);
697 circleToPush = oldCircle;
698 } else {
699 secnotice("canary", "%@, Rejecting: new circle Have no old circle - would reset", concStr);
700 }
701 }
702
703
704 if (circleToPush != NULL) {
705 secnotice("signing", "Pushing:[%s]", local_remote);
706 CFDataRef circle_data = SOSCircleCopyEncodedData(circleToPush, kCFAllocatorDefault, error);
707
708 if (circle_data) {
709 // Ensure we flush changes
710 account->circle_rings_retirements_need_attention = true;
711
712 //recording circle we are pushing in KVS
713 success &= SOSTransportCircleRecordLastCirclePushedInKVS(transport, SOSCircleGetName(circleToPush), circle_data);
714 //posting new circle to peers
715 success &= SOSTransportCirclePostCircle(transport, SOSCircleGetName(circleToPush), circle_data, error);
716 } else {
717 success = false;
718 }
719 CFReleaseNull(circle_data);
720 }
721
722 CFReleaseSafe(newCircle);
723 CFReleaseNull(emptyCircle);
724
725 return success;
726 }