]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTrustClassic+Expansion.m
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountTrustClassic+Expansion.m
1 //
2 // SOSAccountTrustClassicExpansion.m
3 // Security
4 //
5
6
7 #import <Foundation/Foundation.h>
8 #import "Security/SecureObjectSync/SOSAccount.h"
9 #import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
10 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
11 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
12 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
13 #import "Security/SecureObjectSync/SOSViews.h"
14 #import "Security/SecureObjectSync/SOSPeerInfoV2.h"
15 #import "Security/SecureObjectSync/SOSTransportCircleKVS.h"
16 #import "keychain/Signin Metrics/SFSignInAnalytics.h"
17
18 @implementation SOSAccountTrustClassic (Expansion)
19 typedef enum {
20 accept,
21 countersign,
22 leave,
23 revert,
24 modify,
25 ignore
26 } ringAction_t;
27
28 #if !defined(NDEBUG)
29 static const char * __unused actionstring[] = {
30 "accept", "countersign", "leave", "revert", "modify", "ignore",
31 };
32 #endif
33 static NSString* kSOSRingKey = @"trusted_rings";
34
35 //
36 // Generic Calls to Expansion Dictionary
37 //
38 -(CFTypeRef) getValueFromExpansion:(CFStringRef)key err:(CFErrorRef*)error
39 {
40 if (!self.expansion) {
41 return NULL;
42 }
43 return (__bridge CFTypeRef)([self.expansion objectForKey:(__bridge NSString*)key]);
44 }
45
46 -(bool) ensureExpansion:(CFErrorRef *)error
47 {
48 if (!self.expansion) {
49 self.expansion = [NSMutableDictionary dictionary];
50 }
51
52 return SecAllocationError((__bridge CFTypeRef)(self.expansion), error, CFSTR("Can't Alloc Account Expansion dictionary"));
53 }
54
55 -(bool) clearValueFromExpansion:(CFStringRef) key err:(CFErrorRef *)error
56 {
57 bool success = [self ensureExpansion:error];
58
59 require_quiet(success, errOut);
60
61 [self.expansion removeObjectForKey: (__bridge NSString*)(key)];
62 errOut:
63 return success;
64 }
65
66 -(bool) setValueInExpansion:(CFStringRef) key value:(CFTypeRef) value err:(CFErrorRef *)error {
67 if (value == NULL) return [self clearValueFromExpansion:key err:error];
68
69 bool success = [self ensureExpansion:error];
70 require_quiet(success, errOut);
71
72 [self.expansion setObject:(__bridge id _Nonnull)(value) forKey:(__bridge NSString*)key];
73
74 errOut:
75 return success;
76 }
77
78 -(bool) valueSetContainsValue:(CFStringRef) key value:(CFTypeRef) value
79 {
80 CFSetRef foundSet = asSet([self getValueFromExpansion:key err:NULL], NULL);
81 return foundSet && CFSetContainsValue(foundSet, value);
82 }
83
84 -(void) valueUnionWith:(CFStringRef) key valuesToUnion:(CFSetRef) valuesToUnion
85 {
86 CFMutableSetRef unionedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, valuesToUnion);
87 CFSetRef foundSet = asSet([self getValueFromExpansion:key err:NULL], NULL);
88 if (foundSet) {
89 CFSetUnion(unionedSet, foundSet);
90 }
91 [self setValueInExpansion:key value:unionedSet err:NULL];
92 CFReleaseNull(unionedSet);
93 }
94
95 -(void) valueSubtractFrom:(CFStringRef) key valuesToSubtract:(CFSetRef) valuesToSubtract
96 {
97 CFSetRef foundSet = asSet([self getValueFromExpansion:key err:NULL], NULL);
98 if (foundSet) {
99 CFMutableSetRef subtractedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, foundSet);
100 CFSetSubtract(subtractedSet, valuesToSubtract);
101 [self setValueInExpansion:key value:subtractedSet err:NULL];
102 CFReleaseNull(subtractedSet);
103 }
104 }
105
106 //Views
107 -(void) pendEnableViewSet:(CFSetRef) enabledViews
108 {
109 if(CFSetGetValue(enabledViews, kSOSViewKeychainV0) != NULL) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
110
111 [self valueUnionWith:kSOSPendingEnableViewsToBeSetKey valuesToUnion:enabledViews];
112 [self valueSubtractFrom:kSOSPendingDisableViewsToBeSetKey valuesToSubtract:enabledViews];
113 }
114
115 // V2 Dictionary
116 -(bool) updateV2Dictionary:(SOSAccount*)account v2:(CFDictionaryRef) newV2Dict
117 {
118 if(!newV2Dict) return true;
119
120 [self setValueInExpansion:kSOSTestV2Settings value:newV2Dict err:NULL];
121
122 if (self.trustedCircle && self.fullPeerInfo
123 && SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, newV2Dict, NULL)) {
124 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
125 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
126 return SOSCircleUpdatePeerInfo(circle_to_change, account.peerInfo);
127 }];
128 }
129 return true;
130 }
131
132 //
133 // Rings
134 //
135
136 -(bool) forEachRing:(RingNameBlock)block
137 {
138 bool retval = false;
139 __block bool changed = false;
140 __block CFStringRef ringname = NULL;
141 __block CFDataRef ringder = NULL;
142 __block SOSRingRef ring = NULL;
143 __block SOSRingRef newring = NULL;
144 __block CFDataRef newringder = NULL;
145
146 CFMutableDictionaryRef rings = [self getRings:NULL];
147 CFMutableDictionaryRef ringscopy = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
148 if(!rings){
149 CFReleaseNull(ringscopy);
150 return retval;
151 }
152 if(!ringscopy){
153 CFReleaseNull(ringscopy);
154 return retval;
155 }
156 CFDictionaryForEach(rings, ^(const void *key, const void *value) {
157 ringname = (CFStringRef) key;
158 ringder = CFDataCreateCopy(kCFAllocatorDefault, (CFDataRef) value);
159 CFDictionaryAddValue(ringscopy, key, ringder);
160 ring = SOSRingCreateFromData(NULL, ringder);
161 newring = block(ringname, ring);
162 if(newring) {
163 newringder = SOSRingCopyEncodedData(newring, NULL);
164 CFDictionaryReplaceValue(ringscopy, key, newringder);
165 CFReleaseNull(newringder);
166 changed = true;
167 }
168 CFReleaseNull(ring);
169 CFReleaseNull(ringder);
170 CFReleaseNull(newring);
171 });
172 if(changed) {
173 [self setRings:ringscopy];
174 }
175 retval = true;
176
177 CFReleaseNull(ringscopy);
178 return retval;
179 }
180
181 -(bool) resetAllRings:(SOSAccount*)account err:(CFErrorRef *)error
182 {
183 __block bool retval = true;
184 CFMutableSetRef ringList = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
185 if(!ringList){
186 CFReleaseNull(ringList);
187 return retval;
188 }
189
190 [self forEachRing: ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
191 CFSetAddValue(ringList, name);
192 return NULL; // just using this to grab names.
193 }];
194
195 CFSetForEach(ringList, ^(const void *value) {
196 CFStringRef ringName = (CFStringRef) value;
197 retval = retval && [self resetRing:account ringName:ringName err:error];
198 });
199
200 CFReleaseNull(ringList);
201 return retval;
202 }
203
204 -(bool) resetAccountToEmpty:(SOSAccount*)account transport: (SOSCircleStorageTransport*)circleTransport err:(CFErrorRef*) error
205 {
206
207 __block bool result = true;
208
209 result &= [self resetAllRings:account err:error];
210
211 self.fullPeerInfo = nil;
212
213 self.departureCode = kSOSWithdrewMembership;
214 secnotice("circleOps", "Reset Circle to empty by client request");
215
216 result &= [self modifyCircle:circleTransport err:error action:^bool(SOSCircleRef circle) {
217 result = SOSCircleResetToEmpty(circle, error);
218 return result;
219 }];
220
221 if (!result) {
222 secerror("error: %@", error ? *error : NULL);
223 }
224 return result;
225 }
226
227 -(bool) resetAccountToEmptyWithAnalytics:(SOSAccount*)account transport: (SOSCircleStorageTransport*)circleTransport parentEvent:(NSData*)parentEvent err:(CFErrorRef*) error
228 {
229 __block bool result = true;
230
231 NSError* localError = nil;
232 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
233
234 CFErrorRef resetError = NULL;
235 SFSignInAnalytics *resetAllRingsEvent = [parent newSubTaskForEvent:@"resetAllRingsEvent"];
236 result &= [self resetAllRings:account err:&resetError];
237 if(resetError){
238 [resetAllRingsEvent logRecoverableError:(__bridge NSError*)resetError];
239 secerror("reset all rings error: %@", resetError);
240 if(error){
241 *error = resetError;
242 }else{
243 CFReleaseNull(resetError);
244 }
245 }
246 [resetAllRingsEvent stopWithAttributes:nil];
247
248 self.fullPeerInfo = nil;
249
250 self.departureCode = kSOSWithdrewMembership;
251 secnotice("circleOps", "Reset Circle to empty by client request");
252
253 SFSignInAnalytics *resetCircleToEmptyEvent = [parent newSubTaskForEvent:@"resetCircleToEmptyEvent"];
254 result &= [self modifyCircle:circleTransport err:error action:^bool(SOSCircleRef circle) {
255 result = SOSCircleResetToEmpty(circle, error);
256 return result;
257 }];
258 [resetCircleToEmptyEvent stopWithAttributes:nil];
259
260 if (!result) {
261 secerror("error: %@", error ? *error : NULL);
262 }
263 return result;
264 }
265
266 -(void) setRings:(CFMutableDictionaryRef) newrings
267 {
268 [self.expansion setObject:(__bridge NSMutableDictionary*)newrings forKey:(kSOSRingKey)];
269 }
270
271 -(bool) checkForRings:(CFErrorRef*)error
272 {
273 __block bool retval = true;
274 CFMutableDictionaryRef rings = [self getRings:NULL];
275 if(rings && isDictionary(rings)) {
276 [self forEachRing:^SOSRingRef(CFStringRef ringname, SOSRingRef ring) {
277 if(retval == true) {
278 if(!SOSRingIsStable(ring)) {
279 retval = false;
280 secnotice("ring", "Ring %@ not stable", ringname);
281 }
282 }
283 return NULL;
284 }];
285 } else {
286 SOSCreateError(kSOSErrorNotReady, CFSTR("Rings not present"), NULL, error);
287 retval = false;
288 }
289 return retval;
290 }
291
292 -(bool) setRing:(SOSRingRef) addRing ringName:(CFStringRef) ringName err:(CFErrorRef*)error
293 {
294 require_quiet(addRing, errOut);
295 CFMutableDictionaryRef rings = [self getRings:NULL];
296 require_action_quiet(rings, errOut, SOSCreateError(kSOSErrorNoRing, CFSTR("No Rings found"), NULL, error));
297 CFDataRef ringder = SOSRingCopyEncodedData(addRing, error);
298 require_quiet(ringder, errOut);
299 CFDictionarySetValue(rings, ringName, ringder);
300 CFReleaseNull(ringder);
301 return true;
302 errOut:
303 return false;
304 }
305
306 static bool SOSAccountBackupSliceKeyBagNeedsFix(SOSAccount* account, SOSBackupSliceKeyBagRef bskb) {
307
308 if (SOSBSKBIsDirect(bskb) || account.backup_key == NULL)
309 return false;
310
311 CFSetRef peers = SOSBSKBGetPeers(bskb);
312
313 /* first scan for retired peers, and kick'em out!*/
314 SOSAccountIsPeerRetired(account, peers);
315
316 bool needsFix = true;
317
318 SOSPeerInfoRef myPeer = account.peerInfo;
319 if (myPeer) {
320 SOSPeerInfoRef meInBag = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
321 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
322 CFDataRef meInBagBK = SOSPeerInfoCopyBackupKey(meInBag);
323 needsFix = !(meInBag && CFEqualSafe(myBK,
324 meInBagBK));
325 CFReleaseNull(myBK);
326 CFReleaseNull(meInBagBK);
327 }
328
329 CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
330 if(rkbg) needsFix |= !SOSBKSBPrefixedKeyIsInKeyBag(bskb, bskbRkbgPrefix, rkbg);
331 else needsFix |= SOSBSKBHasRecoveryKey(bskb); // if we don't have a recovery key - the bskb shouldn't
332 CFReleaseNull(rkbg);
333
334 return needsFix;
335 }
336
337 -(bool) handleUpdateRing:(SOSAccount*)account prospectiveRing:(SOSRingRef)prospectiveRing transport:(SOSKVSCircleStorageTransport*)circleTransport userPublicKey:(SecKeyRef)userPublic writeUpdate:(bool)writeUpdate err:(CFErrorRef *)error
338 {
339 bool success = true;
340 bool haveOldRing = true;
341
342 const char * __unused localRemote = writeUpdate ? "local": "remote";
343 SOSFullPeerInfoRef fpi = self.fullPeerInfo;
344 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
345 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
346 bool peerActive = (fpi && pi && peerID && [self isInCircleOnly:NULL]);
347 SOSRingRef newRing = NULL;
348 SOSRingRef oldRing = NULL;
349
350 require_quiet(SOSAccountHasPublicKey(account, error), errOut);
351
352 secdebug("ringSigning", "start:[%s] %@", localRemote, prospectiveRing);
353
354
355 require_action_quiet(prospectiveRing, errOut,
356 SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("No Ring to work with"), NULL, error));
357
358 require_action_quiet(SOSRingIsStable(prospectiveRing), errOut, SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("You give rings a bad name"), NULL, error));
359
360 // We should at least have a sane ring system in the account object
361 require_quiet([self checkForRings:error], errOut);
362
363 CFStringRef ringName = SOSRingGetName(prospectiveRing);
364 oldRing = [self copyRing:ringName err:NULL];
365
366 newRing = CFRetainSafe(prospectiveRing); // TODO: SOSAccountCloneRingWithRetirement(account, prospectiveRing, error);
367
368 ringAction_t ringAction = ignore;
369
370 bool userTrustedoldRing = true;
371
372 CFSetRef peers = SOSCircleCopyPeers(self.trustedCircle, kCFAllocatorDefault);
373
374 SecKeyRef oldKey = userPublic;
375
376 if (!oldRing) {
377 oldRing = CFRetainSafe(newRing);
378 }
379
380 SOSConcordanceStatus concstat = SOSRingConcordanceTrust(fpi, peers, oldRing, newRing, oldKey, userPublic, peerID, error);
381 CFReleaseNull(peers);
382
383 CFStringRef concStr = NULL;
384 switch(concstat) {
385 case kSOSConcordanceTrusted:
386 ringAction = countersign;
387 concStr = CFSTR("Trusted");
388 break;
389 case kSOSConcordanceGenOld:
390 ringAction = userTrustedoldRing ? revert : ignore;
391 concStr = CFSTR("Generation Old");
392 break;
393 case kSOSConcordanceBadUserSig:
394 case kSOSConcordanceBadPeerSig:
395 ringAction = userTrustedoldRing ? revert : accept;
396 concStr = CFSTR("Bad Signature");
397 break;
398 case kSOSConcordanceNoUserSig:
399 ringAction = userTrustedoldRing ? revert : accept;
400 concStr = CFSTR("No User Signature");
401 break;
402 case kSOSConcordanceNoPeerSig:
403 ringAction = accept; // We might like this one eventually but don't countersign.
404 concStr = CFSTR("No trusted peer signature");
405 secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later %@", newRing);
406 break;
407 case kSOSConcordanceNoPeer:
408 ringAction = leave;
409 concStr = CFSTR("No trusted peer left");
410 break;
411 case kSOSConcordanceNoUserKey:
412 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
413 ringAction = ignore;
414 break;
415
416 case kSOSConcordanceMissingMe:
417 case kSOSConcordanceImNotWorthy:
418 ringAction = modify;
419 concStr = CFSTR("Incorrect membership for me");
420 break;
421 case kSOSConcordanceInvalidMembership:
422 ringAction = userTrustedoldRing ? revert : ignore;
423 concStr = CFSTR("Invalid Ring Membership");
424 break;
425 default:
426 secerror("##### Bad Error Return from ConcordanceTrust");
427 ringAction = ignore;
428 break;
429 }
430
431 (void)concStr;
432
433 secdebug("ringSigning", "Decided on action [%s] based on concordance state [%@] and [%s] circle.",
434 actionstring[ringAction], concStr, userTrustedoldRing ? "trusted" : "untrusted");
435
436 SOSRingRef ringToPush = NULL;
437 bool iWasInOldRing = peerID && SOSRingHasPeerID(oldRing, peerID);
438 bool iAmInNewRing = peerID && SOSRingHasPeerID(newRing, peerID);
439 bool ringIsBackup = SOSRingGetType(newRing) == kSOSRingBackup;
440 bool ringIsRecovery = SOSRingGetType(newRing) == kSOSRingRecovery;
441
442 if (ringIsBackup && peerActive) {
443 if (ringAction == accept || ringAction == countersign) {
444 CFErrorRef localError = NULL;
445 SOSBackupSliceKeyBagRef bskb = SOSRingCopyBackupSliceKeyBag(newRing, &localError);
446
447 if(!bskb) {
448 secnotice("ringSigning", "Backup ring with no backup slice keybag (%@)", localError);
449 } else if (SOSAccountBackupSliceKeyBagNeedsFix(account, bskb)) {
450 ringAction = modify;
451 }
452 CFReleaseSafe(localError);
453 CFReleaseSafe(bskb);
454 }
455
456 if (ringAction == modify) {
457 CFErrorRef updateError = NULL;
458 [self setRing:newRing ringName:ringName err:error];
459
460 if(SOSAccountUpdateOurPeerInBackup(account, newRing, &updateError)) {
461 secdebug("signing", "Modified backup ring to include us");
462 } else {
463 secerror("Could not add ourselves to the backup: (%@)", updateError);
464 }
465 CFReleaseSafe(updateError);
466
467 // Fall through to normal modify handling.
468 }
469 }
470
471 if (ringIsRecovery && peerActive && (ringAction == modify)) {
472 [self setRing:newRing ringName:ringName err:error];
473 }
474
475
476 if (ringAction == modify) {
477 ringAction = ignore;
478 }
479
480 if (ringAction == leave) {
481 if (iWasInOldRing) {
482 if ([self leaveRing:circleTransport ring:newRing err:error]){
483 ringToPush = newRing;
484 } else {
485 secdebug("ringSigning", "Can't leave ring %@", oldRing);
486 success = false;
487 }
488 ringAction = accept;
489 } else {
490 // We are not in this ring, but we need to update account with it, since we got it from cloud
491 ringAction = accept;
492 }
493 }
494
495 if (ringAction == countersign) {
496 if (iAmInNewRing) {
497 if (SOSRingPeerTrusted(newRing, fpi, NULL)) {
498 secdebug("ringSigning", "Already concur with: %@", newRing);
499 } else {
500 CFErrorRef signingError = NULL;
501
502 if (fpi && SOSRingConcordanceSign(newRing, fpi, &signingError)) {
503 ringToPush = newRing;
504 } else {
505 secerror("Failed to concordance sign, error: %@ Old: %@ New: %@", signingError, oldRing, newRing);
506 success = false;
507 }
508 CFReleaseSafe(signingError);
509 }
510 } else {
511 secdebug("ringSigning", "Not countersigning, not in ring: %@", newRing);
512 }
513 ringAction = accept;
514 }
515
516 if (ringAction == accept) {
517 if (iWasInOldRing && !iAmInNewRing) {
518
519 // Don't destroy evidence of other code determining reason for leaving.
520 //if(!SOSAccountHasLeft(account)) account.departure_code = kSOSMembershipRevoked;
521 // TODO: LeaveReason for rings
522 }
523
524 if (pi && SOSRingHasRejection(newRing, peerID)) {
525 // TODO: ReasonForLeaving for rings
526 SOSRingRemoveRejection(newRing, peerID);
527 }
528
529 [self setRing:newRing ringName:ringName err:error];
530
531 if (pi && account.accountKeyIsTrusted
532 && SOSRingHasApplicant(oldRing, peerID)
533 && SOSRingCountPeers(newRing) > 0
534 && !iAmInNewRing && !SOSRingHasApplicant(newRing, peerID)) {
535 // We weren't rejected (above would have set me to NULL.
536 // We were applying and we weren't accepted.
537 // Our application is declared lost, let us reapply.
538
539 if (SOSRingApply(newRing, userPublic, fpi, NULL))
540 if(peerActive) writeUpdate = true;
541 }
542
543 if (pi && SOSRingHasPeerID(oldRing, peerID)) {
544 [self cleanupRetirementTickets:account circle:self.trustedCircle time:RETIREMENT_FINALIZATION_SECONDS err:NULL];
545 }
546
547
548 account.circle_rings_retirements_need_attention = true;
549
550 if (writeUpdate)
551 ringToPush = newRing;
552 secnotice("circleop", "Setting account.key_interests_need_updating to true in handleUpdateRing");
553 account.key_interests_need_updating = true;
554 }
555
556 /*
557 * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new rings
558 * and pushing our current view of the ring (oldRing). We'll only do this if we actually
559 * are a member of oldRing - never for an empty ring.
560 */
561
562 if (ringAction == revert) {
563 if(haveOldRing && peerActive && SOSRingHasPeerID(oldRing, peerID)) {
564 secdebug("ringSigning", "%@, Rejecting: %@ re-publishing %@", concStr, newRing, oldRing);
565 ringToPush = oldRing;
566 } else {
567 secdebug("ringSigning", "%@, Rejecting: %@ Have no old circle - would reset", concStr, newRing);
568 }
569 }
570
571
572 if (ringToPush != NULL) {
573 secdebug("ringSigning", "Pushing:[%s] %@", localRemote, ringToPush);
574 CFDataRef ringData = SOSRingCopyEncodedData(ringToPush, error);
575 if (ringData) {
576 success &= [circleTransport kvsRingPostRing:SOSRingGetName(ringToPush) ring:ringData err:error];
577 } else {
578 success = false;
579 }
580 CFReleaseNull(ringData);
581 }
582 CFReleaseNull(oldRing);
583 CFReleaseNull(newRing);
584 return success;
585 errOut:
586 CFReleaseNull(oldRing);
587 CFReleaseNull(newRing);
588 return false;
589
590 }
591
592 -(SOSRingRef) copyRing:(CFStringRef)ringName err:(CFErrorRef *)error
593 {
594 CFMutableDictionaryRef rings = [self getRings:error];
595 require_action_quiet(rings, errOut, SOSCreateError(kSOSErrorNoRing, CFSTR("No Rings found"), NULL, error));
596 CFTypeRef ringder = CFDictionaryGetValue(rings, ringName);
597 require_action_quiet(ringder, errOut, SOSCreateErrorWithFormat(kSOSErrorNoRing, NULL, error, NULL, CFSTR("No Ring found %@"), ringName));
598 SOSRingRef ring = SOSRingCreateFromData(NULL, ringder);
599 return (SOSRingRef) ring;
600
601 errOut:
602 return NULL;
603 }
604
605 -(CFMutableDictionaryRef) getRings:(CFErrorRef *)error
606 {
607 CFMutableDictionaryRef rings = (__bridge CFMutableDictionaryRef) [self.expansion objectForKey:kSOSRingKey];
608 if(!rings) {
609 [self addRingDictionary];
610 rings = [self getRings:error];
611 }
612
613 return rings;
614 }
615
616 -(bool) resetRing:(SOSAccount*)account ringName:(CFStringRef) ringName err:(CFErrorRef *)error
617 {
618 bool retval = false;
619
620 SOSRingRef ring = [self copyRing:ringName err:error];
621 SOSRingRef newring = SOSRingCreate(ringName, NULL, SOSRingGetType(ring), error);
622 SOSRingGenerationCreateWithBaseline(newring, ring);
623 SOSBackupRingSetViews(newring, self.fullPeerInfo, SOSBackupRingGetViews(ring, NULL), error);
624 require_quiet(newring, errOut);
625 CFReleaseNull(ring);
626 retval = SOSAccountUpdateRing(account, newring, error);
627 errOut:
628 CFReleaseNull(ring);
629 CFReleaseNull(newring);
630 return retval;
631 }
632
633 -(bool) leaveRing:(SOSKVSCircleStorageTransport*)circle_transport ring:(SOSRingRef) ring err:(CFErrorRef*) error
634 {
635 SOSFullPeerInfoRef fpi = self.fullPeerInfo;
636 if(!fpi) return false;
637 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
638 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
639
640 CFErrorRef localError = NULL;
641
642 bool retval = false;
643 bool writeRing = false;
644 bool writePeerInfo = false;
645
646 if(SOSRingHasPeerID(ring, peerID)) {
647 writePeerInfo = true;
648 }
649
650 if(writePeerInfo || writeRing) {
651 SOSRingWithdraw(ring, NULL, fpi, error);
652 }
653
654 if (writeRing) {
655 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
656
657 if (ring_data) {
658 [circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
659 }
660 CFReleaseNull(ring_data);
661 }
662 retval = true;
663 CFReleaseNull(localError);
664 return retval;
665 }
666
667 @end