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