/ keychain / SecureObjectSync / Tool / accountCirclesViewsPrint.m
accountCirclesViewsPrint.m
  1  //
  2  //  accountCirclesViewsPrint.c
  3  //  Security
  4  //
  5  //  Created by Richard Murphy on 12/8/16.
  6  //
  7  //
  8  
  9  #include "accountCirclesViewsPrint.h"
 10  
 11  //
 12  //  SOSSysdiagnose.c
 13  //  sec
 14  //
 15  //  Created by Richard Murphy on 1/27/16.
 16  //
 17  //
 18  
 19  
 20  #include <stdio.h>
 21  #include <stdlib.h>
 22  #include <string.h>
 23  #include <unistd.h>
 24  #include <sys/utsname.h>
 25  #include <sys/stat.h>
 26  #include <time.h>
 27  #include <notify.h>
 28  #include <pwd.h>
 29  
 30  #include <Security/SecItem.h>
 31  
 32  #include <CoreFoundation/CoreFoundation.h>
 33  #include <CoreFoundation/CFPriv.h>
 34  
 35  #include <Security/SecureObjectSync/SOSCloudCircle.h>
 36  #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
 37  #include <Security/SecureObjectSync/SOSPeerInfo.h>
 38  #include "keychain/SecureObjectSync/SOSPeerInfoPriv.h"
 39  #include "keychain/SecureObjectSync/SOSPeerInfoV2.h"
 40  #include "keychain/SecureObjectSync/SOSUserKeygen.h"
 41  #include "keychain/SecureObjectSync/SOSKVSKeys.h"
 42  #include "keychain/securityd/SOSCloudCircleServer.h"
 43  #include <Security/SecOTRSession.h>
 44  #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
 45  
 46  #include <utilities/SecCFWrappers.h>
 47  #include <utilities/debugging.h>
 48  
 49  #include "SecurityTool/sharedTool/readline.h"
 50  
 51  #include "keychain_log.h"
 52  #include "secToolFileIO.h"
 53  #include "secViewDisplay.h"
 54  
 55  
 56  #include <Security/SecPasswordGenerate.h>
 57  
 58  #define MAXKVSKEYTYPE kUnknownKey
 59  #define DATE_LENGTH 18
 60  
 61  #include <utilities/SecCFWrappers.h>
 62  
 63  
 64  static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus)
 65  {
 66      switch (ccstatus)
 67      {
 68          case kSOSCCInCircle:        return "In Circle";
 69          case kSOSCCNotInCircle:     return "Not in Circle";
 70          case kSOSCCRequestPending:  return "Request pending";
 71          case kSOSCCCircleAbsent:    return "Circle absent";
 72          case kSOSCCError:           return "Circle error";
 73              
 74          default:
 75              return "<unknown ccstatus>";
 76              break;
 77      }
 78  }
 79  
 80  static const char *
 81  getSOSCCLastDepartureReasonDescription(enum DepartureReason reason)
 82  {
 83      switch (reason) {
 84  #define CASE_REASON(x) case kSOS##x: return #x
 85      CASE_REASON(DepartureReasonError);
 86      CASE_REASON(NeverLeftCircle);
 87      CASE_REASON(WithdrewMembership);
 88      CASE_REASON(MembershipRevoked);
 89      CASE_REASON(LeftUntrustedCircle);
 90      CASE_REASON(NeverAppliedToCircle);
 91      CASE_REASON(DiscoveredRetirement); // we should all be so lucky
 92      CASE_REASON(LostPrivateKey);
 93      CASE_REASON(PasswordChanged);
 94  #undef CASE_REASON
 95      default:
 96          return "Unknown";
 97      }
 98  }
 99  
100  static void printPeerInfos(char *label, CFStringRef mypeerID, CFArrayRef (^copyPeers)(CFErrorRef *error)) {
101      CFErrorRef error = NULL;
102      CFArrayRef ppi = copyPeers(&error);
103  
104      if(ppi) {
105          printmsg(CFSTR("%s count: %ld\n"), label, (long)CFArrayGetCount(ppi));
106          CFArrayForEach(ppi, ^(const void *value) {
107              char buf[160];
108              SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
109              if(!peer) { return; }
110              CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
111              CFStringRef devtype = SOSPeerInfoGetPeerDeviceType(peer);
112              CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
113              CFStringRef transportType = CFSTR("KVS");
114              CFStringRef deviceID = CFSTR("");
115              CFStringRef machineID = CFSTR("");
116              CFDictionaryRef gestalt = SOSPeerInfoCopyPeerGestalt(peer);
117              CFStringRef osVersion = NULL;
118              if(gestalt) {
119                  osVersion = CFDictionaryGetValue(gestalt, CFSTR("OSVersion"));
120              } else {
121                  osVersion = CFSTR("Unknown");
122              }
123  
124              if(SOSPeerInfoVersionHasV2Data(peer)){
125                  CFDictionaryRef v2Dictionary = peer->v2Dictionary;
126                  if(v2Dictionary) {
127                      transportType = CFDictionaryGetValue(v2Dictionary, CFSTR("TransportType"));
128                      deviceID = CFDictionaryGetValue(v2Dictionary, CFSTR("DeviceID"));
129                      machineID = CFDictionaryGetValue(v2Dictionary, CFSTR("MachineIDKey"));
130                  }
131              }
132              char *pname = CFStringToCString(peerName);
133              char *dname = CFStringToCString(devtype);
134              char *tname = CFStringToCString(transportType);
135              char *iname = CFStringToCString(deviceID);
136              char *mname = CFStringToCString(machineID);
137              const char *me = CFEqualSafe(mypeerID, peerID) ? "me>" : "   ";
138  
139  
140              snprintf(buf, 160, "%s %s: %-16s dev:%-16s trn:%-16s devid:%-36s mid: %-36s", me, label, pname, dname, tname, iname, mname);
141  
142              free(pname);
143              free(dname);
144              free(tname);
145              free(iname);
146              free(mname);
147  
148              // %s in (Core)Foundation format strings treats the string as MacRoman, need to do this to guarantee UTF8 handling
149              CFStringRef bufstr = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
150              CFStringRef pid = SOSPeerInfoGetPeerID(peer);
151              CFIndex vers = SOSPeerInfoGetVersion(peer);
152              bool isCKKSForAll = SOSPeerInfoSupportsCKKSForAll(peer);
153              printmsg(CFSTR("%@ pid:%@ V%d %@ OS:%@\n"), bufstr, pid, vers, isCKKSForAll ? CFSTR("c4a") : CFSTR("SOS"), osVersion ?: CFSTR(""));
154              CFRelease(bufstr);
155  
156              CFReleaseNull(gestalt);
157          });
158      } else {
159          printmsg(CFSTR("No %s, error: %@\n"), label, error);
160      }
161      CFReleaseNull(ppi);
162      CFReleaseNull(error);
163  }
164  
165  void SOSCCDumpCircleInformation()
166  {
167      CFErrorRef error = NULL;
168      CFArrayRef generations = NULL;
169      bool is_accountKeyIsTrusted = false;
170      __block int count = 0;
171  
172      
173      SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
174      printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus), ccstatus);
175      if (error != NULL) {
176          printmsg(CFSTR("Error checking circle status: %@\n"), error);
177      }
178      CFReleaseNull(error);
179      
180      enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&error);
181      printmsg(CFSTR("LastDepartureReason: %s (%d)\n"), getSOSCCLastDepartureReasonDescription(departureReason), departureReason);
182      if (error != NULL) {
183          printmsg(CFSTR("Error checking last departure reason error: %@\n"), error);
184      }
185      CFReleaseNull(error);
186  
187      is_accountKeyIsTrusted = SOSCCValidateUserPublic(&error);
188      if(is_accountKeyIsTrusted)
189          printmsg(CFSTR("Account user public is trusted\n"));
190      else
191          printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error);
192      CFReleaseNull(error);
193      
194      generations = SOSCCCopyGenerationPeerInfo(&error);
195      if(generations) {
196          CFArrayForEach(generations, ^(const void *value) {
197              count++;
198              if(count%2 == 0)
199                  printmsg(CFSTR("Circle name: %@, "),value);
200              
201              if(count%2 != 0) {
202                  CFStringRef genDesc = SOSGenerationCountCopyDescription(value);
203                  printmsg(CFSTR("Generation Count: %@"), genDesc);
204                  CFReleaseNull(genDesc);
205              }
206              printmsg(CFSTR("%s\n"), "");
207          });
208      } else {
209          printmsg(CFSTR("No generation count: %@\n"), error);
210      }
211      CFReleaseNull(generations);
212      CFReleaseNull(error);
213  
214      SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(NULL);
215      CFStringRef mypeerID = SOSPeerInfoGetPeerID(me);
216  
217      printPeerInfos("     Peers", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyValidPeerPeerInfo(error); });
218      printPeerInfos("   Invalid", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyNotValidPeerPeerInfo(error); });
219      printPeerInfos("   Retired", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyRetirementPeerInfo(error); });
220      printPeerInfos("    Concur", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyConcurringPeerPeerInfo(error); });
221      printPeerInfos("Applicants", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyApplicantPeerInfo(error); });
222      
223      CFReleaseNull(me);
224      CFReleaseNull(error);
225  }
226  
227  void
228  SOSCCDumpEngineInformation(void)
229  {
230      CFErrorRef error = NULL;
231  
232      printmsg(CFSTR("Engine state:\n"));
233      if (!SOSCCForEachEngineStateAsString(&error, ^(CFStringRef oneStateString) {
234          printmsg(CFSTR("%@\n"), oneStateString);
235      })) {
236          printmsg(CFSTR("No engine state, got error: %@\n"), error);
237      }
238  }
239  
240  // security sync -o
241  void
242  SOSCCDumpViewUnwarePeers(void)
243  {
244      SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(NULL);
245      CFStringRef mypeerID = SOSPeerInfoGetPeerID(me);
246  
247      printPeerInfos("view-unaware", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyViewUnawarePeerInfo(error); });
248  
249      CFReleaseNull(me);
250  }
251  
252  /* KVS Dumping Support for iCloud Keychain */
253  
254  static CFTypeRef getObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup)
255  {
256      __block CFTypeRef object = NULL;
257      
258      const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
259      dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
260      dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
261      
262      dispatch_group_enter(dgroup);
263      
264      CloudKeychainReplyBlock replyBlock =
265      ^ (CFDictionaryRef returnedValues, CFErrorRef error)
266      {
267          secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
268          object = returnedValues;
269          if (object)
270              CFRetain(object);
271          if (error)
272          {
273              secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
274          }
275          dispatch_group_leave(dgroup);
276          secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
277          dispatch_semaphore_signal(waitSemaphore);
278      };
279      
280      if (!keysToGet)
281          SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
282      else
283          SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock);
284      
285      dispatch_semaphore_wait(waitSemaphore, finishTime);
286      if (object && (CFGetTypeID(object) == CFNullGetTypeID()))   // return a NULL instead of a CFNull
287      {
288          CFRelease(object);
289          object = NULL;
290      }
291      secerror("returned: %@", object);
292      return object;
293  }
294  
295  static CFStringRef printFullDataString(CFDataRef data){
296      __block CFStringRef fullData = NULL;
297      
298      BufferPerformWithHexString(CFDataGetBytePtr(data), CFDataGetLength(data), ^(CFStringRef dataHex) {
299          fullData = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), dataHex);
300      });
301      
302      return fullData;
303  }
304  
305  static void displayLastKeyParameters(CFTypeRef key, CFTypeRef value)
306  {
307      CFDataRef valueAsData = asData(value, NULL);
308      if(valueAsData){
309          CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH));
310          CFDataRef keyParameterData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData));
311          CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8);
312          CFStringRef keyParameterDescription = UserParametersDescription(keyParameterData);
313          if(keyParameterDescription)
314              printmsg(CFSTR("%@: %@: %@\n"), key, dateString, keyParameterDescription);
315          else
316              printmsg(CFSTR("%@: %@\n"), key, printFullDataString(value));
317          CFReleaseNull(dateString);
318          CFReleaseNull(keyParameterData);
319          CFReleaseNull(dateData);
320          CFReleaseNull(keyParameterDescription);
321      }
322      else{
323          printmsg(CFSTR("%@: %@\n"), key, value);
324      }
325  }
326  
327  static void displayKeyParameters(CFTypeRef key, CFTypeRef value)
328  {
329      if(isData(value)){
330          CFStringRef keyParameterDescription = UserParametersDescription((CFDataRef)value);
331          
332          if(keyParameterDescription)
333              printmsg(CFSTR("%@: %@\n"), key, keyParameterDescription);
334          else
335              printmsg(CFSTR("%@: %@\n"), key, value);
336          
337          CFReleaseNull(keyParameterDescription);
338      }
339      else{
340          printmsg(CFSTR("%@: %@\n"), key, value);
341      }
342  }
343  
344  static void displayLastCircle(CFTypeRef key, CFTypeRef value)
345  {
346      CFDataRef valueAsData = asData(value, NULL);
347      if(valueAsData){
348          CFErrorRef localError = NULL;
349          
350          CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH));
351          CFDataRef circleData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData));
352          CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8);
353          SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) circleData, &localError);
354          
355          if(circle){
356              CFIndex size = 5;
357              CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size);
358              CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL);
359              printmsgWithFormatOptions(format, CFSTR("%@: %@: %@\n"), key, dateString, circle);
360              CFReleaseNull(idLength);
361              CFReleaseNull(format);
362              
363          }
364          else
365              printmsg(CFSTR("%@: %@\n"), key, printFullDataString(circleData));
366          
367          CFReleaseNull(dateString);
368          CFReleaseNull(circleData);
369          CFReleaseSafe(circle);
370          CFReleaseNull(dateData);
371          CFReleaseNull(localError);
372      }
373      else{
374          printmsg(CFSTR("%@: %@\n"), key, value);
375      }
376  }
377  
378  static void displayCircle(CFTypeRef key, CFTypeRef value)
379  {
380      CFDataRef circleData = (CFDataRef)value;
381      
382      CFErrorRef localError = NULL;
383      if (isData(circleData))
384      {
385          CFIndex size = 5;
386          CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size);
387          CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL);
388          SOSCircleRef circle = SOSCircleCreateFromData(NULL, circleData, &localError);
389          printmsgWithFormatOptions(format, CFSTR("%@: %@\n"), key, circle);
390          CFReleaseSafe(circle);
391          CFReleaseNull(idLength);
392          CFReleaseNull(format);
393          
394      }
395      else
396          printmsg(CFSTR("%@: %@\n"), key, value);
397  }
398  
399  static void displayMessage(CFTypeRef key, CFTypeRef value)
400  {
401      CFDataRef message = (CFDataRef)value;
402      if(isData(message)){
403          const char* messageType = SecOTRPacketTypeString(message);
404          printmsg(CFSTR("%@: %s: %ld\n"), key, messageType, CFDataGetLength(message));
405      }
406      else
407          printmsg(CFSTR("%@: %@\n"), key, value);
408  }
409  
410  static void printEverything(CFTypeRef objects)
411  {
412      CFDictionaryForEach(objects, ^(const void *key, const void *value) {
413          if (isData(value))
414          {
415              printmsg(CFSTR("%@: %@\n\n"), key, printFullDataString(value));
416          }
417          else
418              printmsg(CFSTR("%@: %@\n"), key, value);
419      });
420      
421  }
422  
423  static void decodeForKeyType(CFTypeRef key, CFTypeRef value, SOSKVSKeyType type){
424      switch (type) {
425          case kCircleKey:
426              displayCircle(key, value);
427              break;
428          case kRetirementKey:
429          case kMessageKey:
430              displayMessage(key, value);
431              break;
432          case kParametersKey:
433              displayKeyParameters(key, value);
434              break;
435          case kLastKeyParameterKey:
436              displayLastKeyParameters(key, value);
437              break;
438          case kLastCircleKey:
439              displayLastCircle(key, value);
440              break;
441          case kInitialSyncKey:
442          case kAccountChangedKey:
443          case kDebugInfoKey:
444          case kRingKey:
445          default:
446              printmsg(CFSTR("%@: %@\n"), key, value);
447              break;
448      }
449  }
450  
451  static void decodeAllTheValues(CFTypeRef objects){
452      SOSKVSKeyType keyType = 0;
453      __block bool didPrint = false;
454      
455      for (keyType = 0; keyType <= MAXKVSKEYTYPE; keyType++){
456          CFDictionaryForEach(objects, ^(const void *key, const void *value) {
457              if(SOSKVSKeyGetKeyType(key) == keyType){
458                  decodeForKeyType(key, value, keyType);
459                  didPrint = true;
460              }
461          });
462          if(didPrint)
463              printmsg(CFSTR("%@\n"), CFSTR(""));
464          didPrint = false;
465      }
466  }
467  
468  bool SOSCCDumpCircleKVSInformation(char *itemName) {
469      CFArrayRef keysToGet = NULL;
470      if (itemName)
471      {
472          CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
473          fprintf(outFile, "Retrieving %s from KVS\n", itemName);
474          keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
475          CFReleaseSafe(itemStr);
476      }
477      dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
478      dispatch_group_t work_group = dispatch_group_create();
479      CFTypeRef objects = getObjectsFromCloud(keysToGet, generalq, work_group);
480      CFReleaseSafe(keysToGet);
481      if (objects)
482      {
483          fprintf(outFile, "All keys and values straight from KVS\n");
484          printEverything(objects);
485          fprintf(outFile, "\nAll values in decoded form...\n");
486          decodeAllTheValues(objects);
487      }
488      fprintf(outFile, "\n");
489      return true;
490  }