#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecCertificate.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecBase.h>
#include <utilities/array_size.h>
#include <utilities/SecCFWrappers.h>
#include <stdlib.h>
#include <unistd.h>
#include <Security/SecAccessControl.h>
#include <Security/SecAccessControlPriv.h>
#include <libaks_acl_cf_keys.h>
#include "testlist.h"
#if USE_KEYSTORE
#include <coreauthd_spi.h>
#endif
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
#include <MobileKeyBag/MobileKeyBag.h>
#endif
enum ItemAttrType {
kBoolItemAttr,
kNumberItemAttr,
kStringItemAttr,
kDataItemAttr,
kBlobItemAttr,
kDateItemAttr,
kAccessabilityItemAttr,
kAccessGroupItemAttr,
};
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
CFStringRef temporaryPasscode = CFSTR("1111");
static bool changePasscode(CFStringRef oldPasscode, CFStringRef newPasscode)
{
CFDataRef oldPasscodeData = NULL;
CFDataRef newPasscodeData= NULL;
if (oldPasscode) {
oldPasscodeData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, oldPasscode, kCFStringEncodingUTF8, 0x00);
}
if (newPasscode) {
newPasscodeData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, newPasscode, kCFStringEncodingUTF8, 0x00);
}
int status = MKBKeyBagChangeSystemSecret(oldPasscodeData, newPasscodeData, NULL);
CFReleaseSafe(oldPasscodeData);
CFReleaseSafe(newPasscodeData);
return status == kMobileKeyBagSuccess;
}
#endif
#if !TARGET_OS_SIMULATOR
static void WithEachString(void(^each)(CFStringRef attr, enum ItemAttrType atype), ...) {
va_list ap;
va_start(ap, each);
CFStringRef attr;
while((attr = va_arg(ap, CFStringRef)) != NULL) {
enum ItemAttrType atype = va_arg(ap, enum ItemAttrType);
each(attr, atype);
}
va_end(ap);
}
static void ItemForEachPKAttr(CFMutableDictionaryRef item, void(^each)(CFStringRef attr, enum ItemAttrType atype)) {
CFStringRef iclass = CFDictionaryGetValue(item, kSecClass);
if (!iclass) {
return;
} else if (CFEqual(iclass, kSecClassGenericPassword)) {
WithEachString(each,
kSecAttrAccessible, kAccessabilityItemAttr,
kSecAttrAccessGroup, kAccessGroupItemAttr,
kSecAttrAccount, kStringItemAttr,
kSecAttrService, kStringItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
NULL);
} else if (CFEqual(iclass, kSecClassInternetPassword)) {
WithEachString(each,
kSecAttrAccessible, kAccessabilityItemAttr,
kSecAttrAccessGroup, kAccessGroupItemAttr,
kSecAttrAccount, kStringItemAttr,
kSecAttrSecurityDomain, kStringItemAttr,
kSecAttrServer, kStringItemAttr,
kSecAttrProtocol, kNumberItemAttr,
kSecAttrAuthenticationType, kNumberItemAttr,
kSecAttrPort, kNumberItemAttr,
kSecAttrPath, kStringItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
NULL);
} else if (CFEqual(iclass, kSecClassCertificate)) {
WithEachString(each,
kSecAttrAccessible, kAccessabilityItemAttr,
kSecAttrAccessGroup, kAccessGroupItemAttr,
kSecAttrCertificateType, kNumberItemAttr,
kSecAttrIssuer, kDataItemAttr,
kSecAttrSerialNumber, kDataItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
NULL);
} else if (CFEqual(iclass, kSecClassKey)) {
WithEachString(each,
kSecAttrAccessible, kAccessabilityItemAttr,
kSecAttrAccessGroup, kAccessGroupItemAttr,
kSecAttrKeyClass, kStringItemAttr, kSecAttrApplicationLabel, kDataItemAttr,
kSecAttrApplicationTag, kDataItemAttr,
kSecAttrKeyType, kNumberItemAttr,
kSecAttrKeySizeInBits, kNumberItemAttr,
kSecAttrEffectiveKeySize, kNumberItemAttr,
kSecAttrStartDate, kDateItemAttr,
kSecAttrEndDate, kDateItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
NULL);
} else if (CFEqual(iclass, kSecClassIdentity)) {
WithEachString(each,
kSecAttrAccessible, kAccessabilityItemAttr,
kSecAttrAccessGroup, kAccessGroupItemAttr,
kSecAttrCertificateType, kNumberItemAttr,
kSecAttrIssuer, kDataItemAttr,
kSecAttrSerialNumber, kDataItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
kSecAttrKeyClass, kStringItemAttr, kSecAttrApplicationLabel, kDataItemAttr,
kSecAttrApplicationTag, kDataItemAttr,
kSecAttrKeyType, kNumberItemAttr,
kSecAttrKeySizeInBits, kNumberItemAttr,
kSecAttrEffectiveKeySize, kNumberItemAttr,
kSecAttrStartDate, kDateItemAttr,
kSecAttrEndDate, kDateItemAttr,
kSecAttrSynchronizable, kBoolItemAttr,
NULL);
}
}
static CFMutableDictionaryRef ItemCreate(int num) {
CFStringRef iclass = NULL;
switch (num % 4) {
case 0:
iclass = kSecClassInternetPassword;
break;
case 1:
iclass = kSecClassGenericPassword;
break;
case 2:
iclass = kSecClassKey;
break;
case 3:
iclass = kSecClassCertificate;
break;
}
return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, iclass, NULL);
}
static void tests(bool isPasscodeSet)
{
for (int num = 0 ; num < 8; ++num) {
__block CFTypeRef protection = kSecAttrAccessibleWhenUnlocked;
CFMutableDictionaryRef item = ItemCreate(num);
ItemForEachPKAttr(item, ^(CFStringRef attr, enum ItemAttrType atype) {
CFTypeRef value = NULL;
switch (atype) {
case kBoolItemAttr:
value = (num % 2 == 0 ? kCFBooleanTrue : kCFBooleanFalse);
CFRetain(value);
break;
case kNumberItemAttr:
value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
break;
case kStringItemAttr:
case kBlobItemAttr:
value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("acl-stress-string-%d"), num);
break;
case kDataItemAttr:
{
char buf[10];
int len = snprintf(buf, sizeof(buf), "acl-stress-data-%d", num);
value = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buf, len);
break;
}
case kDateItemAttr:
value = NULL; break;
case kAccessabilityItemAttr:
{
CFStringRef accessabilites[] = {
kSecAttrAccessibleWhenUnlocked,
kSecAttrAccessibleAfterFirstUnlock,
kSecAttrAccessibleAlwaysPrivate,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate,
};
protection = accessabilites[num % array_size(accessabilites)];
break;
}
case kAccessGroupItemAttr:
{
CFStringRef accessGroups[] = {
NULL,
CFSTR("com.apple.security.sos"), };
value = accessGroups[num % array_size(accessGroups)];
break;
}
}
if (value)
CFDictionarySetValue(item, attr, value);
CFReleaseSafe(value);
});
SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
ok(aclRef, "Create SecAccessControlRef");
ok(SecAccessControlSetProtection(aclRef, protection, NULL), "Set protection");
ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDecrypt, kCFBooleanTrue, NULL), "Set operation decrypt to true");
ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, kCFBooleanTrue, NULL), "Set operation delete to true");
ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrypt, kCFBooleanTrue, NULL), "Set operation encrypt to true");
SecAccessControlRef invalidAclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
ok(invalidAclRef, "Create invalid SecAccessControlRef");
ok(SecAccessControlSetProtection(invalidAclRef, protection, NULL), "Set protection");
CFTypeRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR("invalidPolicy"), NULL);
ok(constraint, "Create invalid constraint");
ok(SecAccessControlAddConstraintForOperation(invalidAclRef, kAKSKeyOpDecrypt, constraint, NULL), "Add invalid constraint");
CFReleaseSafe(constraint);
CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
CFDictionarySetValue(item, kSecAttrAccessControl, invalidAclRef);
is_status(SecItemAdd(item, NULL), errSecParam, "do not add local with invalid acl");
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after add failed");
CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
ok_status(SecItemAdd(item, NULL), "add local ");
ok_status(SecItemCopyMatching(item, NULL), "find local");
ok_status(SecItemDelete(item), "delete local");
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after delete local");
CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
is_status(SecItemAdd(item, NULL), errSecParam, "add sync");
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find sync");
CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
assert(protection);
SecAccessControlRef privateKeyUsageAclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, protection, kSecAccessControlPrivateKeyUsage, NULL);
ok(privateKeyUsageAclRef, "Create SecAccessControlRef for kSecAccessControlPrivateKeyUsage");
CFDictionarySetValue(item, kSecAttrAccessControl, privateKeyUsageAclRef);
is_status(SecItemAdd(item, NULL), errSecAuthFailed, "add local - kSecAccessControlPrivateKeyUsage without constraint");
CFReleaseNull(privateKeyUsageAclRef);
if(isPasscodeSet) {
privateKeyUsageAclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, protection, kSecAccessControlDevicePasscode | kSecAccessControlPrivateKeyUsage, NULL);
ok(privateKeyUsageAclRef, "Create SecAccessControlRef for kSecAccessControlPrivateKeyUsage");
CFDictionarySetValue(item, kSecAttrAccessControl, privateKeyUsageAclRef);
is_status(SecItemAdd(item, NULL), errSecAuthFailed, "add local - kSecAccessControlPrivateKeyUsage with constraint");
CFReleaseSafe(privateKeyUsageAclRef);
SecAccessControlRef aclWithUIRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, protection, kSecAccessControlUserPresence, NULL);
ok(aclWithUIRef, "Create SecAccessControlRef which require UI interaction");
CFDictionarySetValue(item, kSecAttrAccessControl, aclWithUIRef);
ok_status(SecItemAdd(item, NULL), "add local - acl with authentication UI");
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUIFail);
is_status(SecItemCopyMatching(item, NULL), errSecInteractionNotAllowed, "find local - acl with authentication UI");
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "find local - acl with authentication UI");
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUIAllow);
ok_status(SecItemDelete(item), "delete local - acl with authentication UI");
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after delete local - acl with authentication UI");
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUIFail);
ok_status(SecItemAdd(item, NULL), "add local - acl with authentication UI");
is_status(SecItemCopyMatching(item, NULL), errSecInteractionNotAllowed, "find local - acl with authentication UI");
ok_status(SecItemDelete(item), "delete local - acl with authentication UI");
is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after delete local - acl with authentication UI");
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUIAllow);
CFReleaseSafe(aclWithUIRef);
aclWithUIRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
ok(aclWithUIRef, "Create SecAccessControlRef which require UI interaction");
CFDictionarySetValue(item, kSecAttrAccessControl, aclWithUIRef);
ok_status(SecItemAdd(item, NULL), "add local - acl with authentication UI");
changePasscode(temporaryPasscode, NULL);
ok_status(SecItemDelete(item), "delete local - AKPU");
changePasscode(NULL, temporaryPasscode);
CFReleaseSafe(aclWithUIRef);
}
#endif
CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
is_status(SecItemAdd(item, NULL), errSecParam, "add local - invalid kSecUseAuthenticationUISkip");
is_status(SecItemDelete(item), errSecParam, "delete local - invalid kSecUseAuthenticationUISkip");
CFRelease(item);
CFReleaseSafe(aclRef);
CFReleaseSafe(invalidAclRef);
}
}
#endif
int sec_acl_stress(int argc, char *const *argv)
{
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
bool removeTemporaryPasscode = false;
bool isPasscodeSet = false;
if (MKBGetDeviceLockState(NULL) == kMobileKeyBagDisabled) {
removeTemporaryPasscode = changePasscode(NULL, temporaryPasscode);
isPasscodeSet = MKBGetDeviceLockState(NULL) != kMobileKeyBagDisabled;
}
plan_tests(isPasscodeSet?288:168);
tests(isPasscodeSet);
if (removeTemporaryPasscode) {
changePasscode(temporaryPasscode, NULL);
}
#elif TARGET_OS_MAC && !TARGET_OS_SIMULATOR
plan_tests(152);
tests(false);
#else
plan_tests(1);
ok(true);
#endif
return 0;
}