SecKeybagSupport.c [plain text]
#include <securityd/SecKeybagSupport.h>
#include <securityd/SecItemServer.h>
#if USE_KEYSTORE
#include <IOKit/IOKitLib.h>
#include <libaks.h>
#include <libaks_acl_cf_keys.h>
#include <utilities/der_plist.h>
#include <corecrypto/ccder.h>
#if TARGET_OS_EMBEDDED
#include <MobileKeyBag/MobileKeyBag.h>
#endif
#endif
#if USE_KEYSTORE
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
keybag_handle_t g_keychain_keybag = session_keybag_handle;
#else
keybag_handle_t g_keychain_keybag = device_keybag_handle;
#endif
#else
keybag_handle_t g_keychain_keybag = 0;
#endif
void SecItemServerSetKeychainKeybag(int32_t keybag)
{
g_keychain_keybag=keybag;
}
void SecItemServerResetKeychainKeybag(void)
{
#if USE_KEYSTORE
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
g_keychain_keybag = session_keybag_handle;
#else
g_keychain_keybag = device_keybag_handle;
#endif
#else
g_keychain_keybag = 0;
#endif
}
#if USE_KEYSTORE
static bool hwaes_key_available(void)
{
keybag_handle_t handle = bad_keybag_handle;
keybag_handle_t special_handle = bad_keybag_handle;
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
special_handle = session_keybag_handle;
#elif TARGET_OS_EMBEDDED
special_handle = device_keybag_handle;
#endif
kern_return_t kr = aks_get_system(special_handle, &handle);
if (kr != kIOReturnSuccess) {
#if TARGET_OS_EMBEDDED
int kb_state = MKBGetDeviceLockState(NULL);
asl_log(NULL, NULL, ASL_LEVEL_INFO, "AppleKeyStore lock state: %d", kb_state);
#endif
}
return true;
}
#else
static bool hwaes_key_available(void)
{
return false;
}
#endif
bool ks_crypt(uint32_t operation, keybag_handle_t keybag,
keyclass_t keyclass, uint32_t textLength, const uint8_t *source, keyclass_t *actual_class, CFMutableDataRef dest, CFErrorRef *error) {
#if USE_KEYSTORE
kern_return_t kernResult = kIOReturnBadArgument;
int dest_len = (int)CFDataGetLength(dest);
if (operation == kSecKsWrap) {
kernResult = aks_wrap_key(source, textLength, keyclass, keybag, CFDataGetMutableBytePtr(dest), &dest_len, actual_class);
} else if (operation == kSecKsUnwrap) {
kernResult = aks_unwrap_key(source, textLength, keyclass, keybag, CFDataGetMutableBytePtr(dest), &dest_len);
}
if (kernResult != KERN_SUCCESS) {
if ((kernResult == kIOReturnNotPermitted) || (kernResult == kIOReturnNotPrivileged)) {
return SecError(errSecInteractionNotAllowed, error, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32", bag: %"PRId32") Access to item attempted while keychain is locked."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
} else if (kernResult == kIOReturnError) {
return SecError(errSecDecode, error, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32", bag: %"PRId32") Item can't be decrypted on this device, ever, so drop the item."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
} else {
return SecError(errSecNotAvailable, error, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32", bag: %"PRId32")"),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
}
}
else
CFDataSetLength(dest, dest_len);
return true;
#else
uint32_t dest_len = (uint32_t)CFDataGetLength(dest);
if (operation == kSecKsWrap) {
if (dest_len >= textLength + 8) {
memcpy(CFDataGetMutableBytePtr(dest), source, textLength);
memset(CFDataGetMutableBytePtr(dest) + textLength, 8, 8);
CFDataSetLength(dest, textLength + 8);
*actual_class = keyclass;
} else
return SecError(errSecNotAvailable, error, CFSTR("ks_crypt: failed to wrap item (class %"PRId32")"), keyclass);
} else if (operation == kSecKsUnwrap) {
if (dest_len + 8 >= textLength) {
memcpy(CFDataGetMutableBytePtr(dest), source, textLength - 8);
CFDataSetLength(dest, textLength - 8);
} else
return SecError(errSecNotAvailable, error, CFSTR("ks_crypt: failed to unwrap item (class %"PRId32")"), keyclass);
}
return true;
#endif
}
#if USE_KEYSTORE
bool ks_crypt_acl(uint32_t operation, keybag_handle_t keybag, keyclass_t keyclass,
uint32_t textLength, const uint8_t *source, CFMutableDataRef dest,
CFDataRef acl, CFDataRef acm_context, CFDataRef caller_access_groups, CFErrorRef *error) {
kern_return_t kernResult = kIOReturnBadArgument;
uint8_t *params = NULL, *der = NULL;
const uint8_t *access_groups = caller_access_groups?CFDataGetBytePtr(caller_access_groups):NULL;
size_t params_len = 0, der_len = 0, access_groups_len = caller_access_groups?CFDataGetLength(caller_access_groups):0;
if (operation == kSecKsWrap) {
aks_operation_optional_params(0, 0, CFDataGetBytePtr(acl), CFDataGetLength(acl), 0, 0, (void**)¶ms, ¶ms_len);
kernResult = aks_encrypt(keybag, keyclass, source, textLength, params, params_len, (void**)&der, &der_len);
} else if (operation == kSecKsUnwrap) {
aks_operation_optional_params(access_groups, access_groups_len, 0, 0, CFDataGetBytePtr(acm_context), (int)CFDataGetLength(acm_context), (void**)¶ms, ¶ms_len);
kernResult = aks_decrypt(keybag, source, textLength, params, params_len, (void**)&der, &der_len);
} else if (operation == kSecKsDelete) {
aks_operation_optional_params(access_groups, access_groups_len, 0, 0, CFDataGetBytePtr(acm_context), (int)CFDataGetLength(acm_context), (void**)¶ms, ¶ms_len);
kernResult = aks_delete(keybag, source, textLength, params, params_len);
}
bool result = false;
if (kernResult != KERN_SUCCESS) {
if ((kernResult == kIOReturnNotPermitted) || (kernResult == kIOReturnNotPrivileged)) {
result = SecError(errSecInteractionNotAllowed, error, CFSTR("ks_crypt_acl: %x failed to %s item (class %"PRId32", bag: %"PRId32") Access to item attempted while keychain is locked."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
} else if (kernResult == kIOReturnError) {
result = SecError(errSecDecode, error, CFSTR("ks_crypt_acl: %x failed to %s item (class %"PRId32", bag: %"PRId32") Item can't be decrypted on this device, ever, so drop the item."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
} else {
result = SecError(errSecNotAvailable, error, CFSTR("ks_crypt_acl: %x failed to %s item (class %"PRId32", bag: %"PRId32")"),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag);
}
}
else {
if (operation != kSecKsDelete) {
const uint8_t *value = der;
if (operation == kSecKsUnwrap) {
ccder_tag der_tag;
size_t der_tag_len;
value = ccder_decode_tag(&der_tag, der, der + der_len);
value = ccder_decode_len(&der_tag_len, value, der + der_len);
require_action(der_tag == CCDER_OCTET_STRING, out,
SecError(errSecDecode, error, CFSTR("ks_crypt_acl: %x failed to %s item (class %"PRId32", bag: %"PRId32") Item can't be decrypted due to invalid der tag, so drop the item."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag));
require_action(der_tag_len == (size_t)((der + der_len) - value), out,
SecError(errSecDecode, error, CFSTR("ks_crypt_acl: %x failed to %s item (class %"PRId32", bag: %"PRId32") Item can't be decrypted due to invalid der tag length, so drop the item."),
kernResult, (operation == kSecKsWrap ? "wrap" : "unwrap"), keyclass, keybag));
}
if(CFDataGetLength(dest) != (der + der_len) - value)
CFDataSetLength(dest, (der + der_len) - value);
memcpy(CFDataGetMutableBytePtr(dest), value, CFDataGetLength(dest));
}
result = true;
}
out:
if(params)
free(params);
if(der)
free(der);
return result;
}
#endif
bool use_hwaes(void) {
static bool use_hwaes;
static dispatch_once_t check_once;
dispatch_once(&check_once, ^{
use_hwaes = hwaes_key_available();
if (use_hwaes) {
asl_log(NULL, NULL, ASL_LEVEL_INFO, "using hwaes key");
} else {
asl_log(NULL, NULL, ASL_LEVEL_ERR, "unable to access hwaes key");
}
});
return use_hwaes;
}
bool ks_open_keybag(CFDataRef keybag, CFDataRef password, keybag_handle_t *handle, CFErrorRef *error) {
#if USE_KEYSTORE
kern_return_t kernResult;
kernResult = aks_load_bag(CFDataGetBytePtr(keybag), (int)CFDataGetLength(keybag), handle);
if (kernResult)
return SecKernError(kernResult, error, CFSTR("aks_load_bag failed: %@"), keybag);
if (password) {
kernResult = aks_unlock_bag(*handle, CFDataGetBytePtr(password), (int)CFDataGetLength(password));
if (kernResult) {
aks_unload_bag(*handle);
return SecKernError(kernResult, error, CFSTR("aks_unlock_bag failed"));
}
}
return true;
#else
*handle = KEYBAG_NONE;
return true;
#endif
}
bool ks_close_keybag(keybag_handle_t keybag, CFErrorRef *error) {
#if USE_KEYSTORE
IOReturn kernResult = aks_unload_bag(keybag);
if (kernResult) {
return SecKernError(kernResult, error, CFSTR("aks_unload_bag failed"));
}
#endif
return true;
}