/ keychain / SecureObjectSync / SOSAccountTrustClassic+Expansion.m
SOSAccountTrustClassic+Expansion.m
  1  //
  2  //  SOSAccountTrustClassicExpansion.m
  3  //  Security
  4  //
  5  
  6  
  7  #import <Foundation/Foundation.h>
  8  #import "keychain/SecureObjectSync/SOSAccount.h"
  9  #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
 10  #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
 11  #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
 12  #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
 13  #import "keychain/SecureObjectSync/SOSViews.h"
 14  #import "keychain/SecureObjectSync/SOSPeerInfoV2.h"
 15  #import "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
 16  #import "keychain/SecureObjectSync/SOSTransportCircleKVS.h"
 17  #import "keychain/SecureObjectSync/SOSRingRecovery.h"
 18  
 19  @implementation SOSAccountTrustClassic (Expansion)
 20  typedef enum {
 21      accept,
 22      countersign,
 23      leave,
 24      revert,
 25      modify,
 26      ignore
 27  } ringAction_t;
 28  
 29  static const char *actionstring[] = {
 30      "accept", "countersign", "leave", "revert", "modify", "ignore",
 31  };
 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      __block bool result = true;
206      CFErrorRef resetError = NULL;
207  
208      result &= [self resetAllRings:account err:&resetError];
209      if(resetError){
210          secerror("reset all rings error: %@", resetError);
211          if(error){
212              *error = resetError;
213          }else{
214              CFReleaseNull(resetError);
215          }
216      }
217  
218      self.fullPeerInfo = nil;
219  
220      self.departureCode = kSOSWithdrewMembership;
221      secnotice("circleOps", "Reset Rings to empty by client request");
222  
223      result &= [self modifyCircle:circleTransport err:error action:^bool(SOSCircleRef circle) {
224          result = SOSCircleResetToEmpty(circle, error);
225          return result;
226      }];
227  
228      if (!result) {
229          secerror("error: %@", error ? *error : NULL);
230      } else {
231          notify_post(kSOSCCCircleOctagonKeysChangedNotification);
232      }
233      return result;
234  }
235  
236  -(void) setRings:(CFMutableDictionaryRef) newrings
237  {
238      [self.expansion setObject:(__bridge NSMutableDictionary*)newrings forKey:(kSOSRingKey)];
239  }
240  
241  -(bool) checkForRings:(CFErrorRef*)error
242  {
243      __block bool retval = true;
244      CFMutableDictionaryRef rings = [self getRings:NULL];
245      if(rings && isDictionary(rings)) {
246          [self forEachRing:^SOSRingRef(CFStringRef ringname, SOSRingRef ring) {
247              if(retval == true) {
248                  if(!SOSRingIsStable(ring)) {
249                      retval = false;
250                      secnotice("ring", "Ring %@ not stable", ringname);
251                  }
252              }
253              return NULL;
254          }];
255      } else {
256          SOSCreateError(kSOSErrorNotReady, CFSTR("Rings not present"), NULL, error);
257          retval = false;
258      }
259      return retval;
260  }
261  
262  -(bool) setRing:(SOSRingRef) addRing ringName:(CFStringRef) ringName err:(CFErrorRef*)error
263  {
264      require_quiet(addRing, errOut);
265      CFMutableDictionaryRef rings = [self getRings:NULL];
266      require_action_quiet(rings, errOut, SOSCreateError(kSOSErrorNoRing, CFSTR("No Rings found"), NULL, error));
267      CFDataRef ringder = SOSRingCopyEncodedData(addRing, error);
268      require_quiet(ringder, errOut);
269      CFDictionarySetValue(rings, ringName, ringder);
270      CFReleaseNull(ringder);
271      return true;
272  errOut:
273      return false;
274  }
275  
276  -(bool) handleUpdateRing:(SOSAccount*)account prospectiveRing:(SOSRingRef)prospectiveRing transport:(SOSKVSCircleStorageTransport*)circleTransport userPublicKey:(SecKeyRef)userPublic writeUpdate:(bool)localUpdate err:(CFErrorRef *)error
277  {
278      bool success = false;
279      bool haveOldRing = true;
280      static uint recRingProcessed = 0;
281      static uint bckRingProcessed = 0;
282      
283      const char * __unused localRemote = localUpdate ? "local": "remote";
284      SOSFullPeerInfoRef fpi = self.fullPeerInfo;
285      SOSPeerInfoRef     pi = SOSFullPeerInfoGetPeerInfo(fpi);
286      CFStringRef        peerID = SOSPeerInfoGetPeerID(pi);
287      SecKeyRef          peerPrivKey = SOSFullPeerInfoCopyDeviceKey(fpi, NULL);
288      SecKeyRef          peerPubKey = SOSFullPeerInfoCopyPubKey(fpi, NULL);
289      __block bool       peerActive = (fpi && pi && peerID && [self isInCircleOnly:NULL]);
290      bool ringIsBackup       = SOSRingGetType(prospectiveRing) == kSOSRingBackup;
291      bool ringIsRecovery     = SOSRingGetType(prospectiveRing) == kSOSRingRecovery;
292      CFStringRef ringName = SOSRingGetName(prospectiveRing);
293      CFMutableSetRef peers   = SOSCircleCopyPeers(self.trustedCircle, kCFAllocatorDefault); // retirement tickets and iCloud key filtered out
294      CFMutableSetRef filteredPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
295      CFMutableSetRef filteredPeerInfos = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
296      CFStringRef ringBackupViewName = NULL;
297  
298      SOSRingRef ringToPush = NULL;
299      SOSRingRef newRing = NULL;
300      SOSRingRef oldRing = NULL;
301  
302      CFStringRef modifierPeerID = CFStringCreateTruncatedCopy(SOSRingGetLastModifier(prospectiveRing), 8);
303      secnotice("ring", "start:[%s] modifier: %@", localRemote, modifierPeerID);
304      CFReleaseNull(modifierPeerID);
305  
306      // don't act on our own echos from KVS (remote ring, our peerID as modifier)
307      oldRing = [self copyRing:ringName err:NULL];
308      if(!localUpdate && CFEqualSafe(peerID, SOSRingGetLastModifier(prospectiveRing)) && CFEqualSafe(oldRing, prospectiveRing)) {
309          secnotice("ring", "Ceasing ring handling for an echo of our own posted ring");
310          success = true;
311          goto errOut;
312      }
313      
314      require_quiet(SOSAccountHasPublicKey(account, error), errOut);
315      require_action_quiet(peerPubKey, errOut, SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("No device public key to work with"), NULL, error));
316      require_action_quiet(peerPrivKey, errOut, SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("No device private key to work with"), NULL, error));
317      require_action_quiet(prospectiveRing, errOut, SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("No Ring to work with"), NULL, error));
318      require_action_quiet(SOSRingIsStable(prospectiveRing), errOut, SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("You give rings a bad name"), NULL, error));
319      
320      // We should at least have a sane ring system in the account object
321      require_quiet([self checkForRings:error], errOut);
322  
323      if(ringIsBackup) {
324          ringBackupViewName = SOSRingGetBackupView(prospectiveRing, NULL);
325          peerActive &= ringBackupViewName && SOSPeerInfoIsViewPermitted(pi, ringBackupViewName) && SOSPeerInfoHasBackupKey(pi);
326      }
327      require_action_quiet(peerActive, errOut, success = true);
328  
329      newRing = SOSRingCopyRing(prospectiveRing, NULL);
330      ringAction_t ringAction = ignore;
331      
332      bool userTrustedoldRing = (oldRing) ? SOSRingVerify(oldRing, peerPubKey, NULL): false;
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      
341      CFStringRef concStr = CFSTR("NA");
342      switch(concstat) {
343          case kSOSConcordanceTrusted:
344              ringAction = countersign;
345              concStr = CFSTR("Trusted");
346              break;
347          case kSOSConcordanceGenOld:
348              ringAction = userTrustedoldRing ? revert : ignore;
349              concStr = CFSTR("Generation Old");
350              break;
351          case kSOSConcordanceBadUserSig:
352          case kSOSConcordanceBadPeerSig:
353              ringAction = userTrustedoldRing ? revert : accept;
354              concStr = CFSTR("Bad Signature");
355              break;
356          case kSOSConcordanceNoUserSig:
357              ringAction = userTrustedoldRing ? revert : accept;
358              concStr = CFSTR("No User Signature");
359              break;
360          case kSOSConcordanceNoPeerSig:
361              ringAction = accept; // We might like this one eventually but don't countersign.
362              concStr = CFSTR("No trusted peer signature");
363              secnotice("signing", "##### No trusted peer signature found, accepting hoping for concordance later");
364              break;
365          case kSOSConcordanceNoPeer:
366              ringAction = leave;
367              concStr = CFSTR("No trusted peer left");
368              break;
369          case kSOSConcordanceNoUserKey:
370              secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
371              concStr = CFSTR("No User Public Key Available");
372              ringAction = ignore;
373              break;
374              
375          case kSOSConcordanceMissingMe:
376              ringAction = modify;
377              concStr = CFSTR("Incorrect membership for me");
378              break;
379          case kSOSConcordanceImNotWorthy:
380              ringAction = leave;
381              concStr = CFSTR("This peer shouldn't be in this ring since it isn't in view");
382              break;
383          case kSOSConcordanceInvalidMembership:
384              ringAction = userTrustedoldRing ? revert : ignore;
385              concStr = CFSTR("Invalid Ring Membership");
386              break;
387          default:
388              secerror("##### Bad Error Return from ConcordanceTrust");
389              concStr = CFSTR("Bad Error Return from ConcordanceTrust");
390              ringAction = ignore;
391              break;
392      }
393  
394      secnotice("ring", "Decided on action [%s] based on concordance state [%@] and [%s] ring.",
395               actionstring[ringAction], concStr, userTrustedoldRing ? "trusted" : "untrusted");
396  
397      // if we're ignoring this ring we're done
398      require_action_quiet(ringAction != ignore, errOut, success = true);
399      // can't really remove ourselves since we can't sign when we do - need to rely on other peers to remove us
400      require_action_quiet(ringAction != leave, leaveAndAccept, ringAction = accept);
401  
402      // This will take care of modify, but we're always going to do this scan if we get this far
403      CFSetRef ringPeerIDSet = SOSRingCopyPeerIDs(newRing);
404      if(CFSetGetCount(ringPeerIDSet) == 0) { // this is a reset ring
405          secnotice("ring", "changing state to accept - we have a reset ring");
406          ringAction = accept;
407      } else {
408          // Get the peerIDs appropriate for the ring
409          if(ringIsBackup) {
410              SOSCircleForEachBackupCapablePeerForView(self.trustedCircle, userPublic, ringBackupViewName, ^(SOSPeerInfoRef peer) {
411                  CFSetAddValue(filteredPeerIDs, SOSPeerInfoGetPeerID(peer));
412                  CFSetAddValue(filteredPeerInfos, peer);
413              });
414          } else {
415              SOSCircleForEachValidSyncingPeer(self.trustedCircle, userPublic, ^(SOSPeerInfoRef peer) {
416                  CFSetAddValue(filteredPeerIDs, SOSPeerInfoGetPeerID(peer));
417                  CFSetAddValue(filteredPeerInfos, peer);
418              });
419          }
420  
421          if(!CFEqual(filteredPeerIDs, ringPeerIDSet)) {
422              secnotice("ring", "mismatch between filteredPeerIDs and ringPeerIDSet, fixing ring and gensigning");
423              secnotice("ring", "filteredPeerIDs %@", filteredPeerIDs);
424              secnotice("ring", "  ringPeerIDSet %@", ringPeerIDSet);
425              SOSRingSetPeerIDs(newRing, filteredPeerIDs);
426              SOSRingRemoveSignatures(newRing, NULL);
427              ringAction = countersign;
428          }
429      }
430      CFReleaseNull(ringPeerIDSet);
431  
432      if (ringAction == countersign) {
433          bool stopCountersign = false;
434          CFIndex peerCount = CFSetGetCount(filteredPeerIDs);
435  
436          if(peerCount > 0) {
437              // Fix payloads if necessary
438              if (ringIsBackup && SOSPeerInfoHasBackupKey(pi)) {
439                  __block bool fixBSKB = false;
440                  CFDataRef recoveryKeyData = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
441                  SOSBackupSliceKeyBagRef currentBSKB = SOSRingCopyBackupSliceKeyBag(newRing, NULL);
442                  
443                  if(currentBSKB == NULL) {
444                      secnotice("ring", "Backup ring contains no BSKB");
445                      fixBSKB = true;
446                  }
447                  
448                  if(SOSBSKBAllPeersBackupKeysAreInKeyBag(currentBSKB, filteredPeerInfos) == false) {
449                      secnotice("ring", "BSKB is missing some backup keys");
450                      fixBSKB = true;
451                  }
452  
453                  if(SOSBSKBHasThisRecoveryKey(currentBSKB, recoveryKeyData) == false) {
454                      secnotice("ring", "BSKB is missing recovery key");
455                      fixBSKB = true;
456                  }
457  
458                  if(fixBSKB) {
459                      CFErrorRef localError = NULL;
460                      CFSetRef viewSet = SOSRingGetBackupViewset(newRing, NULL);
461                      secnotice("ring", "Need to fix BSKB - this will prompt a gensign");
462  
463                      SOSBackupSliceKeyBagRef bskb = NULL;
464                      if(recoveryKeyData) {
465                          CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
466                          CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, recoveryKeyData);
467                          bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, filteredPeerInfos, additionalKeys, error);
468                          CFReleaseNull(additionalKeys);
469                      } else {
470                          bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, filteredPeerInfos, error);
471                      }
472  
473                      if(SOSRingSetBackupKeyBag(newRing, fpi, viewSet, bskb, &localError) == false) {
474                          stopCountersign = true;
475                          secnotice("ring", "Couldn't fix BSKB (%@)", localError);
476                      }
477                      SOSRingRemoveSignatures(newRing, NULL);
478                      SOSRingGenerationSign(newRing, NULL, fpi, error);
479                      ringToPush = newRing;
480                      CFReleaseNull(localError);
481                      CFReleaseNull(bskb);
482                  }
483                  CFReleaseNull(recoveryKeyData);
484                  CFReleaseNull(currentBSKB);
485              }
486          }
487  
488          if(stopCountersign) {
489              ringAction = ignore;
490          } else if (SOSRingPeerTrusted(newRing, fpi, NULL)) {
491              secnotice("ring", "Already concur with newRing");
492              ringAction = accept;
493          } else {
494              CFErrorRef signingError = NULL;
495              if (fpi && SOSRingConcordanceSign(newRing, fpi, &signingError)) {
496                  secnotice("ring", "concordance signed");
497                  ringToPush = newRing;
498                  ringAction = accept;
499              } else {
500                  secnotice("ring", "Failed to concordance sign, error: %@", signingError);
501                  success = false;
502                  ringAction = ignore;
503              }
504              CFReleaseSafe(signingError);
505          }
506      }
507  
508  leaveAndAccept:
509      
510      if (ringAction == accept) {
511          if(ringIsRecovery) {
512              if(!localUpdate) { // processing a remote ring - we accept the new recovery key here
513                  if(SOSRingIsEmpty_Internal(newRing)) { // Reset ring will reset the recovery key
514                      secnotice("ring", "Reset ring for recovery from remote peer");
515                      SOSRecoveryKeyBagRef ringRKBG = SOSRecoveryKeyBagCreateForAccount(kCFAllocatorDefault, (__bridge CFTypeRef)account, SOSRKNullKey(), error);
516                      SOSAccountSetRecoveryKeyBagEntry(kCFAllocatorDefault, account, ringRKBG, error);
517                      CFReleaseNull(ringRKBG);
518                  } else {                                // normal ring recovery key harvest
519                      secnotice("ring", "normal ring recovery key harvest");
520                      SOSRecoveryKeyBagRef ringRKBG = SOSRingCopyRecoveryKeyBag(newRing, NULL);
521                      SOSAccountSetRecoveryKeyBagEntry(kCFAllocatorDefault, account, ringRKBG, error);
522                      CFReleaseNull(ringRKBG);
523                  }
524              }
525          }
526          if (pi && SOSRingHasRejection(newRing, peerID)) {
527              SOSRingRemoveRejection(newRing, peerID);
528          }
529          [self setRing:newRing ringName:ringName err:error];
530          account.circle_rings_retirements_need_attention = true;
531          if (localUpdate) {
532              ringToPush = newRing;
533          } else if (ringToPush == NULL) {
534              success = true;
535          }
536      }
537      
538      /*
539       * In the revert section we'll guard the KVS idea of circles by rejecting "bad" new rings
540       * and pushing our current view of the ring (oldRing).  We'll only do this if we actually
541       * are a member of oldRing - never for an empty ring.
542       */
543      
544      if (ringAction == revert) {
545          if(haveOldRing && SOSRingHasPeerID(oldRing, peerID)) {
546              secnotice("ring", "Rejecting: %@", newRing);
547              secnotice("ring", "   RePush: %@", oldRing);
548              ringToPush = oldRing;
549          } else {
550              secnotice("ring", "Rejecting: %@", newRing);
551              secnotice("ring", "Have no old ring - would reset");
552          }
553      }
554  
555      if (ringToPush != NULL) {
556          if(ringIsBackup) {
557              bckRingProcessed++;
558          } else if(ringIsRecovery) {
559              recRingProcessed++;
560          }
561          secnotice("ring", "Pushing:[%s] %@", localRemote, ringToPush);
562          CFDataRef ringData = SOSRingCopyEncodedData(ringToPush, error);
563          if (ringData) {
564              success = [circleTransport kvsRingPostRing:SOSRingGetName(ringToPush) ring:ringData err:error];
565          } else {
566              success = false;
567          }
568          secnotice("ring", "Setting account.key_interests_need_updating to true in handleUpdateRing");
569          account.key_interests_need_updating = true;
570          CFReleaseNull(ringData);
571      }
572  errOut:
573      CFReleaseNull(filteredPeerIDs);
574      CFReleaseNull(filteredPeerInfos);
575      CFReleaseNull(oldRing);
576      CFReleaseNull(newRing);
577      CFReleaseNull(peers);
578      CFReleaseNull(peerPubKey);
579      CFReleaseNull(peerPrivKey);
580      return success;
581  }
582  
583  -(SOSRingRef) copyRing:(CFStringRef)ringName err:(CFErrorRef *)error
584  {
585      CFMutableDictionaryRef rings = [self getRings:error];
586      require_action_quiet(rings, errOut, SOSCreateError(kSOSErrorNoRing, CFSTR("No Rings found"), NULL, error));
587      CFTypeRef ringder = CFDictionaryGetValue(rings, ringName);
588      require_action_quiet(ringder, errOut, SOSCreateErrorWithFormat(kSOSErrorNoRing, NULL, error, NULL, CFSTR("No Ring found %@"), ringName));
589      SOSRingRef ring = SOSRingCreateFromData(NULL, ringder);
590      return (SOSRingRef) ring;
591      
592  errOut:
593      return NULL;
594  }
595  
596  -(CFMutableDictionaryRef) getRings:(CFErrorRef *)error
597  {
598      CFMutableDictionaryRef rings = (__bridge CFMutableDictionaryRef) [self.expansion objectForKey:kSOSRingKey];
599      if(!rings) {
600          [self addRingDictionary];
601          rings = [self getRings:error];
602      }
603      
604      return rings;
605  }
606  
607  -(bool) resetRing:(SOSAccount*)account ringName:(CFStringRef) ringName err:(CFErrorRef *)error
608  {
609      bool retval = false;
610      
611      SOSRingRef ring = [self copyRing:ringName err:error];
612      SOSRingRef newring = SOSRingCreate(ringName, NULL, SOSRingGetType(ring), error);
613      SOSRingGenerationCreateWithBaseline(newring, ring);
614      SOSBackupRingSetViews(newring, self.fullPeerInfo, SOSBackupRingGetViews(ring, NULL), error);
615      require_quiet(newring, errOut);
616      CFReleaseNull(ring);
617      retval = SOSAccountUpdateRing(account, newring, error);
618  errOut:
619      CFReleaseNull(ring);
620      CFReleaseNull(newring);
621      return retval;
622  }
623  
624  
625  @end