]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountUpdate.c
Security-57336.1.9.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 #if ENABLE_V2_BACKUP
192 // We don't make a backup peer for the magic V0 peer, so do it before we munge the set.
193 SOSAccountAppendPeerMetasForViewBackups(account, myViews, syncing_peer_metas);
194 #endif
195
196 // If we saw someone else needing V0, we sync V0, too!
197 if (addV0Views) {
198 CFSetAddValue(myViews, kSOSViewKeychainV0);
199 }
200
201 myMeta = SOSPeerMetaCreateWithComponents(myPi_id, myViews, NULL);
202 CFReleaseSafe(myViews);
203 }
204
205 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
206 if (engine) {
207 SOSEngineCircleChanged(engine, myMeta, syncing_peer_metas, zombie_peer_metas);
208 }
209
210 CFReleaseNull(myMeta);
211 CFReleaseSafe(localError);
212 CFReleaseNull(syncing_peer_metas);
213 CFReleaseNull(zombie_peer_metas);
214 }
215
216 // murf Upcoming call to View Changes Here
217 static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle)
218 {
219 account->circle_rings_retirements_need_attention = true;
220
221 CFMutableSetRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault);
222 CFMutableSetRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault);
223
224 CFMutableSetRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault);
225 CFMutableSetRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault);
226
227 DifferenceAndCall(old_members, new_members, ^(CFSetRef added_members, CFSetRef removed_members) {
228 DifferenceAndCall(old_applicants, new_applicants, ^(CFSetRef added_applicants, CFSetRef removed_applicants) {
229 CFArrayForEach(account->change_blocks, ^(const void * notificationBlock) {
230 secnotice("updates", "calling change block");
231 ((SOSAccountCircleMembershipChangeBlock) notificationBlock)(newCircle, added_members, removed_members, added_applicants, removed_applicants);
232 });
233 });
234 });
235
236 CFReleaseNull(old_applicants);
237 CFReleaseNull(new_applicants);
238
239 CFReleaseNull(old_members);
240 CFReleaseNull(new_members);
241 }
242
243 static void SOSAccountRecordRetiredPeerInCircle(SOSAccountRef account, SOSPeerInfoRef retiree)
244 {
245 // Replace Peer with RetiredPeer, if were a peer.
246 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
247 bool updated = SOSCircleUpdatePeerInfo(circle, retiree);
248 if (updated) {
249 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
250 CFErrorRef cleanupError = NULL;
251 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
252 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
253 CFReleaseSafe(cleanupError);
254 }
255 return updated;
256 });
257 }
258
259 CF_RETURNS_RETAINED
260 CFDictionaryRef SOSAccountHandleRetirementMessages(SOSAccountRef account, CFDictionaryRef circle_retirement_messages, CFErrorRef *error) {
261 CFStringRef circle_name = SOSCircleGetName(account->trusted_circle);
262 CFMutableArrayRef handledRetirementIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
263 // We only handle one circle, look it up:
264
265 require_quiet(account->trusted_circle, finish); // We don't fail, we intentionally handle nothing.
266 CFDictionaryRef retirment_dictionary = CFDictionaryGetValue(circle_retirement_messages, circle_name);
267
268 CFDictionaryForEach(retirment_dictionary, ^(const void *key, const void *value) {
269 if(isData(value)) {
270 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
271 if(pi && CFEqual(key, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) {
272 CFSetAddValue(account->retirees, pi);
273
274 SOSAccountRecordRetiredPeerInCircle(account, pi);
275
276 CFArrayAppendValue(handledRetirementIDs, key);
277 }
278 CFReleaseNull(pi);
279 }
280 });
281
282 // If we are in the retiree list, we somehow got resurrected
283 // clearly we took care of proper departure before so leave
284 // and delcare that we withdrew this time.
285 SOSPeerInfoRef me = SOSAccountGetMyPeerInfo(account);
286 if (me && CFSetContainsValue(account->retirees, me)) {
287 SOSAccountPurgeIdentity(account);
288 account->departure_code = kSOSDiscoveredRetirement;
289 }
290
291 finish:
292 {
293 CFDictionaryRef result = (CFArrayGetCount(handledRetirementIDs) == 0) ? CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL)
294 : CFDictionaryCreateForCFTypes(kCFAllocatorDefault, circle_name, handledRetirementIDs, NULL);
295
296 CFReleaseNull(handledRetirementIDs);
297 return result;
298 }
299 }
300
301 static SOSCircleRef SOSAccountCreateCircleFrom(CFStringRef circleName, CFTypeRef value, CFErrorRef *error) {
302 if (value && !isData(value) && !isNull(value)) {
303 secnotice("circleCreat", "Value provided not appropriate for a circle");
304 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(value));
305 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
306 CFSTR("Expected data or NULL got %@"), description);
307 CFReleaseSafe(description);
308 return NULL;
309 }
310
311 SOSCircleRef circle = NULL;
312 if (!value || isNull(value)) {
313 secnotice("circleCreat", "No circle found in data: %@", value);
314 circle = NULL;
315 } else {
316 circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error);
317 if (circle) {
318 CFStringRef name = SOSCircleGetName(circle);
319 if (!CFEqualSafe(name, circleName)) {
320 secnotice("circleCreat", "Expected circle named %@, got %@", circleName, name);
321 SOSCreateErrorWithFormat(kSOSErrorNameMismatch, NULL, error, NULL,
322 CFSTR("Expected circle named %@, got %@"), circleName, name);
323 CFReleaseNull(circle);
324 }
325 } else {
326 secnotice("circleCreat", "SOSCircleCreateFromData returned NULL.");
327 }
328 }
329 return circle;
330 }
331
332 bool SOSAccountHandleCircleMessage(SOSAccountRef account,
333 CFStringRef circleName, CFDataRef encodedCircleMessage, CFErrorRef *error) {
334 bool success = false;
335 CFErrorRef localError = NULL;
336 SOSCircleRef circle = SOSAccountCreateCircleFrom(circleName, encodedCircleMessage, &localError);
337 if (circle) {
338 success = SOSAccountUpdateCircleFromRemote(account, circle, &localError);
339 CFReleaseSafe(circle);
340 } else {
341 secerror("NULL circle found, ignoring ...");
342 success = true; // don't pend this NULL thing.
343 }
344
345 if (!success) {
346 if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) {
347 secerror("Incompatible circle found, abandoning membership: %@", circleName);
348 CFReleaseNull(account->my_identity);
349 CFReleaseNull(account->trusted_circle);
350 }
351
352 if (error) {
353 *error = localError;
354 localError = NULL;
355 }
356
357 }
358
359 CFReleaseNull(localError);
360
361 return success;
362 }
363
364 bool SOSAccountHandleParametersChange(SOSAccountRef account, CFDataRef parameters, CFErrorRef *error){
365
366 SecKeyRef newKey = NULL;
367 CFDataRef newParameters = NULL;
368 bool success = false;
369
370 if(SOSAccountRetrieveCloudParameters(account, &newKey, parameters, &newParameters, error)) {
371 if (CFEqualSafe(account->user_public, newKey)) {
372 secnotice("updates", "Got same public key sent our way. Ignoring.");
373 success = true;
374 } else if (CFEqualSafe(account->previous_public, newKey)) {
375 secnotice("updates", "Got previous public key repeated. Ignoring.");
376 success = true;
377 } else {
378 SOSAccountSetUnTrustedUserPublicKey(account, newKey);
379 CFReleaseNull(account->user_key_parameters);
380 account->user_key_parameters = newParameters;
381 newKey = NULL;
382 newParameters = NULL;
383
384 if(SOSAccountRetryUserCredentials(account)) {
385 secnotice("keygen", "Successfully used cached password with new parameters: %@", account->user_public);
386 SOSAccountGenerationSignatureUpdate(account, error);
387 } else {
388 SOSAccountPurgePrivateCredential(account);
389 secnotice("keygen", "Got new parameters for public key - failed with cached password: %@", account->user_public);
390 debugDumpUserParameters(CFSTR("params"), account->user_key_parameters);
391 }
392
393 SOSUpdateKeyInterest();
394
395 success = true;
396 }
397 }
398
399 CFReleaseNull(newKey);
400 CFReleaseNull(newParameters);
401
402 return success;
403 }
404
405 static inline bool SOSAccountHasLeft(SOSAccountRef account) {
406 switch(account->departure_code) {
407 case kSOSDiscoveredRetirement: /* Fallthrough */
408 case kSOSLostPrivateKey: /* Fallthrough */
409 case kSOSWithdrewMembership: /* Fallthrough */
410 case kSOSMembershipRevoked: /* Fallthrough */
411 case kSOSLeftUntrustedCircle:
412 return true;
413 case kSOSNeverAppliedToCircle: /* Fallthrough */
414 case kSOSNeverLeftCircle: /* Fallthrough */
415 default:
416 return false;
417 }
418 }
419
420 static const char *concordstring[] = {
421 "kSOSConcordanceTrusted",
422 "kSOSConcordanceGenOld", // kSOSErrorReplay
423 "kSOSConcordanceNoUserSig", // kSOSErrorBadSignature
424 "kSOSConcordanceNoUserKey", // kSOSErrorNoKey
425 "kSOSConcordanceNoPeer", // kSOSErrorPeerNotFound
426 "kSOSConcordanceBadUserSig", // kSOSErrorBadSignature
427 "kSOSConcordanceBadPeerSig", // kSOSErrorBadSignature
428 "kSOSConcordanceNoPeerSig",
429 "kSOSConcordanceWeSigned",
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, prospective_circle);
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 %@ ######", prospective_circle);
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 SOSFullPeerInfoRef me_full = account->my_identity;
470 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
471
472 SOSTransportCircleRef transport = account->circle_transport;
473
474 SOSAccountScanForRetired(account, prospective_circle, error);
475 SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error);
476 if(!newCircle) return false;
477
478 if (me && SOSCircleUpdatePeerInfo(newCircle, me)) {
479 writeUpdate = true; // If we update our peer in the new circle we should write it if we accept it.
480 }
481
482 typedef enum {
483 accept,
484 countersign,
485 leave,
486 revert,
487 ignore
488 } circle_action_t;
489
490 static const char *actionstring[] = {
491 "accept", "countersign", "leave", "revert", "ignore",
492 };
493
494 circle_action_t circle_action = ignore;
495 enum DepartureReason leave_reason = kSOSNeverLeftCircle;
496
497 SecKeyRef old_circle_key = NULL;
498 if(SOSCircleVerify(oldCircle, account->user_public, NULL)) old_circle_key = account->user_public;
499 else if(account->previous_public && SOSCircleVerify(oldCircle, account->previous_public, NULL)) old_circle_key = account->previous_public;
500 bool userTrustedOldCircle = (old_circle_key != NULL) && haveOldCircle;
501
502 SOSConcordanceStatus concstat =
503 SOSCircleConcordanceTrust(oldCircle, newCircle,
504 old_circle_key, account->user_public,
505 me, error);
506
507 CFStringRef concStr = NULL;
508 switch(concstat) {
509 case kSOSConcordanceTrusted:
510 circle_action = countersign;
511 concStr = CFSTR("Trusted");
512 break;
513 case kSOSConcordanceGenOld:
514 circle_action = userTrustedOldCircle ? revert : ignore;
515 concStr = CFSTR("Generation Old");
516 break;
517 case kSOSConcordanceBadUserSig:
518 case kSOSConcordanceBadPeerSig:
519 circle_action = userTrustedOldCircle ? revert : accept;
520 concStr = CFSTR("Bad Signature");
521 break;
522 case kSOSConcordanceNoUserSig:
523 circle_action = userTrustedOldCircle ? revert : accept;
524 concStr = CFSTR("No User Signature");
525 break;
526 case kSOSConcordanceNoPeerSig:
527 circle_action = accept; // We might like this one eventually but don't countersign.
528 concStr = CFSTR("No trusted peer signature");
529 secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle);
530 break;
531 case kSOSConcordanceNoPeer:
532 circle_action = leave;
533 leave_reason = kSOSLeftUntrustedCircle;
534 concStr = CFSTR("No trusted peer left");
535 break;
536 case kSOSConcordanceNoUserKey:
537 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
538 abort();
539 break;
540 default:
541 secerror("##### Bad Error Return from ConcordanceTrust");
542 abort();
543 break;
544 }
545
546 secnotice("signing", "Decided on action [%s] based on concordance state [%s] and [%s] circle.", actionstring[circle_action], concordstring[concstat], userTrustedOldCircle ? "trusted" : "untrusted");
547
548 SOSCircleRef circleToPush = NULL;
549
550 if (circle_action == leave) {
551 circle_action = ignore; (void) circle_action; // Acknowledge this is a dead store.
552
553 if (me && SOSCircleHasPeer(oldCircle, me, NULL)) {
554 secnotice("account", "Leaving circle with peer %@", me);
555 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
556 debugDumpCircle(CFSTR("newCircle"), newCircle);
557 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle);
558 secnotice("account", "Key state: user_public %@, previous_public %@, old_circle_key %@",
559 account->user_public, account->previous_public, old_circle_key);
560
561 if (sosAccountLeaveCircle(account, newCircle, error)) {
562 circleToPush = newCircle;
563 } else {
564 secnotice("signing", "Can't leave circle %@, but dumping identities", oldCircle);
565 success = false;
566 }
567 account->departure_code = leave_reason;
568 circle_action = accept;
569 me = NULL;
570 me_full = NULL;
571 } else {
572 // We are not in this circle, but we need to update account with it, since we got it from cloud
573 secnotice("signing", "We are not in this circle, but we need to update account with it");
574 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
575 debugDumpCircle(CFSTR("newCircle"), newCircle);
576 debugDumpCircle(CFSTR("prospective_circle"), prospective_circle);
577 circle_action = accept;
578 }
579 }
580
581 if (circle_action == countersign) {
582 if (me && SOSCircleHasPeer(newCircle, me, NULL)) {
583 if (SOSCircleVerifyPeerSigned(newCircle, me, NULL)) {
584 secnotice("signing", "Already concur with: %@", newCircle);
585 } else {
586 CFErrorRef signing_error = NULL;
587
588 if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) {
589 circleToPush = newCircle;
590 secnotice("signing", "Concurred with: %@", newCircle);
591 } else {
592 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error, oldCircle, newCircle);
593 success = false;
594 }
595 CFReleaseSafe(signing_error);
596 }
597
598 if(SOSAccountVerifyAndAcceptHSAApplicants(account, newCircle, error)) {
599 circleToPush = newCircle;
600 writeUpdate = true;
601 }
602 } else {
603 secnotice("signing", "Not countersigning, not in circle: %@", newCircle);
604 debugDumpCircle(CFSTR("circle to countersign"), newCircle);
605 }
606 circle_action = accept;
607 }
608
609 if (circle_action == accept) {
610 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) {
611 // Don't destroy evidence of other code determining reason for leaving.
612 if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
613 secnotice("account", "Member of old circle but not of new circle");
614 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
615 debugDumpCircle(CFSTR("newCircle"), newCircle);
616 }
617
618 if (me
619 && SOSCircleHasActivePeer(oldCircle, me, NULL)
620 && !(SOSCircleCountPeers(oldCircle) == 1 && SOSCircleHasPeer(oldCircle, me, NULL)) // If it was our offering, don't change ID to avoid ghosts
621 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
622 secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
623 if (account->my_identity)
624 SOSFullPeerInfoPurgePersistentKey(account->my_identity, NULL);
625 CFReleaseNull(account->my_identity);
626 me = NULL;
627 me_full = NULL;
628 }
629
630 if (me && SOSCircleHasRejectedApplicant(newCircle, me, NULL)) {
631 SOSPeerInfoRef reject = SOSCircleCopyRejectedApplicant(newCircle, me, NULL);
632 if(CFEqualSafe(reject, me) && SOSPeerInfoApplicationVerify(me, account->user_public, NULL)) {
633 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
634 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
635 debugDumpCircle(CFSTR("newCircle"), newCircle);
636 if (account->my_identity)
637 SOSFullPeerInfoPurgePersistentKey(account->my_identity, NULL);
638 CFReleaseNull(account->my_identity);
639 me = NULL;
640 me_full = NULL;
641 } else {
642 secnotice("circle", "Rejected, Reapplying (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
643 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
644 debugDumpCircle(CFSTR("newCircle"), newCircle);
645 SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL);
646 writeUpdate = true;
647 }
648 }
649
650 CFRetainSafe(oldCircle);
651 CFRetainAssign(account->trusted_circle, newCircle);
652 SOSAccountSetPreviousPublic(account);
653
654 secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle);
655
656 if (me && account->user_public_trusted
657 && SOSCircleHasApplicant(oldCircle, me, NULL)
658 && SOSCircleCountPeers(newCircle) > 0
659 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
660 // We weren't rejected (above would have set me to NULL.
661 // We were applying and we weren't accepted.
662 // Our application is declared lost, let us reapply.
663
664 secnotice("signing", "requesting readmission to circle %@", newCircle);
665 if (SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL))
666 writeUpdate = true;
667 }
668
669 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
670 SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
671 }
672
673 SOSAccountNotifyOfChange(account, oldCircle, newCircle);
674
675 CFReleaseNull(oldCircle);
676
677 if (writeUpdate)
678 circleToPush = newCircle;
679 SOSUpdateKeyInterest();
680 }
681
682 /*
683 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new circles
684 * and pushing our current view of the circle (oldCircle). We'll only do this if we actually
685 * are a member of oldCircle - never for an empty circle.
686 */
687
688 if (circle_action == revert) {
689 if(haveOldCircle && me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
690 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr, newCircle, oldCircle);
691 debugDumpCircle(CFSTR("oldCircle"), oldCircle);
692 debugDumpCircle(CFSTR("newCircle"), newCircle);
693 circleToPush = oldCircle;
694 } else {
695 secnotice("canary", "%@, Rejecting: %@ Have no old circle - would reset", concStr, newCircle);
696 }
697 }
698
699
700 if (circleToPush != NULL) {
701 secnotice("signing", "Pushing:[%s] %@", local_remote, circleToPush);
702 CFDataRef circle_data = SOSCircleCopyEncodedData(circleToPush, kCFAllocatorDefault, error);
703
704 if (circle_data) {
705 //recording circle we are pushing in KVS
706 success &= SOSTransportCircleRecordLastCirclePushedInKVS(transport, SOSCircleGetName(circleToPush), circle_data);
707 //posting new circle to peers
708 success &= SOSTransportCirclePostCircle(transport, SOSCircleGetName(circleToPush), circle_data, error);
709 } else {
710 success = false;
711 }
712 CFReleaseNull(circle_data);
713 }
714
715 CFReleaseSafe(newCircle);
716 CFReleaseNull(emptyCircle);
717 return success;
718 }