]> git.saurik.com Git - apple/security.git/blobdiff - Security/utilities/src/cloud_keychain_diagnose.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / utilities / src / cloud_keychain_diagnose.c
diff --git a/Security/utilities/src/cloud_keychain_diagnose.c b/Security/utilities/src/cloud_keychain_diagnose.c
new file mode 100644 (file)
index 0000000..155c9f4
--- /dev/null
@@ -0,0 +1,1245 @@
+/*
+ * clang cloud_keychain_diagnose.c -laks -framework CoreFoundation -framework IOKit -framework Security -o /tmp/cloud_keychain_diagnose
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFPriv.h>
+
+#if !TARGET_IPHONE_SIMULATOR
+
+/* Header Declarations */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <asl.h>
+#include <asl_msg.h>
+
+#if TARGET_OS_EMBEDDED
+#include <asl_core.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <libaks.h>
+
+#include "SOSCloudCircle.h"
+#include "SOSPeerInfo.h"
+
+
+/* Constant Declarations */
+#define SUCCESS 0
+#define FAILURE -1
+
+#define MAX_PATH_LEN                1024
+#define SUFFIX_LENGTH               4
+#define BUFFER_SIZE                 1024
+#define MAX_DATA_RATE               32
+
+/* External CloudKeychain Bridge Types */
+typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
+extern void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock);
+
+/* External AppleKeyStore Types */
+enum {
+ my_keybag_state_bio_unlock = 1 << 3
+};
+
+
+/* Dictionary Dump State */
+struct dict_dump_state
+{
+    FILE                *log_file;
+    CFDictionaryRef     dict;
+    unsigned int        indent_level;
+};
+
+/* Static Function Declarations */
+static
+void
+usage();
+
+static
+int
+gather_diagnostics();
+
+static
+int
+enable_cloud_keychain_diagnostics(
+    const unsigned int enable_flag);
+
+static
+int
+build_log_path(
+    char *log_path);
+
+static
+int
+dump_system_information(
+    FILE *log_file);
+
+static
+int
+dump_circle_state(
+    FILE *log_file);
+
+static
+int
+dump_keychain_sync_kvs(
+    FILE *log_file);
+
+static
+void
+dump_dict(
+    FILE *log_file,
+    CFDictionaryRef dict,
+    const unsigned int indent_level);
+
+static
+void
+dump_dict_applier(
+    const void *key,
+    const void *value,
+    void *context);
+
+static
+int
+dump_asl_sender(
+    FILE *log_file,
+    const char *asl_sender);
+
+static
+void
+dump_cferror(
+    FILE *log_file,
+    const char *description,
+    CFErrorRef error);
+
+/* Function Definitions */
+int
+main(
+    int argc,
+    char **argv)
+{
+    int result = EXIT_FAILURE;
+    
+    /* Parse the arguments. */
+    if (argc > 2) {
+    
+        usage();
+    }
+
+    /* Should we just gather logs and status? */
+    if (argc == 1) {
+    
+        if (gather_diagnostics()) {
+            
+            fprintf(stderr, "Could not gather diagnostics\n");
+            goto BAIL;
+        }        
+    } else {
+    
+        /* Should we enable or disable logging? */
+        if (strncmp(argv[1], "enable", 6) == 0) {
+        
+            /* Enable. */
+            if (enable_cloud_keychain_diagnostics(1)) {
+            
+                fprintf(stderr, "Could not enable additional cloud keychain diagnostics\n");
+                goto BAIL;
+            }            
+        } else if (strncmp(argv[1], "disable", 7) == 0) {
+
+            /* Enable. */
+            if (enable_cloud_keychain_diagnostics(1)) {
+            
+                fprintf(stderr, "Could not disable additional cloud keychain diagnostics\n");
+                goto BAIL;
+            } 
+        } else {
+        
+            /* Get a job, hippy. */
+            usage();
+        }
+    }
+    
+    /* Set the exit status to success. */
+    result = EXIT_FAILURE;
+    
+BAIL:
+
+    return result;
+}
+
+/* Static Function Definitions */
+static
+void
+usage()
+{
+    fprintf(stderr, "usage: cloud_keychain_diagnose [enable|disable]\n");
+    exit(EXIT_FAILURE);
+}
+
+static
+int
+gather_diagnostics()
+{
+    int     result = FAILURE;
+    char    log_path[MAX_PATH_LEN] = "";
+    int     log_fd = -1;
+    FILE    *log_file = NULL;
+    
+    /*
+     * Create the diagnostics file.
+     *
+     * Dump the system information.
+     *     on OS X, defaults read if the shim is active
+     * Dump the circle state.
+     * Dump the raw KVS data.
+     * Dump known ASL logs
+     *
+     * Remaining work to do from rdar://12479351
+     * grab the syslog
+     * query for all items with sync=1
+     * enable KVS logging
+     * enable push notification logging
+     */
+    
+    /* Build the log path. */
+    if (build_log_path(log_path)) {
+    
+        fprintf(stderr, "Could not build the log path\n");
+        goto BAIL;
+    }
+    
+    /* Create it with a randomized suffix. */
+    log_fd = mkstemps(log_path, SUFFIX_LENGTH);
+    if (log_fd == -1) {
+    
+        fprintf(stderr, "Could not create the log file: %s\n", strerror(errno));
+        goto BAIL;
+    }
+    
+    /* Create a file object from the descriptor. */
+    log_file = fdopen(log_fd, "w");
+    if (log_file == NULL) {
+    
+        fprintf(stderr, "Could not recreate the log file: %s\n", strerror(errno));
+        goto BAIL;
+    }
+    
+    log_fd = -1;
+    
+    printf("Writing cloud keychain diagnostics to %s\n", log_path);
+    
+    /* Dump the system information. */
+    if (dump_system_information(log_file)) {
+    
+        fprintf(stderr, "Could not dump the system information\n");
+        goto BAIL;
+    }
+    
+    /* Dump the SOS circle state. */
+    if (dump_circle_state(log_file)) {
+    
+        fprintf(stderr, "Could not dump the SOS circle state\n");
+        goto BAIL;
+    }
+    
+    /* Dump the raw keychain syncing KVS. */
+    if (dump_keychain_sync_kvs(log_file)) {
+    
+        fprintf(stderr, "Could not the raw keychain syncing KVS\n");
+        goto BAIL;
+    }
+    
+    /* 
+     * Dump the various and sundry ASL logs.
+     */
+    
+    if (dump_asl_sender(log_file, "com.apple.kb-service")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for com.apple.kb-service\n");
+        goto BAIL;
+    }
+    
+    if (dump_asl_sender(log_file, "com.apple.securityd")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for com.apple.securityd\n");
+        goto BAIL;
+    }
+    
+    if (dump_asl_sender(log_file, "com.apple.secd")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for com.apple.secd\n");
+        goto BAIL;
+    }
+    
+    if (dump_asl_sender(log_file, "CloudKeychainProxy")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for CloudKeychainProxy\n");
+        goto BAIL;
+    }
+    
+    if (dump_asl_sender(log_file, "securityd")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for securityd\n");
+        goto BAIL;
+    }
+    
+    if (dump_asl_sender(log_file, "secd")) {
+    
+        fprintf(stderr, "Could not dump the ASL log for secd\n");
+        goto BAIL;
+    }
+
+    /* Set the result to success. */
+    result = SUCCESS;
+
+BAIL:
+
+    /* Close the diagnostics file? */
+    if (log_file != NULL) {
+    
+        fclose(log_file);
+        log_file = NULL;
+    }
+    
+    /* Close the diagnostics file descriptor? */
+    if (log_fd != -1) {
+    
+        close(log_fd);
+        log_fd = -1;
+        (void) log_fd;
+    }
+    
+    return result;
+}
+
+static
+int
+enable_cloud_keychain_diagnostics(
+    const unsigned int enable_flag)
+{
+    int result = FAILURE;
+    
+    /* Set the result to success. */
+    result = SUCCESS;
+    
+    return result;
+}
+
+static
+int
+build_log_path(
+    char *log_path)
+{
+    int             result = FAILURE;
+    time_t          now;
+    struct tm       *time_cube;
+    CFDictionaryRef system_version_dict = NULL;
+    CFStringRef     product_name = NULL;
+    
+    /* Get the current time. */
+    now = time(NULL);
+    
+    /* Convert the time into something usable. */
+    time_cube = localtime(&now);
+    if (time_cube == NULL) {
+    
+        fprintf(stderr, "I don't know what time it is.\n");
+        goto BAIL;
+    }
+    
+    /* Copy the system version dictionary. */
+    system_version_dict = _CFCopySystemVersionDictionary();
+    if (system_version_dict == NULL) {
+    
+        fprintf(stderr, "Could not copy the system version dictionary\n");
+        goto BAIL;
+    }
+    
+    /* Extract the product name. */
+    product_name = CFDictionaryGetValue(system_version_dict, _kCFSystemVersionProductNameKey);
+    if (product_name == NULL) {
+    
+        fprintf(stderr, "Could not extract the product name from the system version dictionary\n");
+        goto BAIL;
+    }
+
+    /* Is this a Mac? */
+    if (CFEqual(product_name, CFSTR("Mac OS X"))) {
+    
+        /* Prepare the file template to go into /tmp. */
+        snprintf(
+            log_path,
+            MAX_PATH_LEN,
+            "/tmp/cloud_keychain_diagnostics.%d_%d_%d.%d%d%d.XXXX.txt",
+            1900 + time_cube->tm_year,
+            time_cube->tm_mon,
+            time_cube->tm_mday,
+            time_cube->tm_hour,
+            time_cube->tm_min,
+            time_cube->tm_sec);
+    } else {
+    
+        /* Prepare the file template to go into CrashReporter. */
+        snprintf(
+            log_path,
+            MAX_PATH_LEN,
+            "/Library/Logs/CrashReporter/cloud_keychain_diagnostics.%d_%d_%d.%d%d%d.XXXX.txt",
+            1900 + time_cube->tm_year,
+            time_cube->tm_mon,
+            time_cube->tm_mday,
+            time_cube->tm_hour,
+            time_cube->tm_min,
+            time_cube->tm_sec);
+    }
+    
+    /* Set the result to success. */
+    result = SUCCESS;
+    
+BAIL:
+
+    /* Release the system version dictionary? */
+    if (system_version_dict != NULL) {
+    
+        CFRelease(system_version_dict);
+        system_version_dict = NULL;
+    }
+    
+    return result;
+}
+
+static
+int
+dump_system_information(
+    FILE *log_file)
+{
+    int             result = FAILURE;
+    CFDictionaryRef dict = NULL;
+    char            buffer[BUFFER_SIZE];
+    CFStringRef     product_name;
+    CFStringRef     product_version;
+    CFStringRef     product_build_version;
+    time_t          now;
+    CFTypeRef       shim_flag = NULL;
+    int             keybag_handle = bad_keybag_handle;
+    kern_return_t   kr = 0;
+    keybag_state_t  keybag_state = 0;
+    
+    /*
+     * Dump the system information.
+     *  ProductName
+     *  ProductVersion
+     *  ProductBuildVersion
+     *  Host name
+     */
+    
+    /* Dump a header. */
+    fprintf(log_file, "Host Information:\n");
+    fprintf(log_file, "=================\n");
+    
+    /* Copy the system version dictionary. */
+    dict = _CFCopySystemVersionDictionary();
+    if (dict == NULL) {
+    
+        fprintf(stderr, "Could not copy the system version dictionary\n");
+        goto BAIL;
+    }
+    
+    /* Extract the product name. */
+    product_name = CFDictionaryGetValue(dict, _kCFSystemVersionProductNameKey);
+    if (product_name == NULL) {
+    
+        fprintf(stderr, "Could not extract the product name from the system version dictionary\n");
+        goto BAIL;
+    }
+    
+    /* Convert the product name to a C string. */
+    if (!CFStringGetCString(product_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+    
+        fprintf(stderr, "Could not convert the product name to a C string\n");
+        goto BAIL;
+    }
+    
+    /* Dump the product name. */
+    fprintf(log_file, "Product Name: %s\n", buffer);
+    
+    /* Extract the product version. */
+    product_version = CFDictionaryGetValue(dict, _kCFSystemVersionProductVersionKey);
+    if (product_version == NULL) {
+    
+        fprintf(stderr, "Could not extract the product version from the system version dictionary\n");
+        goto BAIL;
+    }
+    
+    /* Convert the product version to a C string. */
+    if (!CFStringGetCString(product_version, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+    
+        fprintf(stderr, "Could not convert the product version to a C string\n");
+        goto BAIL;
+    }
+    
+    /* Dump the product version */
+    fprintf(log_file, "Product Version: %s\n", buffer);
+    
+    /* Extract the product build version. */
+    product_build_version = CFDictionaryGetValue(dict, _kCFSystemVersionBuildVersionKey);
+    if (product_build_version == NULL) {
+    
+        fprintf(stderr, "Could not extract the product build version from the system version dictionary\n");
+        goto BAIL;
+    }
+    
+    /* Convert the product build version to a C string. */
+    if (!CFStringGetCString(product_build_version, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+    
+        fprintf(stderr, "Could not convert the product build version to a C string\n");
+        goto BAIL;
+    }
+    
+    /* Dump the product build version. */
+    fprintf(log_file, "Product Build Version: %s\n", buffer);
+    
+    /* Lookup the host name. */
+    if (gethostname(buffer, BUFFER_SIZE) == -1) {
+    
+        fprintf(stderr, "Could not lookup the host name\n");
+        goto BAIL;
+    }
+    
+    /* Dump the host name. */
+    fprintf(log_file, "Host Name: %s\n", buffer);
+    
+    /* Lookup the current time. */
+    if (gethostname(buffer, BUFFER_SIZE) == -1) {
+    
+        fprintf(stderr, "Could not lookup the host name\n");
+        goto BAIL;
+    }
+
+    /* Get the current time. */
+    now = time(NULL);
+    
+    /* Dump the current time. */
+    fprintf(log_file, "Time: %s", ctime(&now));
+    
+    /* Is this a Mac? */
+    if (CFEqual(product_name, CFSTR("Mac OS X"))) {
+    
+        /* Set the keybag handle. */
+        keybag_handle = session_keybag_handle;
+        
+        /* Lookup the state of the shim. */
+        shim_flag = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
+        if (shim_flag && CFGetTypeID(shim_flag) == CFBooleanGetTypeID()) {
+        
+            /* Is the shim enabled? */
+            if (CFBooleanGetValue((CFBooleanRef)shim_flag)) {
+            
+                fprintf(log_file, "The SecItem shim is enabled\n");
+            } else {
+                
+                fprintf(log_file, "The SecItem shim is disabled\n");
+            }
+        } else {
+        
+            fprintf(log_file, "The SecItem shim is disabled\n");
+        }
+    } else {
+    
+        /* Set the keybag handle. */
+        keybag_handle = device_keybag_handle;
+    }
+    
+    /* Get the keybag state. */
+    kr = aks_get_lock_state(keybag_handle, &keybag_state);
+    if (kr) {
+    
+        fprintf(stderr, "Could not call aks_get_lock_state\n");
+    } else {
+    
+        switch (keybag_state) {
+        
+            case keybag_state_unlocked: {
+                
+                fprintf(log_file, "Keybag State: Unlocked\n");
+            }break;
+            
+            case keybag_state_locked: {
+                
+                fprintf(log_file, "Keybag State: Locked\n");
+            }break;
+            
+            case keybag_state_no_pin: {
+                
+                fprintf(log_file, "Keybag State: No Passcode\n");
+            }break;
+            
+            case keybag_state_been_unlocked: {
+                
+                fprintf(log_file, "Keybag State: Been Unlocked\n");
+            }break;
+            
+            case my_keybag_state_bio_unlock: {
+                
+                fprintf(log_file, "Keybag State: Bio Unlock\n");
+            }break;
+            
+            default: {
+            
+                fprintf(log_file, "Keybag State: UNKNOWN\n");
+            }break;
+        }
+    }
+    
+    /* Dump a footer. */
+    fprintf(log_file, "=================\n\n");
+
+    /* Set the result to success. */
+    result = SUCCESS;
+    
+BAIL:
+
+    /* Release the shim flag? */
+    if (shim_flag) {
+    
+        CFRelease(shim_flag);
+        shim_flag = NULL;
+    }
+    
+    /* Release the system version dictionary? */
+    if (dict != NULL) {
+    
+        CFRelease(dict);
+        dict = NULL;
+    }
+    
+    return result;
+}
+
+static
+int
+dump_circle_state(
+    FILE *log_file)
+{
+    int             result = FAILURE;
+    CFErrorRef      error = NULL;
+    SOSCCStatus     circle_status;
+    CFArrayRef      peer_list = NULL;
+    CFIndex         num_peers;
+    CFIndex         i;
+    SOSPeerInfoRef  peer_info;
+    CFDictionaryRef peer_gestalt = NULL;
+    CFStringRef     peer_name;
+    CFStringRef     peer_device_type;
+    CFStringRef     peerID;
+    char            buffer[BUFFER_SIZE] = {};
+
+    /*
+     * Dump the SOS circle state.
+     */
+    
+    /* Dump a header. */
+    fprintf(log_file, "SOS Circle State:\n");
+    fprintf(log_file, "=================\n");
+
+    /* Are we in a circle? */
+    circle_status = SOSCCThisDeviceIsInCircle(&error);
+    if (error != NULL) {
+    
+        /* Dump and consume the error. */
+        dump_cferror(log_file, "Could not call SOSCCThisDeviceIsInCircle", error);
+    } else {
+        char *circle_state_string = NULL;
+
+        switch (circle_status) {
+        
+            case kSOSCCInCircle: {
+                circle_state_string = "kSOSCCInCircle";
+            }break;
+            
+            case kSOSCCNotInCircle: {
+                circle_state_string = "kSOSCCNotInCircle";
+            }break;
+            
+            case kSOSCCRequestPending: {
+                circle_state_string = "kSOSCCRequestPending";
+            }break;
+            
+            case kSOSCCCircleAbsent: {
+                circle_state_string = "kSOSCCCircleAbsent";
+            }break;
+            
+            case kSOSCCError: {
+                circle_state_string = "kSOSCCError";
+            }break;
+        
+            default: {
+                sprintf(buffer, "Unknown circle status (%d)?", circle_status);
+                circle_state_string = buffer;
+            }
+        }
+        
+        fprintf(log_file, "Circle Status: %s\n", circle_state_string);
+    }
+    
+    /* Can we authenticate? */
+    if (!SOSCCCanAuthenticate(&error)) {
+
+        if (error) {
+        
+            /* Dump and consume the error. */
+            dump_cferror(log_file, "Could not call SOSCCCanAuthenticate", error);
+        } else {
+
+            fprintf(log_file, "Can Authenticate: NO\n");
+        }
+    } else {
+    
+        fprintf(log_file, "Can Authenticate: YES\n");
+    }
+    
+    /* Copy the peers. */
+    peer_list = SOSCCCopyPeerPeerInfo(&error);
+    if (!peer_list) {
+
+        /* Dump the error. */
+        dump_cferror(log_file, "Could not call SOSCCCopyPeerPeerInfo", error);
+    } else {
+    
+        /* Get the number of peers. */
+        num_peers = CFArrayGetCount(peer_list);
+        
+        fprintf(log_file, "Number of syncing peers: %ld\n", num_peers);
+        
+        if (num_peers > 0) {
+        
+            fprintf(log_file, "\n");
+        }
+        
+        /* Enumerate the peers. */
+        for (i = 0; i < num_peers; i++) {
+        
+            peer_info = (SOSPeerInfoRef) CFArrayGetValueAtIndex(peer_list, i);
+            if (peer_info == NULL) {
+            
+                fprintf(stderr, "Could not extract peer %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /*
+            peer_gestalt = SOSPeerInfoCopyPeerGestalt(peer_info);
+            if (peer_gestalt == NULL) {
+
+                fprintf(stderr, "Could not copy peer gestalt %ld of %ld\n", i, num_peers);
+                goto BAIL;            
+            }
+            */
+            
+            /* Get the peer name. */
+            peer_name = SOSPeerInfoGetPeerName(peer_info);
+            if (peer_name == NULL) {
+            
+                fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Convert the peer name to a C string. */
+            if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer name to a C string\n");
+                goto BAIL;
+            }
+    
+            /* Dump the peer name. */
+            fprintf(log_file, " Peer Name: %s\n", buffer);
+            
+            /* Get the peer device type. */
+            peer_device_type = SOSPeerInfoGetPeerDeviceType(peer_info);
+            if (peer_device_type == NULL) {
+            
+                fprintf(stderr, "Could not extract peer device type %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Convert the peer device type to a C string. */
+            if (!CFStringGetCString(peer_device_type, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer device type to a C string\n");
+                goto BAIL;
+            }
+            
+            /* Dump the peer name. */
+            fprintf(log_file, " Peer Device Type: %s\n", buffer);
+            
+            /* Get the peer ID. */            
+            peerID = SOSPeerInfoGetPeerID(peer_info);
+            if (peerID == NULL) {
+            
+                fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Dump the peer name. */
+            fprintf(log_file, " Peer ID: %s\n", buffer);
+            
+            /* Convert the peer ID to a C string. */
+            if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer ID to a C string\n");
+                goto BAIL;
+            }
+            
+            /* Make it pretty. */
+            fprintf(log_file, "\n");
+        }
+        
+        /* Release the peer list. */
+        CFRelease(peer_list);
+        peer_list = NULL;
+    }
+
+    /* Copy the applicant peers. */
+    peer_list = SOSCCCopyApplicantPeerInfo(&error);
+    if (!peer_list) {
+
+        /* Dump the error. */
+        dump_cferror(log_file, "Could not call SOSCCCopyApplicantPeerInfo", error);
+    } else {
+    
+        /* Get the number of peers. */
+        num_peers = CFArrayGetCount(peer_list);
+        
+        fprintf(log_file, "Number of applicant peers: %ld\n", num_peers);
+        
+        if (num_peers > 0) {
+        
+            fprintf(log_file, "\n");
+        }
+        
+        /* Enumerate the peers. */
+        for (i = 0; i < num_peers; i++) {
+        
+            peer_info = (SOSPeerInfoRef) CFArrayGetValueAtIndex(peer_list, i);
+            if (peer_info == NULL) {
+            
+                fprintf(stderr, "Could not extract peer %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /*
+            peer_gestalt = SOSPeerInfoCopyPeerGestalt(peer_info);
+            if (peer_gestalt == NULL) {
+
+                fprintf(stderr, "Could not copy peer gestalt %ld of %ld\n", i, num_peers);
+                goto BAIL;            
+            }
+            */
+            
+            /* Get the peer name. */
+            peer_name = SOSPeerInfoGetPeerName(peer_info);
+            if (peer_name == NULL) {
+            
+                fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Convert the peer name to a C string. */
+            if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer name to a C string\n");
+                goto BAIL;
+            }
+    
+            /* Dump the peer name. */
+            fprintf(log_file, " Applicant Name: %s\n", buffer);
+            
+            /* Get the peer device type. */
+            peer_device_type = SOSPeerInfoGetPeerDeviceType(peer_info);
+            if (peer_device_type == NULL) {
+            
+                fprintf(stderr, "Could not extract peer device type %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Convert the peer device type to a C string. */
+            if (!CFStringGetCString(peer_device_type, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer device type to a C string\n");
+                goto BAIL;
+            }
+            
+            /* Dump the peer name. */
+            fprintf(log_file, " Applicant Device Type: %s\n", buffer);
+            
+            /* Get the peer ID. */            
+            peerID = SOSPeerInfoGetPeerID(peer_info);
+            if (peerID == NULL) {
+            
+                fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
+                goto BAIL;
+            }
+            
+            /* Dump the peer name. */
+            fprintf(log_file, " Applicant ID: %s\n", buffer);
+            
+            /* Convert the peer ID to a C string. */
+            if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+            
+                fprintf(stderr, "Could not convert the peer ID to a C string\n");
+                goto BAIL;
+            }
+            
+            /* Make it pretty. */
+            if (i < num_peers - 1) {
+            
+                fprintf(log_file, "\n");
+            }
+        }
+        
+        /* Release the peer list. */
+        CFRelease(peer_list);
+        peer_list = NULL;
+    }
+    
+    /* Dump a footer. */
+    fprintf(log_file, "=================\n\n");
+
+    /* Set the result to success. */
+    result = SUCCESS;
+    
+BAIL:
+    
+    /* Release the peer gestalt? */
+    if (peer_gestalt != NULL) {
+    
+        CFRelease(peer_gestalt);
+        peer_gestalt = NULL;
+    }
+    
+    /* Release the peer list? */
+    if (peer_list != NULL) {
+    
+        CFRelease(peer_list);
+        peer_list = NULL;
+    }
+    
+    /* Release the error string? */
+    if (error != NULL) {
+    
+        CFRelease(error);
+        error = NULL;
+    }
+    
+    return result;
+}
+
+static
+int
+dump_keychain_sync_kvs(
+    FILE *log_file)
+{
+    int                     result = FAILURE;
+    dispatch_group_t        cloud_group;
+    dispatch_queue_t        cloud_queue;
+    dispatch_semaphore_t    waitSemaphore;
+    dispatch_time_t         finishTime;
+    __block CFDictionaryRef kvs_dict = NULL;
+
+    /*
+     * Dump the keychain syncing KVS.
+     */
+    
+    /* Dump a header. */
+    fprintf(log_file, "Keychain Syncing KVS:\n");
+    fprintf(log_file, "=================\n");
+    
+    /* Create the serial dispatch queue to talk to CloudKeychainProxy. */
+    cloud_queue = dispatch_queue_create("cloud_queue", DISPATCH_QUEUE_SERIAL);
+
+    /* Create a semaphore. */
+    waitSemaphore = dispatch_semaphore_create(0);
+    
+    /* Create the finish time. */
+    finishTime = dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC);
+    
+    /* Create the dispatch group. */
+    cloud_group = dispatch_group_create();
+
+    /* Enter the dispatch group. */
+    dispatch_group_enter(cloud_group);
+
+    /* Establish the CloudKeychainProxy reply hander. */
+    CloudKeychainReplyBlock replyBlock = ^(CFDictionaryRef returnedValues, CFErrorRef error)
+    {
+        /* Did we get back some values? */
+        if (returnedValues) {
+        
+            kvs_dict = (returnedValues);
+            CFRetain(kvs_dict);
+        }
+        
+        /* Leave the cloud group. */
+        dispatch_group_leave(cloud_group);
+        
+        /* Signal the other queue we're done. */
+        dispatch_semaphore_signal(waitSemaphore);
+    };
+
+    /* Ask CloudKeychainProxy for all of the raw KVS data. */
+    SOSCloudKeychainGetAllObjectsFromCloud(cloud_queue, replyBlock);
+
+    /* Wait for CloudKeychainProxy to respond, up to 30 seconds. */
+    dispatch_semaphore_wait(waitSemaphore, finishTime);
+    
+    /* Release the semaphore. */
+       dispatch_release(waitSemaphore);
+    
+    /* Did we get any raw KVS data from CloudKeychainProxy? */
+    if (kvs_dict) {
+
+        dump_dict(log_file, kvs_dict, 0);
+    }
+    
+    /* Dump a footer. */
+    fprintf(log_file, "=================\n\n");
+
+    /* Set the result to success. */
+    result = SUCCESS;
+        
+    /* Release the KVS dictionary? */
+    if (kvs_dict != NULL) {
+    
+        CFRelease(kvs_dict);
+        kvs_dict = NULL;
+    }
+    
+    return result;
+}
+
+static
+void
+dump_dict(
+    FILE *log_file,
+    CFDictionaryRef dict,
+    const unsigned int indent_level)
+{
+    struct dict_dump_state dump_state;
+    
+    /* Setup the context. */
+    dump_state.log_file = log_file;
+    dump_state.dict = dict;
+    dump_state.indent_level = indent_level;
+
+    /* Apply the dumper to each element in the dictionary. */
+    CFDictionaryApplyFunction(dict, dump_dict_applier, (void *)&dump_state);
+}
+
+static
+void
+dump_dict_applier(
+    const void *key,
+    const void *value,
+    void *context)
+{
+    CFTypeRef               key_object;
+    CFTypeRef               value_object;
+    struct dict_dump_state  *dump_state;
+    unsigned int            i;
+    char                    buffer[BUFFER_SIZE];
+    CFIndex                 length;
+    const UInt8*            bytes;
+    
+    /* Assign the CF types. */
+    key_object = (CFTypeRef) key;
+    value_object = (CFTypeRef) value;
+    
+    /* Get the context. */
+    dump_state = (struct dict_dump_state *)context;
+    
+    /* Indent appropriately. */
+    for (i = 0; i < dump_state->indent_level; i++) {
+    
+        fprintf(dump_state->log_file, " ");
+    }
+    
+    /* Determine the key type. */
+    if (CFGetTypeID(key_object) == CFStringGetTypeID()) {
+
+        /* Convert the key to a C string. */
+        if (!CFStringGetCString((CFStringRef) key_object, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+        
+            fprintf(stderr, "Could not convert the key to a C string\n");
+            fprintf(dump_state->log_file, "[Failed Key Type]: ");
+        } else {
+        
+            fprintf(dump_state->log_file, "%s: ", buffer);
+        }
+    }
+    
+    /* Determine the value type. */
+    if (CFGetTypeID(value_object) == CFStringGetTypeID()) {
+
+        /* Convert the value to a C string. */
+        if (!CFStringGetCString((CFStringRef) value_object, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
+        
+            fprintf(stderr, "Could not convert the value to a C string\n");
+            fprintf(dump_state->log_file, "[Failed Value Type]: ");
+        } else {
+        
+            fprintf(dump_state->log_file, "%s\n", buffer);
+        }
+    } else if (CFGetTypeID(value_object) == CFDataGetTypeID()) {
+        
+        length = CFDataGetLength((CFDataRef)value_object);
+        bytes = CFDataGetBytePtr((CFDataRef) value_object);
+        
+        fprintf(dump_state->log_file, "0x");        
+
+        for (i = 0; i < (unsigned int)length && i < MAX_DATA_RATE; i++) {
+        
+            fprintf(dump_state->log_file, "%02x", (unsigned char)bytes[i]);
+        }
+        
+        fprintf(dump_state->log_file, " (%ld bytes)\n", length);
+        
+        
+    } else if (CFGetTypeID(value_object) == CFDictionaryGetTypeID()) {
+        
+        /* Recurse */
+        fprintf(dump_state->log_file, "\n");
+        dump_dict(dump_state->log_file, (CFDictionaryRef) value_object, dump_state->indent_level + 1);
+    } else {
+    
+        fprintf(dump_state->log_file, "[Unknown Value Type]\n");
+    }
+}
+
+static
+int
+dump_asl_sender(
+    FILE *log_file,
+    const char *asl_sender)
+{
+    int             result = FAILURE;
+    aslmsg          log_query = NULL;
+    aslresponse     log_response = NULL;
+    aslmsg          log_message;
+    char            *message_string;
+    uint32_t        message_length;
+    
+    /*
+     * Dump the ASL logs for the given sender.
+     */
+    
+    /* Dump a header. */
+    fprintf(log_file, "ASL: %s\n", asl_sender);
+    fprintf(log_file, "=================\n");
+    
+    /* Create the ASL query. */
+    log_query = asl_new(ASL_TYPE_QUERY);
+    if (log_query == NULL) {
+    
+        fprintf(stderr, "Could not create ASL query\n");
+        goto BAIL;
+    }
+    
+    /* Setup the ASL query. */
+    asl_set_query(log_query, ASL_KEY_SENDER, asl_sender, ASL_QUERY_OP_EQUAL);
+    
+    /* Perform the ASL search. */
+    log_response = asl_search(NULL, log_query);
+    if (log_response == NULL) {
+    
+        fprintf(log_file, "Could not perform ASL search for %s\n", asl_sender);
+    } else {
+    
+        /* Enumerate the ASL messages in the response. */
+        while ((log_message = asl_next(log_response)) != NULL) {
+        
+            /* Format the message entry. */
+            message_string = asl_format_message((asl_msg_t *)log_message, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &message_length);
+            if (message_string == NULL) {
+            
+                fprintf(stderr, "Could not create ASL message string\n");
+                goto BAIL;
+            }
+            
+            fprintf(log_file, "%s", message_string);
+            
+            /* Release the message string. */
+            free(message_string);
+            message_string = NULL;        
+        }
+    }
+    
+    /* Dump a footer. */
+    fprintf(log_file, "=================\n\n");
+
+    /* Set the result to success. */
+    result = SUCCESS;
+    
+BAIL:
+    
+    /* Release the ASL response? */
+    if (log_response != NULL) {
+    
+        asl_free(log_response);
+        log_response = NULL;
+    }
+    
+    /* Release the ASL query? */
+    if (log_query != NULL) {
+    
+        asl_free(log_query);
+        log_query = NULL;
+    }
+    
+    return result;
+}
+
+static
+void
+dump_cferror(
+    FILE *log_file,
+    const char *description,
+    CFErrorRef error)
+{
+    CFStringRef     error_string = NULL;
+    char            buffer[BUFFER_SIZE];
+    
+    error_string = CFErrorCopyDescription(error);
+    if (error_string == NULL) {
+    
+        fprintf(stderr, "Could not copy error description?\n");
+        goto BAIL;
+    }
+    
+    (void) CFStringGetCString(error_string, buffer, BUFFER_SIZE, kCFStringEncodingUTF8);
+    
+    fprintf(stderr, "%s: %s\n", description, buffer);
+    fprintf(log_file, "%s: %s\n", description, buffer);
+
+BAIL:
+
+    /* Release the error string? */
+    if (error_string != NULL) {
+    
+        CFRelease(error_string);
+        error_string = NULL;
+    }
+}
+
+#else  // TARGET_IPHONE_SIMULATOR
+
+int
+main(
+     int argc,
+     char **argv)
+{
+#pragma unused (argc, argv)
+    return 0;
+}
+
+#endif