cloud_keychain_diagnose.c [plain text]
#if !TARGET_OS_EMBEDDED
#include "sec/Security/SecBase.h"
#include "sec/Security/SecKey.h"
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFPriv.h>
#if !TARGET_IPHONE_SIMULATOR
#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"
#define SUCCESS 0
#define FAILURE -1
#define MAX_PATH_LEN 1024
#define SUFFIX_LENGTH 4
#define BUFFER_SIZE 1024
#define MAX_DATA_RATE 32
typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
extern void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock);
enum {
my_keybag_state_bio_unlock = 1 << 3
};
struct dict_dump_state
{
FILE *log_file;
CFDictionaryRef dict;
unsigned int indent_level;
};
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);
int
main(
int argc,
char **argv)
{
int result = EXIT_FAILURE;
if (argc > 2) {
usage();
}
if (argc == 1) {
if (gather_diagnostics()) {
fprintf(stderr, "Could not gather diagnostics\n");
goto BAIL;
}
} else {
if (strncmp(argv[1], "enable", 6) == 0) {
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) {
if (enable_cloud_keychain_diagnostics(1)) {
fprintf(stderr, "Could not disable additional cloud keychain diagnostics\n");
goto BAIL;
}
} else {
usage();
}
}
result = EXIT_FAILURE;
BAIL:
return result;
}
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;
if (build_log_path(log_path)) {
fprintf(stderr, "Could not build the log path\n");
goto BAIL;
}
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;
}
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);
if (dump_system_information(log_file)) {
fprintf(stderr, "Could not dump the system information\n");
goto BAIL;
}
if (dump_circle_state(log_file)) {
fprintf(stderr, "Could not dump the SOS circle state\n");
goto BAIL;
}
if (dump_keychain_sync_kvs(log_file)) {
fprintf(stderr, "Could not the raw keychain syncing KVS\n");
goto BAIL;
}
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;
}
result = SUCCESS;
BAIL:
if (log_file != NULL) {
fclose(log_file);
log_file = NULL;
}
if (log_fd != -1) {
close(log_fd);
log_fd = -1;
}
return result;
}
static
int
enable_cloud_keychain_diagnostics(
const unsigned int enable_flag)
{
int result = FAILURE;
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;
now = time(NULL);
time_cube = localtime(&now);
if (time_cube == NULL) {
fprintf(stderr, "I don't know what time it is.\n");
goto BAIL;
}
system_version_dict = _CFCopySystemVersionDictionary();
if (system_version_dict == NULL) {
fprintf(stderr, "Could not copy the system version dictionary\n");
goto BAIL;
}
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;
}
if (CFEqual(product_name, CFSTR("Mac OS X"))) {
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 {
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);
}
result = SUCCESS;
BAIL:
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;
fprintf(log_file, "Host Information:\n");
fprintf(log_file, "=================\n");
dict = _CFCopySystemVersionDictionary();
if (dict == NULL) {
fprintf(stderr, "Could not copy the system version dictionary\n");
goto BAIL;
}
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;
}
if (!CFStringGetCString(product_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the product name to a C string\n");
goto BAIL;
}
fprintf(log_file, "Product Name: %s\n", buffer);
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;
}
if (!CFStringGetCString(product_version, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the product version to a C string\n");
goto BAIL;
}
fprintf(log_file, "Product Version: %s\n", buffer);
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;
}
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;
}
fprintf(log_file, "Product Build Version: %s\n", buffer);
if (gethostname(buffer, BUFFER_SIZE) == -1) {
fprintf(stderr, "Could not lookup the host name\n");
goto BAIL;
}
fprintf(log_file, "Host Name: %s\n", buffer);
if (gethostname(buffer, BUFFER_SIZE) == -1) {
fprintf(stderr, "Could not lookup the host name\n");
goto BAIL;
}
now = time(NULL);
fprintf(log_file, "Time: %s", ctime(&now));
if (CFEqual(product_name, CFSTR("Mac OS X"))) {
keybag_handle = session_keybag_handle;
shim_flag = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
if (shim_flag && CFGetTypeID(shim_flag) == CFBooleanGetTypeID()) {
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 {
keybag_handle = device_keybag_handle;
}
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;
}
}
fprintf(log_file, "=================\n\n");
result = SUCCESS;
BAIL:
if (shim_flag) {
CFRelease(shim_flag);
shim_flag = NULL;
}
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;
char *circle_state_string = NULL;
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];
fprintf(log_file, "SOS Circle State:\n");
fprintf(log_file, "=================\n");
circle_status = SOSCCThisDeviceIsInCircle(&error);
if (error != NULL) {
dump_cferror(log_file, "Could not call SOSCCThisDeviceIsInCircle", error);
} else {
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;
case kSOSCCParamErr: {
circle_state_string = "kSOSCCParamErr";
}break;
case kSOSCCMemoryErr: {
circle_state_string = "kSOSCCMemoryErr";
}break;
default: {
circle_state_string = "Unknown circle status?";
}
}
fprintf(log_file, "Circle Status: %s\n", circle_state_string);
}
if (!SOSCCCanAuthenticate(&error)) {
if (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");
}
peer_list = SOSCCCopyPeerPeerInfo(&error);
if (error) {
dump_cferror(log_file, "Could not call SOSCCCopyPeerPeerInfo", error);
} else {
num_peers = CFArrayGetCount(peer_list);
fprintf(log_file, "Number of syncing peers: %ld\n", num_peers);
if (num_peers > 0) {
fprintf(log_file, "\n");
}
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_name = SOSPeerInfoGetPeerName(peer_info);
if (peer_name == NULL) {
fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
goto BAIL;
}
if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the peer name to a C string\n");
goto BAIL;
}
fprintf(log_file, " Peer Name: %s\n", buffer);
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;
}
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;
}
fprintf(log_file, " Peer Device Type: %s\n", buffer);
peerID = SOSPeerInfoGetPeerID(peer_info);
if (peerID == NULL) {
fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
goto BAIL;
}
fprintf(log_file, " Peer ID: %s\n", buffer);
if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the peer ID to a C string\n");
goto BAIL;
}
fprintf(log_file, "\n");
}
CFRelease(peer_list);
peer_list = NULL;
}
peer_list = SOSCCCopyApplicantPeerInfo(&error);
if (error) {
dump_cferror(log_file, "Could not call SOSCCCopyApplicantPeerInfo", error);
} else {
num_peers = CFArrayGetCount(peer_list);
fprintf(log_file, "Number of applicant peers: %ld\n", num_peers);
if (num_peers > 0) {
fprintf(log_file, "\n");
}
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_name = SOSPeerInfoGetPeerName(peer_info);
if (peer_name == NULL) {
fprintf(stderr, "Could not extract peer name %ld of %ld\n", i, num_peers);
goto BAIL;
}
if (!CFStringGetCString(peer_name, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the peer name to a C string\n");
goto BAIL;
}
fprintf(log_file, " Applicant Name: %s\n", buffer);
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;
}
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;
}
fprintf(log_file, " Applicant Device Type: %s\n", buffer);
peerID = SOSPeerInfoGetPeerID(peer_info);
if (peerID == NULL) {
fprintf(stderr, "Could not extract peer ID %ld of %ld\n", i, num_peers);
goto BAIL;
}
fprintf(log_file, " Applicant ID: %s\n", buffer);
if (!CFStringGetCString(peerID, buffer, BUFFER_SIZE, kCFStringEncodingUTF8)) {
fprintf(stderr, "Could not convert the peer ID to a C string\n");
goto BAIL;
}
if (i < num_peers - 1) {
fprintf(log_file, "\n");
}
}
CFRelease(peer_list);
peer_list = NULL;
}
fprintf(log_file, "=================\n\n");
result = SUCCESS;
BAIL:
if (peer_gestalt != NULL) {
CFRelease(peer_gestalt);
peer_gestalt = NULL;
}
if (peer_list != NULL) {
CFRelease(peer_list);
peer_list = NULL;
}
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;
fprintf(log_file, "Keychain Syncing KVS:\n");
fprintf(log_file, "=================\n");
cloud_queue = dispatch_queue_create("cloud_queue", DISPATCH_QUEUE_SERIAL);
waitSemaphore = dispatch_semaphore_create(0);
finishTime = dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC);
cloud_group = dispatch_group_create();
dispatch_group_enter(cloud_group);
CloudKeychainReplyBlock replyBlock = ^(CFDictionaryRef returnedValues, CFErrorRef error)
{
if (returnedValues) {
kvs_dict = (returnedValues);
CFRetain(kvs_dict);
}
dispatch_group_leave(cloud_group);
dispatch_semaphore_signal(waitSemaphore);
};
SOSCloudKeychainGetAllObjectsFromCloud(cloud_queue, replyBlock);
dispatch_semaphore_wait(waitSemaphore, finishTime);
dispatch_release(waitSemaphore);
if (kvs_dict) {
dump_dict(log_file, kvs_dict, 0);
}
fprintf(log_file, "=================\n\n");
result = SUCCESS;
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;
dump_state.log_file = log_file;
dump_state.dict = dict;
dump_state.indent_level = indent_level;
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;
key_object = (CFTypeRef) key;
value_object = (CFTypeRef) value;
dump_state = (struct dict_dump_state *)context;
for (i = 0; i < dump_state->indent_level; i++) {
fprintf(dump_state->log_file, " ");
}
if (CFGetTypeID(key_object) == CFStringGetTypeID()) {
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);
}
}
if (CFGetTypeID(value_object) == CFStringGetTypeID()) {
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()) {
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;
fprintf(log_file, "ASL: %s\n", asl_sender);
fprintf(log_file, "=================\n");
log_query = asl_new(ASL_TYPE_QUERY);
if (log_query == NULL) {
fprintf(stderr, "Could not create ASL query\n");
goto BAIL;
}
asl_set_query(log_query, ASL_KEY_SENDER, asl_sender, ASL_QUERY_OP_EQUAL);
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 {
while ((log_message = aslresponse_next(log_response)) != NULL) {
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);
free(message_string);
message_string = NULL;
}
}
fprintf(log_file, "=================\n\n");
result = SUCCESS;
BAIL:
if (log_response != NULL) {
aslresponse_free(log_response);
log_response = NULL;
}
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:
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