/ keychain / SecureObjectSync / Tool / keychain_sync.m
keychain_sync.m
  1  /*
  2   * Copyright (c) 2003-2007,2009-2010,2013-2014 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   *
 23   * keychain_add.c
 24   */
 25  
 26  
 27  #include <stdio.h>
 28  #include <stdlib.h>
 29  #include <string.h>
 30  #include <unistd.h>
 31  #include <sys/utsname.h>
 32  #include <sys/stat.h>
 33  #include <time.h>
 34  #include <getopt.h>
 35  #include <readpassphrase.h>
 36  
 37  #include <Security/SecItem.h>
 38  
 39  #include <CoreFoundation/CFNumber.h>
 40  #include <CoreFoundation/CFString.h>
 41  
 42  #include <Security/SecureObjectSync/SOSCloudCircle.h>
 43  #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
 44  #include <Security/SecureObjectSync/SOSPeerInfo.h>
 45  #include "keychain/SecureObjectSync/SOSPeerInfoPriv.h"
 46  #include "keychain/SecureObjectSync/SOSPeerInfoV2.h"
 47  #include "keychain/SecureObjectSync/SOSUserKeygen.h"
 48  #include "keychain/SecureObjectSync/SOSKVSKeys.h"
 49  #include "keychain/securityd/SOSCloudCircleServer.h"
 50  #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
 51  #include <Security/SecOTRSession.h>
 52  #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
 53  
 54  #include <utilities/SecCFWrappers.h>
 55  #include <utilities/debugging.h>
 56  
 57  #include "SecurityTool/sharedTool/readline.h"
 58  #include <notify.h>
 59  
 60  #include "keychain_sync.h"
 61  #include "keychain_log.h"
 62  
 63  #include "secToolFileIO.h"
 64  #include "secViewDisplay.h"
 65  #include "accountCirclesViewsPrint.h"
 66  
 67  #include <Security/SecPasswordGenerate.h>
 68  
 69  #define MAXKVSKEYTYPE kUnknownKey
 70  #define DATE_LENGTH 18
 71  
 72  
 73  static bool clearAllKVS(CFErrorRef *error)
 74  {
 75      __block bool result = false;
 76      const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
 77      dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 78      dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
 79      dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
 80  
 81      secnotice("circleOps", "security tool called SOSCloudKeychainClearAll to clear KVS");
 82      SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
 83      {
 84          result = (cerror != NULL);
 85          dispatch_semaphore_signal(waitSemaphore);
 86      });
 87      
 88  	dispatch_semaphore_wait(waitSemaphore, finishTime);
 89  
 90      return result;
 91  }
 92  
 93  static bool enableDefaultViews()
 94  {
 95      bool result = false;
 96      CFMutableSetRef viewsToEnable = SOSViewCopyViewSet(kViewSetV0);
 97      CFMutableSetRef viewsToDisable = CFSetCreateMutable(NULL, 0, NULL);
 98      
 99      result = SOSCCViewSet(viewsToEnable, viewsToDisable);
100      CFRelease(viewsToEnable);
101      CFRelease(viewsToDisable);
102      return result;
103  }
104  
105  static bool requestToJoinCircle(CFErrorRef *error)
106  {
107      // Set the visual state of switch based on membership in circle
108      bool hadError = false;
109      SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(error);
110      
111      switch (ccstatus)
112      {
113      case kSOSCCCircleAbsent:
114          hadError = !SOSCCResetToOffering(error);
115          hadError &= enableDefaultViews();
116          break;
117      case kSOSCCNotInCircle:
118          hadError = !SOSCCRequestToJoinCircle(error);
119          hadError &= enableDefaultViews();
120          break;
121      default:
122          printerr(CFSTR("Request to join circle with bad status:  %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus), ccstatus);
123          break;
124      }
125      return hadError;
126  }
127  
128  static bool setPassword(char *labelAndPassword, CFErrorRef *err)
129  {
130      char *last = NULL;
131      char *token0 = strtok_r(labelAndPassword, ":", &last);
132      char *token1 = strtok_r(NULL, "", &last);
133      CFStringRef label = token1 ? CFStringCreateWithCString(NULL, token0, kCFStringEncodingUTF8) : CFSTR("security command line tool");
134      char *password_token = token1 ? token1 : token0;
135      password_token = password_token ? password_token : "";
136      CFDataRef password = CFDataCreate(NULL, (const UInt8*) password_token, strlen(password_token));
137      bool returned = !SOSCCSetUserCredentials(label, password, err);
138      CFRelease(label);
139      CFRelease(password);
140      return returned;
141  }
142  
143  static bool tryPassword(char *labelAndPassword, CFErrorRef *err)
144  {
145      char *last = NULL;
146      char *token0 = strtok_r(labelAndPassword, ":", &last);
147      char *token1 = strtok_r(NULL, "", &last);
148      CFStringRef label = token1 ? CFStringCreateWithCString(NULL, token0, kCFStringEncodingUTF8) : CFSTR("security command line tool");
149      char *password_token = token1 ? token1 : token0;
150      password_token = password_token ? password_token : "";
151      CFDataRef password = CFDataCreate(NULL, (const UInt8*) password_token, strlen(password_token));
152      bool returned = !SOSCCTryUserCredentials(label, password, err);
153      CFRelease(label);
154      CFRelease(password);
155      return returned;
156  }
157  
158  /*
159   * Prompt user, call SOSCCTryUserCredentials.
160   * Does not support optional label syntax like -T/-P.
161   * Returns true on success.
162   */
163  static bool
164  promptAndTryPassword(CFErrorRef *error)
165  {
166      bool success = false;
167      char passbuf[1024];
168      CFDataRef password;
169  
170      if (readpassphrase("iCloud password: ", passbuf, sizeof(passbuf), RPP_REQUIRE_TTY) != NULL) {
171          password = CFDataCreate(NULL, (const UInt8 *)passbuf, strlen(passbuf));
172          if (password != NULL) {
173              success = SOSCCTryUserCredentials(CFSTR("security command line tool"), password, error);
174              CFReleaseNull(password);
175          }
176      }
177  
178      return success;
179  }
180  
181  static bool syncAndWait(CFErrorRef *err)
182  {
183      __block CFTypeRef objects = NULL;
184  
185      dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
186  
187      const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
188      dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
189      dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
190  
191      CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
192      {
193          secinfo("sync", "SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues);
194          if (error)
195              secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error);
196          objects = CFRetainSafe(returnedValues);
197  
198          secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects);
199          dispatch_semaphore_signal(waitSemaphore);
200      };
201  
202      SOSCloudKeychainSynchronizeAndWait(generalq, replyBlock);
203  
204  	dispatch_semaphore_wait(waitSemaphore, finishTime);
205  
206      (void)SOSCCDumpCircleKVSInformation(NULL);
207      fprintf(outFile, "\n");
208      return false;
209  }
210  
211  static void dumpStringSet(CFStringRef label, CFSetRef s) {
212      if(!s || !label) return;
213      
214      printmsg(CFSTR("%@: { "), label);
215      __block bool first = true;
216      CFSetForEach(s, ^(const void *p) {
217          CFStringRef fmt = CFSTR(", %@");
218          if(first) {
219              fmt = CFSTR("%@");
220          }
221          CFStringRef string = (CFStringRef) p;
222          printmsg(fmt, string);
223          first=false;
224      });
225      printmsg(CFSTR(" }\n"), NULL);
226  }
227  
228  static bool dumpMyPeer(CFErrorRef *error) {
229      SOSPeerInfoRef myPeer = SOSCCCopyMyPeerInfo(error);
230  
231      if (!myPeer) return false;
232      
233      CFStringRef peerID = SOSPeerInfoGetPeerID(myPeer);
234      CFStringRef peerName = SOSPeerInfoGetPeerName(myPeer);
235      CFIndex peerVersion = SOSPeerInfoGetVersion(myPeer);
236      bool retirement = SOSPeerInfoIsRetirementTicket(myPeer);
237      
238      printmsg(CFSTR("Peer Name: %@    PeerID: %@   Version: %d\n"), peerName, peerID, peerVersion);
239      if(retirement) {
240          CFDateRef retdate = SOSPeerInfoGetRetirementDate(myPeer);
241          printmsg(CFSTR("Retired: %@\n"), retdate);
242  
243      }
244      
245      if(peerVersion >= 2) {
246          CFMutableSetRef views = SOSPeerInfoV2DictionaryCopySet(myPeer, sViewsKey);
247          CFStringRef serialNumber = SOSPeerInfoV2DictionaryCopyString(myPeer, sSerialNumberKey);
248          CFBooleanRef preferIDS = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDS);
249          CFBooleanRef preferIDSFragmentation = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDSFragmentation);
250          CFBooleanRef preferIDSACKModel = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDSACKModel);
251          CFStringRef transportType = SOSPeerInfoV2DictionaryCopyString(myPeer, sTransportType);
252          CFStringRef idsDeviceID = SOSPeerInfoV2DictionaryCopyString(myPeer, sDeviceID);
253  
254          printmsg(CFSTR("Serial#: %@   PrefIDS#: %@   PrefFragmentation#: %@   PrefACK#: %@   transportType#: %@   idsDeviceID#: %@\n"),
255                   serialNumber, preferIDS, preferIDSFragmentation, preferIDSACKModel, transportType, idsDeviceID);
256  
257          printmsg(CFSTR("Serial#: %@\n"),
258                   serialNumber);
259          dumpStringSet(CFSTR("             Views: "), views);
260  
261  
262          CFReleaseSafe(serialNumber);
263          CFReleaseSafe(preferIDS);
264          CFReleaseSafe(preferIDSFragmentation);
265          CFReleaseSafe(views);
266          CFReleaseSafe(transportType);
267          CFReleaseSafe(idsDeviceID);
268      }
269  
270      bool ret = myPeer != NULL;
271      CFReleaseNull(myPeer);
272      return ret;
273  }
274  
275  static bool setBag(char *itemName, CFErrorRef *err)
276  {
277      __block bool success = false;
278      __block CFErrorRef error = NULL;
279  
280      CFStringRef random = SecPasswordCreateWithRandomDigits(10, NULL);
281  
282      CFStringPerformWithUTF8CFData(random, ^(CFDataRef stringAsData) {
283          if (0 == strncasecmp(optarg, "single", 6) || 0 == strncasecmp(optarg, "all", 3)) {
284              bool includeV0 = (0 == strncasecmp(optarg, "all", 3));
285              printmsg(CFSTR("Setting iCSC single using entropy from string: %@\n"), random);
286              CFDataRef aks_bag = SecAKSCopyBackupBagWithSecret(CFDataGetLength(stringAsData), (uint8_t*)CFDataGetBytePtr(stringAsData), &error);
287  
288              if (aks_bag) {
289                  success = SOSCCRegisterSingleRecoverySecret(aks_bag, includeV0, &error);
290                  if (!success) {
291                      printmsg(CFSTR("Failed registering single secret %@"), error);
292                      CFReleaseNull(aks_bag);
293                  }
294              } else {
295                  printmsg(CFSTR("Failed to create aks_bag: %@"), error);
296              }
297              CFReleaseNull(aks_bag);
298          } else if (0 == strncasecmp(optarg, "device", 6)) {
299              printmsg(CFSTR("Setting Device Secret using entropy from string: %@\n"), random);
300  
301              SOSPeerInfoRef me = SOSCCCopyMyPeerWithNewDeviceRecoverySecret(stringAsData, &error);
302  
303              success = me != NULL;
304  
305              if (!success)
306                  printmsg(CFSTR("Failed: %@\n"), err);
307              CFReleaseNull(me);
308          } else {
309              printmsg(CFSTR("Unrecognized argument to -b %s\n"), optarg);
310          }
311      });
312  
313  
314      return success;
315  }
316  
317  static void prClientViewState(char *label, bool result) {
318      fprintf(outFile, "Sync Status for %s: %s\n", label, (result) ? "enabled": "not enabled");
319  }
320  
321  static bool clientViewStatus(CFErrorRef *error) {
322      prClientViewState("KeychainV0", SOSCCIsIcloudKeychainSyncing());
323      prClientViewState("Safari", SOSCCIsSafariSyncing());
324      prClientViewState("AppleTV", SOSCCIsAppleTVSyncing());
325      prClientViewState("HomeKit", SOSCCIsHomeKitSyncing());
326      prClientViewState("Wifi", SOSCCIsWiFiSyncing());
327      prClientViewState("AlwaysOnNoInitialSync", SOSCCIsContinuityUnlockSyncing());
328      return false;
329  }
330  
331  #pragma mark -
332  #pragma mark --remove-peer
333  
334  static void
335  add_matching_peerinfos(CFMutableArrayRef list, CFArrayRef spids, CFArrayRef (*copy_peer_func)(CFErrorRef *))
336  {
337      CFErrorRef error;
338      CFArrayRef peers;
339      SOSPeerInfoRef pi;
340      CFStringRef spid;
341      CFIndex i, j;
342  
343      peers = copy_peer_func(&error);
344      if (peers != NULL) {
345          for (i = 0; i < CFArrayGetCount(peers); i++) {
346              pi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(peers, i);
347              for (j = 0; j < CFArrayGetCount(spids); j++) {
348                  spid = (CFStringRef)CFArrayGetValueAtIndex(spids, j);
349                  if (CFStringGetLength(spid) < 8) {
350                      continue;
351                  }
352                  if (CFStringHasPrefix(SOSPeerInfoGetPeerID(pi), spid)) {
353                      CFArrayAppendValue(list, pi);
354                  }
355              }
356          }
357          CFRelease(peers);
358      } else {
359          // unlikely
360          CFShow(error);
361          CFRelease(error);
362      }
363  }
364  
365  static CFArrayRef
366  copy_peerinfos(CFArrayRef spids)
367  {
368      CFMutableArrayRef matches;
369  
370      matches = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
371      add_matching_peerinfos(matches, spids, SOSCCCopyValidPeerPeerInfo);
372      add_matching_peerinfos(matches, spids, SOSCCCopyNotValidPeerPeerInfo);
373      add_matching_peerinfos(matches, spids, SOSCCCopyRetirementPeerInfo);
374  
375      return matches;
376  }
377  
378  static bool
379  doRemovePeers(CFArrayRef peerids, CFErrorRef *error)
380  {
381      bool success = false;
382      CFArrayRef peers = NULL;
383      CFErrorRef localError = NULL;
384      CFIndex i;
385      char buf[16];
386  
387      peers = copy_peerinfos(peerids);
388      if (peers == NULL || CFArrayGetCount(peers) == 0) {
389          fprintf(stdout, "No matching peers to remove.\n");
390          success = true;
391          goto done;
392      }
393  
394      fprintf(stdout, "Matched the following devices:\n");
395      for (i = 0; i < CFArrayGetCount(peers); i++) {
396          // Ugly.
397          CFShow(CFArrayGetValueAtIndex(peers, i));
398      }
399  
400      if (readpassphrase("Confirm removal (y/N): ", buf, sizeof(buf), RPP_ECHO_ON | RPP_FORCEUPPER) == NULL) {
401          goto done;
402      }
403  
404      if (buf[0] != 'Y') {
405          success = true;
406          goto done;
407      }
408  
409      success = SOSCCRemovePeersFromCircle(peers, &localError);
410      if (!success && isSOSErrorCoded(localError, kSOSErrorPrivateKeyAbsent)) {
411          CFReleaseNull(localError);
412  
413          success = promptAndTryPassword(&localError);
414          if (success) {
415              success = SOSCCRemovePeersFromCircle(peers, &localError);
416          }
417      }
418  
419  done:
420      CFReleaseNull(peers);
421  
422      if (!success && error != NULL) {
423          *error = localError;
424      } else {
425          CFReleaseNull(localError);
426      }
427  
428      return success;
429  }
430  
431  #pragma mark -
432  
433  // enable, disable, accept, reject, status, Reset, Clear
434  int
435  keychain_sync(int argc, char * const *argv)
436  {
437      /*
438  	 "Keychain Syncing"
439  	 "    -d     disable"
440  	 "    -e     enable (join/create circle)"
441  	 "    -i     info (current status)"
442  	 "    -m     dump my peer"
443  	 "
444  	 "Account/Circle Management"
445  	 "    -a     accept all applicants"
446  	 "    -r     reject all applicants"
447       "    -b     device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
448  
449  	 "    -N     (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
450  	 "    -O     reset to offering"
451  	 "    -R     reset circle"
452       "    -o     list view unaware peers in circle"
453       "    -0     boot view unaware peers from circle"
454       "    -5     cleanup old KVS keys in KVS"
455  	 "
456       "Circle Tools\n"
457       "    --remove-peer SPID     Remove a peer identified by the first 8 or more\n"
458       "                           characters of its spid. Specify multiple times to\n"
459       "                           remove more than one peer.\n"
460  	 "Password"
461  	 "    -P     [label:]password  set password (optionally for a given label) for sync"
462  	 "    -T     [label:]password  try password (optionally for a given label) for sync"
463  	 "
464  	 "KVS"
465  	 "    -k     pend all registered kvs keys"
466  	 "    -C     clear all values from KVS"
467  	 "    -D     [itemName]  dump contents of KVS"
468       "    -W     sync and dump"
469  	 "
470  	 "Misc"
471  	 "    -v     [enable|disable|query:viewname] enable, disable, or query my PeerInfo's view set"
472  	 "             viewnames are: keychain|masterkey|iclouddrive|photos|cloudkit|escrow|fde|maildrop|icloudbackup|notes|imessage|appletv|homekit|"
473       "                            wifi|passwords|creditcards|icloudidentity|othersyncable"
474       "    -L     list all known view and their status"
475  	 "    -U     purge private key material cache\n"
476       "    -V     Report View Sync Status on all known clients.\n"
477       */
478      enum {
479          SYNC_REMOVE_PEER,
480      };
481      int action = -1;
482      const struct option longopts[] = {
483          { "remove-peer",    required_argument,  &action,    SYNC_REMOVE_PEER, },
484          { NULL,             0,                  NULL,       0, },
485      };
486      int ch, result = 0;
487      CFErrorRef error = NULL;
488      bool hadError = false;
489      CFMutableArrayRef peers2remove = NULL;
490      SOSLogSetOutputTo(NULL, NULL);
491  
492      while ((ch = getopt_long(argc, argv, "ab:deikmorv:NCDLOP:RT:UWV05", longopts, NULL)) != -1) {
493          switch  (ch) {
494              case 'a':
495              {
496                  CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
497                  if (applicants) {
498                      hadError = !SOSCCAcceptApplicants(applicants, &error);
499                      CFRelease(applicants);
500                  } else {
501                      fprintf(errFile, "No applicants to accept\n");
502                  }
503                  break;
504              }
505              case 'b':
506              {
507                  hadError = setBag(optarg, &error);
508                  break;
509              }
510              case 'd':
511              {
512                  fprintf(outFile, "Turning OFF keychain syncing\n");
513                  hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
514                  break;
515              }
516              case 'e':
517              {
518                  fprintf(outFile, "Turning ON keychain syncing\n");
519                  hadError = requestToJoinCircle(&error);
520                  break;
521              }
522              case 'i':
523              {
524                  SOSCCDumpCircleInformation();
525                  SOSCCDumpEngineInformation();
526                  break;
527              }
528              case 'k':
529              {
530                  notify_post("com.apple.security.cloudkeychain.forceupdate");
531                  break;
532              }
533              case 'm':
534              {
535                  hadError = !dumpMyPeer(&error);
536                  break;
537              }
538              case 'o':
539              {
540                  SOSCCDumpViewUnwarePeers();
541                  break;
542              }
543              case 'r':
544              {
545                  CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
546                  if (applicants)	{
547                      hadError = !SOSCCRejectApplicants(applicants, &error);
548                      CFRelease(applicants);
549                  } else {
550                      fprintf(errFile, "No applicants to reject\n");
551                  }
552                  break;
553              }
554              case 'v':
555              {
556                  hadError = !viewcmd(optarg, &error);
557                  break;
558              }
559              case 'C':
560              {
561                  hadError = clearAllKVS(&error);
562                  break;
563              }
564              case 'D':
565              {
566                  (void)SOSCCDumpCircleKVSInformation(optarg);
567                  break;
568              }
569              case 'L':
570              {
571                  hadError = !listviewcmd(&error);
572                  break;
573              }
574              case 'N':
575              {
576                  hadError = !SOSCCAccountSetToNew(&error);
577                  if (!hadError)
578                      notify_post(kSOSCCCircleChangedNotification);
579                  break;
580              }
581              case 'O':
582              {
583                  hadError = !SOSCCResetToOffering(&error);
584                  break;
585              }
586              case 'P':
587              {
588                  hadError = setPassword(optarg, &error);
589                  break;
590              }
591              case 'R':
592              {
593                  hadError = !SOSCCResetToEmpty(&error);
594                  break;
595              }
596              case 'T':
597              {
598                  hadError = tryPassword(optarg, &error);
599                  break;
600              }
601              case 'U':
602              {
603                  hadError = !SOSCCPurgeUserCredentials(&error);
604                  break;
605              }
606              case 'V':
607              {
608                  hadError = clientViewStatus(&error);
609                  break;
610              }
611              case 'W':
612              {
613                  hadError = syncAndWait(&error);
614                  break;
615              }
616              case '0':
617              {
618                  CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
619                  if (unawares) {
620                      hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
621                  } else {
622                      hadError = true;
623                  }
624                  CFReleaseNull(unawares);
625                  break;
626              }
627              case '5' :
628              {
629                  bool result = SOSCCCleanupKVSKeys(&error);
630                  if(result)
631                  {
632                      printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
633                  }else {
634                      hadError = true;
635                  }
636                  break;
637              }
638              case 0:
639              {
640                  if (action == SYNC_REMOVE_PEER) {
641                      CFStringRef optstr = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
642                      if (peers2remove == NULL) {
643                          peers2remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
644                      }
645                      CFArrayAppendValue(peers2remove, optstr);
646                      CFReleaseNull(optstr);
647                  } else {
648                      return SHOW_USAGE_MESSAGE;
649                  }
650                  break;
651              }
652              case '?':
653              default:
654                  return SHOW_USAGE_MESSAGE;
655          }
656      }
657  
658      if (peers2remove != NULL) {
659          hadError = !doRemovePeers(peers2remove, &error);
660          CFRelease(peers2remove);
661      }
662  
663      if (hadError) {
664          printerr(CFSTR("Error: %@\n"), error);
665      }
666  
667      return result;
668  }