#include <securityd/keystore.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <CoreFoundation/CFData.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonCryptor.h>
#include <libkern/OSByteOrder.h>
#include <security_utilities/debugging.h>
#include <assert.h>
#include <Security/SecInternal.h>
#include <TargetConditionals.h>
#include <AssertMacros.h>
#include <asl.h>
#if TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
#define USE_KEYSTORE 1
#define USE_DISPATCH 1
#else
#define USE_KEYSTORE 0
#endif
#if USE_KEYSTORE
#include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
#if USE_DISPATCH
#include <dispatch/dispatch.h>
static dispatch_once_t ks_init_once;
#else
#include <pthread.h>
static pthread_once_t ks_init_once = PTHREAD_ONCE_INIT;
#endif
static io_connect_t ks_connect_handle = MACH_PORT_NULL;
static void ks_service_matching_callback(void *refcon, io_iterator_t iterator)
{
io_object_t obj = IO_OBJECT_NULL;
while ((obj = IOIteratorNext(iterator))) {
kern_return_t ret = IOServiceOpen(obj, mach_task_self(), 0,
(io_connect_t*)refcon);
if (ret) {
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"IOServiceOpen() failed: %d", ret);
}
IOObjectRelease(obj);
}
}
io_connect_t ks_connect_to_service(const char *className)
{
kern_return_t kernResult;
io_connect_t connect = IO_OBJECT_NULL;
io_iterator_t iterator = IO_OBJECT_NULL;
IONotificationPortRef notifyport = NULL;
CFDictionaryRef classToMatch;
if ((classToMatch = IOServiceMatching(className)) == NULL) {
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"IOServiceMatching failed for '%s'", className);
return connect;
}
CFRetain(classToMatch);
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
classToMatch, &iterator);
if (kernResult == KERN_SUCCESS) {
CFRelease(classToMatch);
} else {
asl_log(NULL, NULL, ASL_LEVEL_WARNING,
"IOServiceGetMatchingServices() failed %d", kernResult);
notifyport = IONotificationPortCreate(kIOMasterPortDefault);
if (!notifyport) {
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"IONotificationPortCreate() failed");
return connect;
}
kernResult = IOServiceAddMatchingNotification(notifyport,
kIOFirstMatchNotification, classToMatch,
ks_service_matching_callback, &connect, &iterator);
if (kernResult) {
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"IOServiceAddMatchingNotification() failed: %d", kernResult);
return connect;
}
}
ks_service_matching_callback(&connect, iterator);
if (notifyport) {
if (connect == IO_OBJECT_NULL) {
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"Waiting for %s to show up.", className);
CFStringRef mode = CFSTR("WaitForCryptoService");
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyport), mode);
CFRunLoopRunInMode(mode, 30.0, true);
if (connect == MACH_PORT_NULL)
asl_log(NULL, NULL, ASL_LEVEL_ERR, "Cannot find %s", className);
}
IONotificationPortDestroy(notifyport);
}
IOObjectRelease(iterator);
if (connect != IO_OBJECT_NULL) {
secdebug("iokit", "obtained connection for '%s'", className);
}
return connect;
}
#if USE_DISPATCH
static void ks_crypto_init(void *unused)
#else
static void ks_crypto_init(void)
#endif
{
ks_connect_handle = ks_connect_to_service(kAppleKeyStoreServiceName);
}
io_connect_t ks_get_connect(void)
{
#if USE_DISPATCH
dispatch_once_f(&ks_init_once, NULL, ks_crypto_init);
#else
pthread_once(&ks_init_once, ks_crypto_init);
#endif
return ks_connect_handle;
}
bool ks_available(void)
{
ks_get_connect();
return true;
}
static bool ks_crypt(uint32_t selector, uint64_t keybag,
uint64_t keyclass, const uint8_t *input, size_t inputLength,
uint8_t *buffer, size_t *keySize) {
kern_return_t kernResult;
if (!ks_connect_handle) {
secdebug("ks", "AppleKeyStore.kext not found");
return false;
}
uint64_t inputs[] = { keybag, keyclass };
uint32_t num_inputs = sizeof(inputs)/sizeof(*inputs);
kernResult = IOConnectCallMethod(ks_connect_handle, selector,
inputs, num_inputs, input, inputLength, NULL, NULL, buffer,
keySize);
if (kernResult != KERN_SUCCESS) {
asl_log(NULL, NULL, ASL_LEVEL_ERR, "kAppleKeyStore selector(%d): %d",
selector, kernResult);
return false;
}
return true;
}
void ks_free(ks_object_t object) {
free(object._kso);
}
uint8_t *ks_unwrap(uint64_t keybag, uint64_t keyclass,
const uint8_t *wrappedKey, size_t wrappedKeySize,
uint8_t *buffer, size_t bufferSize, size_t *keySize) {
*keySize = bufferSize;
if (!ks_crypt(kAppleKeyStoreKeyUnwrap,
keybag, keyclass, wrappedKey, wrappedKeySize, buffer, keySize))
return NULL;
return buffer;
}
uint8_t *ks_wrap(uint64_t keybag, uint64_t keyclass,
const uint8_t *key, size_t keyByteSize,
uint8_t *buffer, size_t bufferSize, size_t *wrappedKeySize) {
*wrappedKeySize = bufferSize;
if (ks_crypt(kAppleKeyStoreKeyWrap,
keybag, keyclass, key, keyByteSize,
buffer, wrappedKeySize))
return NULL;
return buffer;
}
#else
bool ks_available(void)
{
return false;
}
#endif