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