SecAccessControl.c [plain text]
#include <AssertMacros.h>
#include <Security/SecAccessControl.h>
#include <Security/SecAccessControlPriv.h>
#include <Security/SecItem.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFError.h>
#include <utilities/der_plist.h>
#if TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR)
#define USE_KEYSTORE 1
#elif TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
#define USE_KEYSTORE 1
#else
#define USE_KEYSTORE 0
#endif
#include <libaks_acl_cf_keys.h>
static CFTypeRef kSecAccessControlKeyProtection = CFSTR("prot");
CFTypeRef kSecAccessControlPolicyUserPresent = CFSTR("DeviceOwnerAuthenticated");
struct __SecAccessControl {
CFRuntimeBase _base;
CFMutableDictionaryRef dict;
};
static CFStringRef SecAccessControlCopyDescription(CFTypeRef cf) {
SecAccessControlRef access_control = (SecAccessControlRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecAccessControlRef: %p>"), access_control);
}
static Boolean SecAccessControlCompare(CFTypeRef lhs, CFTypeRef rhs) {
SecAccessControlRef laccess_control = (SecAccessControlRef)lhs;
SecAccessControlRef raccess_control = (SecAccessControlRef)rhs;
return (laccess_control == raccess_control) || CFEqual(laccess_control->dict, raccess_control->dict);
}
static void SecAccessControlDestroy(CFTypeRef cf) {
SecAccessControlRef access_control = (SecAccessControlRef)cf;
CFReleaseSafe(access_control->dict);
}
CFGiblisWithCompareFor(SecAccessControl);
SecAccessControlRef SecAccessControlCreate(CFAllocatorRef allocator, CFErrorRef *error) {
SecAccessControlRef access_control = CFTypeAllocate(SecAccessControl, struct __SecAccessControl, allocator);
if (!access_control) {
SecError(errSecAllocate, error, CFSTR("allocate memory for SecAccessControl"));
return NULL;
}
access_control->dict = CFDictionaryCreateMutableForCFTypes(allocator);
return access_control;
}
SecAccessControlRef SecAccessControlCreateWithFlags(CFAllocatorRef allocator, CFTypeRef protection,
SecAccessControlCreateFlags flags, CFErrorRef *error) {
SecAccessControlRef access_control = NULL;
CFTypeRef constraint = NULL;
require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
if (!SecAccessControlSetProtection(access_control, protection, error))
goto errOut;
if (flags & kSecAccessControlUserPresence) {
require_quiet(constraint = SecAccessConstraintCreatePolicy(kSecAccessControlPolicyUserPresent, error), errOut);
require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt,
constraint, error), errOut);
CFReleaseNull(constraint);
require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
}
return access_control;
errOut:
CFReleaseSafe(access_control);
CFReleaseSafe(constraint);
return NULL;
}
CFTypeRef SecAccessControlGetProtection(SecAccessControlRef access_control) {
return CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyProtection);
}
static bool checkItemInArray(CFTypeRef item, const CFTypeRef *values, CFIndex count, CFStringRef errMessage, CFErrorRef *error) {
for (CFIndex i = 0; i < count; i++) {
if (CFEqualSafe(item, values[i])) {
return true;
}
}
return SecError(errSecParam, error, errMessage, item);
}
#define CheckItemInArray(item, values, msg) \
{ \
const CFTypeRef vals[] = values; \
if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \
return false; \
} \
}
#define ItemArray(...) { __VA_ARGS__ }
bool SecAccessControlSetProtection(SecAccessControlRef access_control, CFTypeRef protection, CFErrorRef *error) {
CheckItemInArray(protection, ItemArray(kSecAttrAccessibleAlways, kSecAttrAccessibleAfterFirstUnlock,
kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
"SecAccessControl: invalid protection %@");
CFDictionarySetValue(access_control->dict, kSecAccessControlKeyProtection, protection);
return true;
}
SecAccessConstraintRef SecAccessConstraintCreatePolicy(CFTypeRef policy, CFErrorRef *error) {
return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintPolicy, policy, NULL);
}
SecAccessConstraintRef SecAccessConstraintCreatePasscode(bool systemPasscode) {
return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintUserPasscode, kCFBooleanTrue, NULL);
}
SecAccessConstraintRef SecAccessConstraintCreateTouchID(CFDataRef uuid, CFErrorRef *error) {
return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintBio,
uuid ? uuid : (const void *)kCFBooleanTrue, NULL);
}
SecAccessConstraintRef SecAccessConstraintCreateKofN(size_t numRequired, CFArrayRef constraints, CFErrorRef *error) {
CFNumberRef k = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numRequired);
CFMutableDictionaryRef kofn = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclParamKofN, k, NULL);
CFRelease(k);
const CFTypeRef keysToCopy[] = { kAKSKeyAclConstraintBio, kAKSKeyAclConstraintPolicy,
kAKSKeyAclConstraintUserPasscode };
SecAccessConstraintRef constraint;
CFArrayForEachC(constraints, constraint) {
require_quiet(isDictionary(constraint), errOut);
bool found = false;
for (CFIndex i = 0; i < (CFIndex)(sizeof(keysToCopy) / sizeof(keysToCopy[0])); i++) {
CFTypeRef value = CFDictionaryGetValue(constraint, keysToCopy[i]);
if (value) {
CFDictionarySetValue(kofn, keysToCopy[i], value);
found = true;
break;
}
}
require_quiet(found, errOut);
}
constraint = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintKofN, kofn, NULL);
CFRelease(kofn);
return constraint;
errOut:
SecError(errSecParam, error, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
CFReleaseSafe(kofn);
return NULL;
}
bool SecAccessConstraintSetOption(SecAccessConstraintRef constraint, CFTypeRef option, CFTypeRef value, CFErrorRef *error) {
CheckItemInArray(option, ItemArray(kAKSKeyAclConstraintAccessGroups, kAKSKeyAclParamCredentialMaxAge),
"SecAccessControl: invalid constraint option %@");
CFDictionarySetValue((CFMutableDictionaryRef)constraint, option, value);
return true;
}
bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control, CFTypeRef operation, CFTypeRef constraint, CFErrorRef *error) {
CheckItemInArray(operation, ItemArray(kAKSKeyOpEncrypt, kAKSKeyOpDecrypt,
kAKSKeyOpSync, kAKSKeyOpDefaultAcl, kAKSKeyOpDelete),
"SecAccessControl: invalid operation %@");
if (!isDictionary(constraint) && !CFEqual(constraint, kCFBooleanTrue) && !CFEqual(constraint, kCFBooleanFalse) ) {
return SecError(errSecParam, error, CFSTR("invalid constraint"));
}
CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
if (!ops) {
ops = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control));
CFDictionarySetValue(access_control->dict, kAKSKeyAcl, ops);
}
CFDictionarySetValue(ops, operation, constraint);
return true;
}
void SecAccessControlRemoveConstraintForOperation(SecAccessControlRef access_control, CFTypeRef operation) {
CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
if (ops)
CFDictionaryRemoveValue(ops, operation);
}
SecAccessConstraintRef SecAccessControlGetConstraint(SecAccessControlRef access_control, CFTypeRef operation) {
CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
if (!ops || CFDictionaryGetCount(ops) == 0)
return kCFBooleanTrue;
SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation);
if (!constraint) {
constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl);
}
return constraint;
}
CFDictionaryRef SecAccessControlGetConstraints(SecAccessControlRef access_control) {
return CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
}
void SecAccessControlSetConstraints(SecAccessControlRef access_control, CFDictionaryRef constraints) {
CFMutableDictionaryRef mutableConstraints = CFDictionaryCreateMutableCopy(NULL, 0, constraints);
CFDictionarySetValue(access_control->dict, kAKSKeyAcl, mutableConstraints);
CFReleaseSafe(mutableConstraints);
}
void SecAccessControlSetAccessGroups(SecAccessControlRef access_control, CFArrayRef access_groups) {
CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
if (!ops) {
ops = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control));
CFDictionarySetValue(access_control->dict, kAKSKeyAcl, ops);
}
CFDictionarySetValue(ops, kAKSKeyAccessGroups, access_groups);
}
CFArrayRef SecAccessControlGetAccessGroups(SecAccessControlRef access_control, CFTypeRef operation) {
CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
if (!ops)
return NULL;
CFArrayRef access_groups = NULL;
SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation);
if (!constraint) {
constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl);
}
if (constraint && isDictionary(constraint)) {
access_groups = CFDictionaryGetValue(constraint, kAKSKeyAclConstraintAccessGroups);
}
if (!access_groups) {
access_groups = CFDictionaryGetValue(ops, kAKSKeyAccessGroups);
}
return access_groups;
}
CFDataRef SecAccessControlCopyData(SecAccessControlRef access_control) {
size_t len = der_sizeof_plist(access_control->dict, NULL);
CFMutableDataRef encoded = CFDataCreateMutable(0, len);
CFDataSetLength(encoded, len);
uint8_t *der_end = CFDataGetMutableBytePtr(encoded);
const uint8_t *der = der_end;
der_end += len;
der_end = der_encode_plist(access_control->dict, NULL, der, der_end);
if (!der_end) {
CFReleaseNull(encoded);
}
return encoded;
}
SecAccessControlRef SecAccessControlCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) {
SecAccessControlRef access_control;
require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
CFPropertyListRef plist;
const uint8_t *der = CFDataGetBytePtr(data);
const uint8_t *der_end = der + CFDataGetLength(data);
require_quiet(der = der_decode_plist(0, kCFPropertyListMutableContainers, &plist, error, der, der_end), errOut);
if (der != der_end) {
SecError(errSecDecode, error, CFSTR("trailing garbage at end of SecAccessControl data"));
goto errOut;
}
CFReleaseSafe(access_control->dict);
access_control->dict = (CFMutableDictionaryRef)plist;
return access_control;
errOut:
CFReleaseSafe(access_control);
return NULL;
}