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