#include <stdlib.h>
#include <strings.h>
#include "certGroupUtils.h"
#include "TPCertInfo.h"
#include "TPCrlInfo.h"
#include "tpPolicies.h"
#include "tpdebugging.h"
#include "tpCrlVerify.h"
#include <Security/oidsalg.h>
#include <Security/cssmapple.h>
#include "tpCertAllowList.h"
#include <Security/SecBase.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
#include <CoreFoundation/CFError.h>
#include <CoreFoundation/CFDictionary.h>
static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
CFStringRef resourceType, CFStringRef subDirName)
{
CFURLRef rsrcUrl = NULL;
CFBundleRef bundle = NULL;
CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kSecSystemTrustStoreBundlePath, kCFURLPOSIXPathStyle, true);
if (bundleUrl) {
bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
CFRelease(bundleUrl);
}
if (bundle) {
rsrcUrl = CFBundleCopyResourceURL(bundle, resourceName, resourceType, subDirName);
if (!rsrcUrl) {
tpDebug("resource: not found");
}
CFRelease(bundle);
}
return rsrcUrl;
}
static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
CFStringRef resourceType, CFStringRef subDirName)
{
CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
CFDataRef data = NULL;
if (url) {
SInt32 error;
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
url, &data, NULL, NULL, &error)) {
tpDebug("Allow list read: %ld", (long) error);
}
CFRelease(url);
}
return data;
}
static CFDictionaryRef InitializeAllowList()
{
CFDataRef xmlData = NULL;
xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL);
CFPropertyListRef allowList = NULL;
if (xmlData) {
allowList = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
CFRelease(xmlData);
}
if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
return (CFDictionaryRef) allowList;
} else {
if (allowList)
CFRelease(allowList);
return NULL;
}
}
static inline CFComparisonResult CFDataCompare(CFDataRef left, CFDataRef right)
{
const size_t left_size = CFDataGetLength(left);
const size_t right_size = CFDataGetLength(right);
const size_t shortest = (left_size <= right_size) ? left_size : right_size;
int comparison = memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), shortest);
if (comparison > 0 || (comparison == 0 && left_size > right_size))
return kCFCompareGreaterThan;
else if (comparison < 0 || (comparison == 0 && left_size < right_size))
return kCFCompareLessThan;
else
return kCFCompareEqualTo;
}
static inline void CFStringAppendHexData(CFMutableStringRef s, CFDataRef data) {
const uint8_t *bytes = CFDataGetBytePtr(data);
CFIndex len = CFDataGetLength(data);
for (CFIndex ix = 0; ix < len; ++ix) {
CFStringAppendFormat(s, 0, CFSTR("%02X"), bytes[ix]);
}
}
static inline CF_RETURNS_RETAINED CFStringRef CFDataCopyHexString(CFDataRef data) {
CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 2 * CFDataGetLength(data));
CFStringAppendHexData(hexString, data);
return hexString;
}
CSSM_RETURN tpCheckCertificateAllowList(TPCertGroup &certGroup) {
CSSM_RETURN result = CSSMERR_TP_NOT_TRUSTED;
unsigned numCerts = certGroup.numCerts();
int i;
CFRange range;
CFArrayRef allowedCerts = NULL;
TPCertInfo *last = certGroup.lastCert();
if (!last) {
return result;
}
const CSSM_DATA *authKeyID = last->authorityKeyID();
if (!authKeyID || !authKeyID->Data) {
return result;
}
CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authKeyID->Data;
if (!ake || ake->format != CSSM_X509_DATAFORMAT_PARSED) {
return result;
}
const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue;
if (!akid || !akid->keyIdentifierPresent) {
return result;
}
CFDataRef akData = CFDataCreate(kCFAllocatorDefault, akid->keyIdentifier.Data, akid->keyIdentifier.Length);
CFStringRef akString = CFDataCopyHexString(akData);
CFDictionaryRef allowList = InitializeAllowList();
if (NULL == allowList) {
goto errout;
}
allowedCerts = (CFArrayRef)CFDictionaryGetValue(allowList, akString);
if (!allowedCerts || !CFArrayGetCount(allowedCerts)) {
goto errout;
}
range = CFRangeMake(0, CFArrayGetCount(allowedCerts));
for (i = 0; i < numCerts; i++) {
TPCertInfo *cert = certGroup.certAtIndex(i);
UInt8 hashBytes[CC_SHA256_DIGEST_LENGTH] = {0};
const CSSM_DATA *certData = cert->itemData();
if (!certData || !certData->Data || (certData->Length <= 0)) {
goto errout;
}
int err = CCDigest(kCCDigestSHA256, certData->Data, certData->Length, hashBytes);
if (err) {
goto errout;
}
CFDataRef hashData = CFDataCreate(kCFAllocatorDefault, hashBytes, sizeof(hashBytes));
CFIndex position = CFArrayBSearchValues(allowedCerts, range, hashData, (CFComparatorFunction)CFDataCompare, NULL);
if (position < CFArrayGetCount(allowedCerts)) {
CFDataRef possibleMatch = (CFDataRef) CFArrayGetValueAtIndex(allowedCerts, position);
if (!CFDataCompare(hashData, possibleMatch)) {
result = CSSM_OK;
}
}
CFRelease(hashData);
}
errout:
if (akString)
CFRelease(akString);
if (akData)
CFRelease(akData);
if (allowList)
CFRelease(allowList);
return result;
}