1/*
2 * clang cloud_keychain_diagnose.c -laks -framework CoreFoundation -framework IOKit -framework Security -o /tmp/cloud_keychain_diagnose
3 */
4
5#include <CoreFoundation/CoreFoundation.h>
6#include <CoreFoundation/CFPriv.h>
7
8#if !TARGET_IPHONE_SIMULATOR
9
10/* Header Declarations */
11#include <stdlib.h>
12#include <stdio.h>
13#include <unistd.h>
14#include <asl.h>
15#include <asl_msg.h>
16
17#if TARGET_OS_EMBEDDED
18#include <asl_core.h>
19#endif
20
21#include <string.h>
22#include <errno.h>
23#include <libaks.h>
24
25#include "SOSCloudCircle.h"
26#include "SOSPeerInfo.h"
27
28
29/* Constant Declarations */
30#define SUCCESS 0
31#define FAILURE -1
32
33#define MAX_PATH_LEN                1024
34#define SUFFIX_LENGTH               4
35#define BUFFER_SIZE                 1024
36#define MAX_DATA_RATE               32
37
38/* External CloudKeychain Bridge Types */
39typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
40extern void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock);
41
42/* External AppleKeyStore Types */
43enum {
44 my_keybag_state_bio_unlock = 1 << 3
45};
46
47
48/* Dictionary Dump State */
49struct dict_dump_state
50{
51    FILE                *log_file;
52    CFDictionaryRef     dict;
53    unsigned int        indent_level;
54};
55
56/* Static Function Declarations */
57static
58void
59usage();
60
61static
62int
63gather_diagnostics();
64
65static
66int
67enable_cloud_keychain_diagnostics(
68    const unsigned int enable_flag);
69
70static
71int
72build_log_path(
73    char *log_path);
74
75static
76int
77dump_system_information(
78    FILE *log_file);
79
80static
81int
82dump_circle_state(
83    FILE *log_file);
84
85static
86int
87dump_keychain_sync_kvs(
88    FILE *log_file);
89
90static
91void
92dump_dict(
93    FILE *log_file,
94    CFDictionaryRef dict,
95    const unsigned int indent_level);
96
97static
98void
99dump_dict_applier(
100    const void *key,
101    const void *value,
102    void *context);
103
104static
105int
106dump_asl_sender(
107    FILE *log_file,
108    const char *asl_sender);
109
110static
111void
112dump_cferror(
113    FILE *log_file,
114    const char *description,
115    CFErrorRef error);
116
117/* Function Definitions */
118int
119main(
120    int argc,
121    char **argv)
122{
123    int result = EXIT_FAILURE;
124
125    /* Parse the arguments. */
126    if (argc > 2) {
127
128        usage();
129    }
130
131    /* Should we just gather logs and status? */
132    if (argc == 1) {
133
134        if (gather_diagnostics()) {
135
136            fprintf(stderr, "Could not gather diagnostics\n");
137            goto BAIL;
138        }
139    } else {
140
141        /* Should we enable or disable logging? */
142        if (strncmp(argv[1], "enable", 6) == 0) {
143
144            /* Enable. */
145            if (enable_cloud_keychain_diagnostics(1)) {
146
147                fprintf(stderr, "Could not enable additional cloud keychain diagnostics\n");
148                goto BAIL;
149            }
150        } else if (strncmp(argv[1], "disable", 7) == 0) {
151
152            /* Enable. */
153            if (enable_cloud_keychain_diagnostics(1)) {
154
155                fprintf(stderr, "Could not disable additional cloud keychain diagnostics\n");
156                goto BAIL;
157            }
158        } else {
159
160            /* Get a job, hippy. */
161            usage();
162        }
163    }
164
165    /* Set the exit status to success. */
166    result = EXIT_FAILURE;
167
168BAIL:
169
170    return result;
171}
172
173/* Static Function Definitions */
174static
175void
176usage()
177{
178    fprintf(stderr, "usage: cloud_keychain_diagnose [enable|disable]\n");
179    exit(EXIT_FAILURE);
180}
181
182static
183int
184gather_diagnostics()
185{
186    int     result = FAILURE;
187    char    log_path[MAX_PATH_LEN] = "";
188    int     log_fd = -1;
189    FILE    *log_file = NULL;
190
191    /*
192     * Create the diagnostics file.
193     *
194     * Dump the system information.
195     *     on OS X, defaults read if the shim is active
196     * Dump the circle state.
197     * Dump the raw KVS data.
198     * Dump known ASL logs
199     *
200     * Remaining work to do from rdar://12479351
201     * grab the syslog
202     * query for all items with sync=1
203     * enable KVS logging
204     * enable push notification logging
205     */
206
207    /* Build the log path. */
208    if (build_log_path(log_path)) {
209
210        fprintf(stderr, "Could not build the log path\n");
211        goto BAIL;
212    }
213
214    /* Create it with a randomized suffix. */
215    log_fd = mkstemps(log_path, SUFFIX_LENGTH);
216    if (log_fd == -1) {
217
218        fprintf(stderr, "Could not create the log file: %s\n", strerror(errno));
219        goto BAIL;
220    }
221
222    /* Create a file object from the descriptor. */
223    log_file = fdopen(log_fd, "w");
224    if (log_file == NULL) {
225
226        fprintf(stderr, "Could not recreate the log file: %s\n", strerror(errno));
227        goto BAIL;
228    }
229
230    log_fd = -1;
231
232    printf("Writing cloud keychain diagnostics to %s\n", log_path);
233
234    /* Dump the system information. */
235    if (dump_system_information(log_file)) {
236
237        fprintf(stderr, "Could not dump the system information\n");
238        goto BAIL;
239    }
240
241    /* Dump the SOS circle state. */
242    if (dump_circle_state(log_file)) {
243
244        fprintf(stderr, "Could not dump the SOS circle state\n");
245        goto BAIL;
246    }
247
248    /* Dump the raw keychain syncing KVS. */
249    if (dump_keychain_sync_kvs(log_file)) {
250
251        fprintf(stderr, "Could not the raw keychain syncing KVS\n");
252        goto BAIL;
253    }
254
255    /*
256     * Dump the various and sundry ASL logs.
257     */
258
259    if (dump_asl_sender(log_file, "com.apple.kb-service")) {
260
261        fprintf(stderr, "Could not dump the ASL log for com.apple.kb-service\n");
262        goto BAIL;
263    }
264
265    if (dump_asl_sender(log_file, "com.apple.securityd")) {
266
267        fprintf(stderr, "Could not dump the ASL log for com.apple.securityd\n");
268        goto BAIL;
269    }
270
271    if (dump_asl_sender(log_file, "com.apple.secd")) {
272
273        fprintf(stderr, "Could not dump the ASL log for com.apple.secd\n");
274        goto BAIL;
275    }
276
277    if (dump_asl_sender(log_file, "CloudKeychainProxy")) {
278
279        fprintf(stderr, "Could not dump the ASL log for CloudKeychainProxy\n");
280        goto BAIL;
281    }
282
283    if (dump_asl_sender(log_file, "securityd")) {
284
285        fprintf(stderr, "Could not dump the ASL log for securityd\n");
286        goto BAIL;
287    }
288
289    if (dump_asl_sender(log_file, "secd")) {
290
291        fprintf(stderr, "Could not dump the ASL log for secd\n");
292        goto BAIL;
293    }
294
295    /* Set the result to success. */
296    result = SUCCESS;
297
298BAIL:
299
300    /* Close the diagnostics file? */
301    if (log_file != NULL) {
302
303        fclose(log_file);
304        log_file = NULL;
305    }
306
307    /* Close the diagnostics file descriptor? */
308    if (log_fd != -1) {
309
310        close(log_fd);
311        log_fd = -1;
312        (void) log_fd;
313    }
314
315    return result;
316}
317
318static
319int
320enable_cloud_keychain_diagnostics(
321    const unsigned int enable_flag)
322{
323    int result = FAILURE;
324
325    /* Set the result to success. */
326    result = SUCCESS;
327
328    return result;
329}
330
331static
332int
333build_log_path(
334    char *log_path)
335{
336    int             result = FAILURE;
337    time_t          now;
338    struct tm       *time_cube;
339    CFDictionaryRef system_version_dict = NULL;
340    CFStringRef     product_name = NULL;
341
342    /* Get the current time. */
343    now = time(NULL);
344
345    /* Convert the time into something usable. */
346    time_cube = localtime(&now);
347    if (time_cube == NULL) {
348
349        fprintf(stderr, "I don't know what time it is.\n");
350        goto BAIL;
351    }
352
353    /* Copy the system version dictionary. */
354    system_version_dict = _CFCopySystemVersionDictionary();
355    if (system_version_dict == NULL) {
356
357        fprintf(stderr, "Could not copy the system version dictionary\n");
358        goto BAIL;
359    }
360
361    /* Extract the product name. */
362    product_name = CFDictionaryGetValue(system_version_dict, _kCFSystemVersionProductNameKey);
363    if (product_name == NULL) {
364
365        fprintf(stderr, "Could not extract the product name from the system version dictionary\n");
366        goto BAIL;
367    }
368
369    /* Is this a Mac? */
370    if (CFEqual(product_name, CFSTR("Mac OS X"))) {
371
372        /* Prepare the file template to go into /tmp. */
373        snprintf(
374            log_path,
375            MAX_PATH_LEN,
376            "/tmp/cloud_keychain_diagnostics.%d_%d_%d.%d%d%d.XXXX.txt",
377            1900 + time_cube->tm_year,
378            time_cube->tm_mon,
379            time_cube->tm_mday,
380            time_cube->tm_hour,
381            time_cube->tm_min,
382            time_cube->tm_sec);
383    } else {
384
385        /* Prepare the file template to go into CrashReporter. */
386        snprintf(
387            log_path,
388            MAX_PATH_LEN,
389            "/Library/Logs/CrashReporter/cloud_keychain_diagnostics.%d_%d_%d.%d%d%d.XXXX.txt",
390            1900 + time_cube->tm_year,
391            time_cube->tm_mon,
392            time_cube->tm_mday,
393            time_cube->tm_hour,
394            time_cube->tm_min,
395            time_cube->tm_sec);
396    }
397
398    /* Set the result to success. */
399    result = SUCCESS;
400
401BAIL:
402
403    /* Release the system version dictionary? */
404    if (system_version_dict != NULL) {
405
406        CFRelease(system_version_dict);
407        system_version_dict = NULL;
408    }
409
410    return result;
411}
412
413static
414int
415dump_system_information(
416    FILE *log_file)
417{
418    int             result = FAILURE;
419    CFDictionaryRef dict = NULL;
420    char            buffer[BUFFER_SIZE];
421    CFStringRef     product_name;
422    CFStringRef     product_version;
423    CFStringRef     product_build_version;
424    time_t          now;
425    CFTypeRef       shim_flag = NULL;
426    int             keybag_handle = bad_keybag_handle;
427    kern_return_t   kr = 0;
428    keybag_state_t  keybag_state = 0;
429
430    /*
431     * Dump the system information.
432     *  ProductName
433     *  ProductVersion
434     *  ProductBuildVersion
435     *  Host name
436     */
437
438    /* Dump a header. */
439    fprintf(log_file, "Host Information:\n");
440    fprintf(log_file, "=================\n");
441
442    /* Copy the system version dictionary. */
443    dict = _CFCopySystemVersionDictionary();
444    if (dict == NULL) {
445
446        fprintf(stderr, "Could not copy the system version dictionary\n");
447        goto BAIL;
448    }
449
450    /* Extract the product name. */
451    product_name = CFDictionaryGetValue(dict, _kCFSystemVersionProductNameKey);
452    if (product_name == NULL) {
453
454        fprintf(stderr, "Could not extract the product name from the system version dictionary\n");
455        goto BAIL;
456    }
457
458    /* Convert the product name to a C string. */
459    if (!CFStringGetCString(product_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
460
461        fprintf(stderr, "Could not convert the product name to a C string\n");
462        goto BAIL;
463    }
464
465    /* Dump the product name. */
466    fprintf(log_file, "Product Name: %s\n", buffer);
467
468    /* Extract the product version. */
469    product_version = CFDictionaryGetValue(dict, _kCFSystemVersionProductVersionKey);
470    if (product_version == NULL) {
471
472        fprintf(stderr, "Could not extract the product version from the system version dictionary\n");
473        goto BAIL;
474    }
475
476    /* Convert the product version to a C string. */
477    if (!CFStringGetCString(product_version, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
478
479        fprintf(stderr, "Could not convert the product version to a C string\n");
480        goto BAIL;
481    }
482
483    /* Dump the product version */
484    fprintf(log_file, "Product Version: %s\n", buffer);
485
486    /* Extract the product build version. */
487    product_build_version = CFDictionaryGetValue(dict, _kCFSystemVersionBuildVersionKey);
488    if (product_build_version == NULL) {
489
490        fprintf(stderr, "Could not extract the product build version from the system version dictionary\n");
491        goto BAIL;
492    }
493
494    /* Convert the product build version to a C string. */
495    if (!CFStringGetCString(product_build_version, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
496
497        fprintf(stderr, "Could not convert the product build version to a C string\n");
498        goto BAIL;
499    }
500
501    /* Dump the product build version. */
502    fprintf(log_file, "Product Build Version: %s\n", buffer);
503
504    /* Lookup the host name. */
505    if (gethostname(buffer, BUFFER_SIZE) == -1) {
506
507        fprintf(stderr, "Could not lookup the host name\n");
508        goto BAIL;
509    }
510
511    /* Dump the host name. */
512    fprintf(log_file, "Host Name: %s\n", buffer);
513
514    /* Lookup the current time. */
515    if (gethostname(buffer, BUFFER_SIZE) == -1) {
516
517        fprintf(stderr, "Could not lookup the host name\n");
518        goto BAIL;
519    }
520
521    /* Get the current time. */
522    now = time(NULL);
523
524    /* Dump the current time. */
525    fprintf(log_file, "Time: %s", ctime(&now));
526
527    /* Is this a Mac? */
528    if (CFEqual(product_name, CFSTR("Mac OS X"))) {
529
530        /* Set the keybag handle. */
531        keybag_handle = session_keybag_handle;
532
533        /* Lookup the state of the shim. */
534        shim_flag = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
535        if (shim_flag && CFGetTypeID(shim_flag) == CFBooleanGetTypeID()) {
536
537            /* Is the shim enabled? */
538            if (CFBooleanGetValue((CFBooleanRef)shim_flag)) {
539
540                fprintf(log_file, "The SecItem shim is enabled\n");
541            } else {
542
543                fprintf(log_file, "The SecItem shim is disabled\n");
544            }
545        } else {
546
547            fprintf(log_file, "The SecItem shim is disabled\n");
548        }
549    } else {
550
551        /* Set the keybag handle. */
552        keybag_handle = device_keybag_handle;
553    }
554
555    /* Get the keybag state. */
556    kr = aks_get_lock_state(keybag_handle, &keybag_state);
557    if (kr) {
558
559        fprintf(stderr, "Could not call aks_get_lock_state\n");
560    } else {
561
562        switch (keybag_state) {
563
564            case keybag_state_unlocked: {
565
566                fprintf(log_file, "Keybag State: Unlocked\n");
567            }break;
568
569            case keybag_state_locked: {
570
571                fprintf(log_file, "Keybag State: Locked\n");
572            }break;
573
574            case keybag_state_no_pin: {
575
576                fprintf(log_file, "Keybag State: No Passcode\n");
577            }break;
578
579            case keybag_state_been_unlocked: {
580
581                fprintf(log_file, "Keybag State: Been Unlocked\n");
582            }break;
583
584            case my_keybag_state_bio_unlock: {
585
586                fprintf(log_file, "Keybag State: Bio Unlock\n");
587            }break;
588
589            default: {
590
591                fprintf(log_file, "Keybag State: UNKNOWN\n");
592            }break;
593        }
594    }
595
596    /* Dump a footer. */
597    fprintf(log_file, "=================\n\n");
598
599    /* Set the result to success. */
600    result = SUCCESS;
601
602BAIL:
603
604    /* Release the shim flag? */
605    if (shim_flag) {
606
607        CFRelease(shim_flag);
608        shim_flag = NULL;
609    }
610
611    /* Release the system version dictionary? */
612    if (dict != NULL) {
613
614        CFRelease(dict);
615        dict = NULL;
616    }
617
618    return result;
619}
620
621static
622int
623dump_circle_state(
624    FILE *log_file)
625{
626    int             result = FAILURE;
627    CFErrorRef      error = NULL;
628    SOSCCStatus     circle_status;
629    CFArrayRef      peer_list = NULL;
630    CFIndex         num_peers;
631    CFIndex         i;
632    SOSPeerInfoRef  peer_info;
633    CFDictionaryRef peer_gestalt = NULL;
634    CFStringRef     peer_name;
635    CFStringRef     peer_device_type;
636    CFStringRef     peerID;
637    char            buffer[BUFFER_SIZE] = {};
638
639    /*
640     * Dump the SOS circle state.
641     */
642
643    /* Dump a header. */
644    fprintf(log_file, "SOS Circle State:\n");
645    fprintf(log_file, "=================\n");
646
647    /* Are we in a circle? */
648    circle_status = SOSCCThisDeviceIsInCircle(&error);
649    if (error != NULL) {
650
651        /* Dump and consume the error. */
652        dump_cferror(log_file, "Could not call SOSCCThisDeviceIsInCircle", error);
653    } else {
654        char *circle_state_string = NULL;
655
656        switch (circle_status) {
657
658            case kSOSCCInCircle: {
659                circle_state_string = "kSOSCCInCircle";
660            }break;
661
662            case kSOSCCNotInCircle: {
663                circle_state_string = "kSOSCCNotInCircle";
664            }break;
665
666            case kSOSCCRequestPending: {
667                circle_state_string = "kSOSCCRequestPending";
668            }break;
669
670            case kSOSCCCircleAbsent: {
671                circle_state_string = "kSOSCCCircleAbsent";
672            }break;
673
674            case kSOSCCError: {
675                circle_state_string = "kSOSCCError";
676            }break;
677
678            default: {
679                sprintf(buffer, "Unknown circle status (%d)?", circle_status);
680                circle_state_string = buffer;
681            }
682        }
683
684        fprintf(log_file, "Circle Status: %s\n", circle_state_string);
685    }
686
687    /* Can we authenticate? */
688    if (!SOSCCCanAuthenticate(&error)) {
689
690        if (error) {
691
692            /* Dump and consume the error. */
693            dump_cferror(log_file, "Could not call SOSCCCanAuthenticate", error);
694        } else {
695
696            fprintf(log_file, "Can Authenticate: NO\n");
697        }
698    } else {
699
700        fprintf(log_file, "Can Authenticate: YES\n");
701    }
702
703    /* Copy the peers. */
704    peer_list = SOSCCCopyPeerPeerInfo(&error);
705    if (!peer_list) {
706
707        /* Dump the error. */
708        dump_cferror(log_file, "Could not call SOSCCCopyPeerPeerInfo", error);
709    } else {
710
711        /* Get the number of peers. */
712        num_peers = CFArrayGetCount(peer_list);
713
714        fprintf(log_file, "Number of syncing peers: %ld\n", num_peers);
715
716        if (num_peers > 0) {
717
718            fprintf(log_file, "\n");
719        }
720
721        /* Enumerate the peers. */
722        for (i = 0; i < num_peers; i++) {
723
724            peer_info = (SOSPeerInfoRef) CFArrayGetValueAtIndex(peer_list, i);
725            if (peer_info == NULL) {
726
727                fprintf(stderr, "Could not extract peer %ld of %ld\n", i, num_peers);
728                goto BAIL;
729            }
730
731            /*
732            peer_gestalt = SOSPeerInfoCopyPeerGestalt(peer_info);
733            if (peer_gestalt == NULL) {
734
735                fprintf(stderr, "Could not copy peer gestalt %ld of %ld\n", i, num_peers);
736                goto BAIL;
737            }
738            */
739
740            /* Get the peer name. */
741            peer_name = SOSPeerInfoGetPeerName(peer_info);
742            if (peer_name == NULL) {
743
744                fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
745                goto BAIL;
746            }
747
748            /* Convert the peer name to a C string. */
749            if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
750
751                fprintf(stderr, "Could not convert the peer name to a C string\n");
752                goto BAIL;
753            }
754
755            /* Dump the peer name. */
756            fprintf(log_file, " Peer Name: %s\n", buffer);
757
758            /* Get the peer device type. */
759            peer_device_type = SOSPeerInfoGetPeerDeviceType(peer_info);
760            if (peer_device_type == NULL) {
761
762                fprintf(stderr, "Could not extract peer device type %ld of %ld\n", i, num_peers);
763                goto BAIL;
764            }
765
766            /* Convert the peer device type to a C string. */
767            if (!CFStringGetCString(peer_device_type, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
768
769                fprintf(stderr, "Could not convert the peer device type to a C string\n");
770                goto BAIL;
771            }
772
773            /* Dump the peer name. */
774            fprintf(log_file, " Peer Device Type: %s\n", buffer);
775
776            /* Get the peer ID. */
777            peerID = SOSPeerInfoGetPeerID(peer_info);
778            if (peerID == NULL) {
779
780                fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
781                goto BAIL;
782            }
783
784            /* Dump the peer name. */
785            fprintf(log_file, " Peer ID: %s\n", buffer);
786
787            /* Convert the peer ID to a C string. */
788            if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
789
790                fprintf(stderr, "Could not convert the peer ID to a C string\n");
791                goto BAIL;
792            }
793
794            /* Make it pretty. */
795            fprintf(log_file, "\n");
796        }
797
798        /* Release the peer list. */
799        CFRelease(peer_list);
800        peer_list = NULL;
801    }
802
803    /* Copy the applicant peers. */
804    peer_list = SOSCCCopyApplicantPeerInfo(&error);
805    if (!peer_list) {
806
807        /* Dump the error. */
808        dump_cferror(log_file, "Could not call SOSCCCopyApplicantPeerInfo", error);
809    } else {
810
811        /* Get the number of peers. */
812        num_peers = CFArrayGetCount(peer_list);
813
814        fprintf(log_file, "Number of applicant peers: %ld\n", num_peers);
815
816        if (num_peers > 0) {
817
818            fprintf(log_file, "\n");
819        }
820
821        /* Enumerate the peers. */
822        for (i = 0; i < num_peers; i++) {
823
824            peer_info = (SOSPeerInfoRef) CFArrayGetValueAtIndex(peer_list, i);
825            if (peer_info == NULL) {
826
827                fprintf(stderr, "Could not extract peer %ld of %ld\n", i, num_peers);
828                goto BAIL;
829            }
830
831            /*
832            peer_gestalt = SOSPeerInfoCopyPeerGestalt(peer_info);
833            if (peer_gestalt == NULL) {
834
835                fprintf(stderr, "Could not copy peer gestalt %ld of %ld\n", i, num_peers);
836                goto BAIL;
837            }
838            */
839
840            /* Get the peer name. */
841            peer_name = SOSPeerInfoGetPeerName(peer_info);
842            if (peer_name == NULL) {
843
844                fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
845                goto BAIL;
846            }
847
848            /* Convert the peer name to a C string. */
849            if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
850
851                fprintf(stderr, "Could not convert the peer name to a C string\n");
852                goto BAIL;
853            }
854
855            /* Dump the peer name. */
856            fprintf(log_file, " Applicant Name: %s\n", buffer);
857
858            /* Get the peer device type. */
859            peer_device_type = SOSPeerInfoGetPeerDeviceType(peer_info);
860            if (peer_device_type == NULL) {
861
862                fprintf(stderr, "Could not extract peer device type %ld of %ld\n", i, num_peers);
863                goto BAIL;
864            }
865
866            /* Convert the peer device type to a C string. */
867            if (!CFStringGetCString(peer_device_type, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
868
869                fprintf(stderr, "Could not convert the peer device type to a C string\n");
870                goto BAIL;
871            }
872
873            /* Dump the peer name. */
874            fprintf(log_file, " Applicant Device Type: %s\n", buffer);
875
876            /* Get the peer ID. */
877            peerID = SOSPeerInfoGetPeerID(peer_info);
878            if (peerID == NULL) {
879
880                fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
881                goto BAIL;
882            }
883
884            /* Dump the peer name. */
885            fprintf(log_file, " Applicant ID: %s\n", buffer);
886
887            /* Convert the peer ID to a C string. */
888            if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
889
890                fprintf(stderr, "Could not convert the peer ID to a C string\n");
891                goto BAIL;
892            }
893
894            /* Make it pretty. */
895            if (i < num_peers - 1) {
896
897                fprintf(log_file, "\n");
898            }
899        }
900
901        /* Release the peer list. */
902        CFRelease(peer_list);
903        peer_list = NULL;
904    }
905
906    /* Dump a footer. */
907    fprintf(log_file, "=================\n\n");
908
909    /* Set the result to success. */
910    result = SUCCESS;
911
912BAIL:
913
914    /* Release the peer gestalt? */
915    if (peer_gestalt != NULL) {
916
917        CFRelease(peer_gestalt);
918        peer_gestalt = NULL;
919    }
920
921    /* Release the peer list? */
922    if (peer_list != NULL) {
923
924        CFRelease(peer_list);
925        peer_list = NULL;
926    }
927
928    /* Release the error string? */
929    if (error != NULL) {
930
931        CFRelease(error);
932        error = NULL;
933    }
934
935    return result;
936}
937
938static
939int
940dump_keychain_sync_kvs(
941    FILE *log_file)
942{
943    int                     result = FAILURE;
944    dispatch_group_t        cloud_group;
945    dispatch_queue_t        cloud_queue;
946    dispatch_semaphore_t    waitSemaphore;
947    dispatch_time_t         finishTime;
948    __block CFDictionaryRef kvs_dict = NULL;
949
950    /*
951     * Dump the keychain syncing KVS.
952     */
953
954    /* Dump a header. */
955    fprintf(log_file, "Keychain Syncing KVS:\n");
956    fprintf(log_file, "=================\n");
957
958    /* Create the serial dispatch queue to talk to CloudKeychainProxy. */
959    cloud_queue = dispatch_queue_create("cloud_queue", DISPATCH_QUEUE_SERIAL);
960
961    /* Create a semaphore. */
962    waitSemaphore = dispatch_semaphore_create(0);
963
964    /* Create the finish time. */
965    finishTime = dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC);
966
967    /* Create the dispatch group. */
968    cloud_group = dispatch_group_create();
969
970    /* Enter the dispatch group. */
971    dispatch_group_enter(cloud_group);
972
973    /* Establish the CloudKeychainProxy reply hander. */
974    CloudKeychainReplyBlock replyBlock = ^(CFDictionaryRef returnedValues, CFErrorRef error)
975    {
976        /* Did we get back some values? */
977        if (returnedValues) {
978
979            kvs_dict = (returnedValues);
980            CFRetain(kvs_dict);
981        }
982
983        /* Leave the cloud group. */
984        dispatch_group_leave(cloud_group);
985
986        /* Signal the other queue we're done. */
987        dispatch_semaphore_signal(waitSemaphore);
988    };
989
990    /* Ask CloudKeychainProxy for all of the raw KVS data. */
991    SOSCloudKeychainGetAllObjectsFromCloud(cloud_queue, replyBlock);
992
993    /* Wait for CloudKeychainProxy to respond, up to 30 seconds. */
994    dispatch_semaphore_wait(waitSemaphore, finishTime);
995
996    /* Release the semaphore. */
997	dispatch_release(waitSemaphore);
998
999    /* Did we get any raw KVS data from CloudKeychainProxy? */
1000    if (kvs_dict) {
1001
1002        dump_dict(log_file, kvs_dict, 0);
1003    }
1004
1005    /* Dump a footer. */
1006    fprintf(log_file, "=================\n\n");
1007
1008    /* Set the result to success. */
1009    result = SUCCESS;
1010
1011    /* Release the KVS dictionary? */
1012    if (kvs_dict != NULL) {
1013
1014        CFRelease(kvs_dict);
1015        kvs_dict = NULL;
1016    }
1017
1018    return result;
1019}
1020
1021static
1022void
1023dump_dict(
1024    FILE *log_file,
1025    CFDictionaryRef dict,
1026    const unsigned int indent_level)
1027{
1028    struct dict_dump_state dump_state;
1029
1030    /* Setup the context. */
1031    dump_state.log_file = log_file;
1032    dump_state.dict = dict;
1033    dump_state.indent_level = indent_level;
1034
1035    /* Apply the dumper to each element in the dictionary. */
1036    CFDictionaryApplyFunction(dict, dump_dict_applier, (void *)&dump_state);
1037}
1038
1039static
1040void
1041dump_dict_applier(
1042    const void *key,
1043    const void *value,
1044    void *context)
1045{
1046    CFTypeRef               key_object;
1047    CFTypeRef               value_object;
1048    struct dict_dump_state  *dump_state;
1049    unsigned int            i;
1050    char                    buffer[BUFFER_SIZE];
1051    CFIndex                 length;
1052    const UInt8*            bytes;
1053
1054    /* Assign the CF types. */
1055    key_object = (CFTypeRef) key;
1056    value_object = (CFTypeRef) value;
1057
1058    /* Get the context. */
1059    dump_state = (struct dict_dump_state *)context;
1060
1061    /* Indent appropriately. */
1062    for (i = 0; i < dump_state->indent_level; i++) {
1063
1064        fprintf(dump_state->log_file, " ");
1065    }
1066
1067    /* Determine the key type. */
1068    if (CFGetTypeID(key_object) == CFStringGetTypeID()) {
1069
1070        /* Convert the key to a C string. */
1071        if (!CFStringGetCString((CFStringRef) key_object, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
1072
1073            fprintf(stderr, "Could not convert the key to a C string\n");
1074            fprintf(dump_state->log_file, "[Failed Key Type]: ");
1075        } else {
1076
1077            fprintf(dump_state->log_file, "%s: ", buffer);
1078        }
1079    }
1080
1081    /* Determine the value type. */
1082    if (CFGetTypeID(value_object) == CFStringGetTypeID()) {
1083
1084        /* Convert the value to a C string. */
1085        if (!CFStringGetCString((CFStringRef) value_object, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
1086
1087            fprintf(stderr, "Could not convert the value to a C string\n");
1088            fprintf(dump_state->log_file, "[Failed Value Type]: ");
1089        } else {
1090
1091            fprintf(dump_state->log_file, "%s\n", buffer);
1092        }
1093    } else if (CFGetTypeID(value_object) == CFDataGetTypeID()) {
1094
1095        length = CFDataGetLength((CFDataRef)value_object);
1096        bytes = CFDataGetBytePtr((CFDataRef) value_object);
1097
1098        fprintf(dump_state->log_file, "0x");
1099
1100        for (i = 0; i < (unsigned int)length && i < MAX_DATA_RATE; i++) {
1101
1102            fprintf(dump_state->log_file, "%02x", (unsigned char)bytes[i]);
1103        }
1104
1105        fprintf(dump_state->log_file, " (%ld bytes)\n", length);
1106
1107
1108    } else if (CFGetTypeID(value_object) == CFDictionaryGetTypeID()) {
1109
1110        /* Recurse */
1111        fprintf(dump_state->log_file, "\n");
1112        dump_dict(dump_state->log_file, (CFDictionaryRef) value_object, dump_state->indent_level + 1);
1113    } else {
1114
1115        fprintf(dump_state->log_file, "[Unknown Value Type]\n");
1116    }
1117}
1118
1119static
1120int
1121dump_asl_sender(
1122    FILE *log_file,
1123    const char *asl_sender)
1124{
1125    int             result = FAILURE;
1126    aslmsg          log_query = NULL;
1127    aslresponse     log_response = NULL;
1128    aslmsg          log_message;
1129    char            *message_string;
1130    uint32_t        message_length;
1131
1132    /*
1133     * Dump the ASL logs for the given sender.
1134     */
1135
1136    /* Dump a header. */
1137    fprintf(log_file, "ASL: %s\n", asl_sender);
1138    fprintf(log_file, "=================\n");
1139
1140    /* Create the ASL query. */
1141    log_query = asl_new(ASL_TYPE_QUERY);
1142    if (log_query == NULL) {
1143
1144        fprintf(stderr, "Could not create ASL query\n");
1145        goto BAIL;
1146    }
1147
1148    /* Setup the ASL query. */
1149    asl_set_query(log_query, ASL_KEY_SENDER, asl_sender, ASL_QUERY_OP_EQUAL);
1150
1151    /* Perform the ASL search. */
1152    log_response = asl_search(NULL, log_query);
1153    if (log_response == NULL) {
1154
1155        fprintf(log_file, "Could not perform ASL search for %s\n", asl_sender);
1156    } else {
1157
1158        /* Enumerate the ASL messages in the response. */
1159        while ((log_message = asl_next(log_response)) != NULL) {
1160
1161            /* Format the message entry. */
1162            message_string = asl_format_message((asl_msg_t *)log_message, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &message_length);
1163            if (message_string == NULL) {
1164
1165                fprintf(stderr, "Could not create ASL message string\n");
1166                goto BAIL;
1167            }
1168
1169            fprintf(log_file, "%s", message_string);
1170
1171            /* Release the message string. */
1172            free(message_string);
1173            message_string = NULL;
1174        }
1175    }
1176
1177    /* Dump a footer. */
1178    fprintf(log_file, "=================\n\n");
1179
1180    /* Set the result to success. */
1181    result = SUCCESS;
1182
1183BAIL:
1184
1185    /* Release the ASL response? */
1186    if (log_response != NULL) {
1187
1188        asl_free(log_response);
1189        log_response = NULL;
1190    }
1191
1192    /* Release the ASL query? */
1193    if (log_query != NULL) {
1194
1195        asl_free(log_query);
1196        log_query = NULL;
1197    }
1198
1199    return result;
1200}
1201
1202static
1203void
1204dump_cferror(
1205    FILE *log_file,
1206    const char *description,
1207    CFErrorRef error)
1208{
1209    CFStringRef     error_string = NULL;
1210    char            buffer[BUFFER_SIZE];
1211
1212    error_string = CFErrorCopyDescription(error);
1213    if (error_string == NULL) {
1214
1215        fprintf(stderr, "Could not copy error description?\n");
1216        goto BAIL;
1217    }
1218
1219    (void) CFStringGetCString(error_string, buffer, BUFFER_SIZE, kCFStringEncodingUTF8);
1220
1221    fprintf(stderr, "%s: %s\n", description, buffer);
1222    fprintf(log_file, "%s: %s\n", description, buffer);
1223
1224BAIL:
1225
1226    /* Release the error string? */
1227    if (error_string != NULL) {
1228
1229        CFRelease(error_string);
1230        error_string = NULL;
1231    }
1232}
1233
1234#else  // TARGET_IPHONE_SIMULATOR
1235
1236int
1237main(
1238     int argc,
1239     char **argv)
1240{
1241#pragma unused (argc, argv)
1242    return 0;
1243}
1244
1245#endif
1246