SecCertificateSource.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <AssertMacros.h>
#include <CommonCrypto/CommonDigest.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecItem.h>
#include <Security/SecItemInternal.h>
#include <Security/SecTrustSettingsPriv.h>
#include <Security/SecPolicyInternal.h>
#include <utilities/debugging.h>
#include <utilities/SecCFWrappers.h>
#include <securityd/SecTrustServer.h>
#include <securityd/SecItemServer.h>
#include <securityd/SecTrustStoreServer.h>
#include <securityd/SecCAIssuerRequest.h>
#include "OTATrustUtilities.h"
#include "SecCertificateSource.h"
static CFArrayRef subject_to_anchors(CFDataRef nic);
static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets);
static CFArrayRef subject_to_anchors(CFDataRef nic)
{
CFArrayRef result = NULL;
if (NULL == nic)
{
return result;
}
SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiref)
{
return result;
}
CFDictionaryRef lookupTable = SecOTAPKICopyAnchorLookupTable(otapkiref);
CFRelease(otapkiref);
if (NULL == lookupTable)
{
return result;
}
unsigned char subject_digest[CC_SHA1_DIGEST_LENGTH];
memset(subject_digest, 0, CC_SHA1_DIGEST_LENGTH);
(void)CC_SHA1(CFDataGetBytePtr(nic), (CC_LONG)CFDataGetLength(nic), subject_digest);
CFDataRef sha1Digest = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subject_digest, CC_SHA1_DIGEST_LENGTH, kCFAllocatorNull);
result = (CFArrayRef)CFDictionaryGetValue(lookupTable, sha1Digest);
CFReleaseSafe(lookupTable);
CFReleaseSafe(sha1Digest);
return result;
}
static CFArrayRef CopyCertDataFromIndices(CFArrayRef offsets)
{
CFMutableArrayRef result = NULL;
SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiref)
{
return result;
}
const char* anchorTable = SecOTAPKIGetAnchorTable(otapkiref);
if (NULL == anchorTable)
{
CFReleaseSafe(otapkiref);
return result;
}
CFIndex num_offsets = CFArrayGetCount(offsets);
result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
for (CFIndex idx = 0; idx < num_offsets; idx++)
{
CFNumberRef offset = (CFNumberRef)CFArrayGetValueAtIndex(offsets, idx);
uint32_t offset_value = 0;
if (CFNumberGetValue(offset, kCFNumberSInt32Type, &offset_value))
{
char* pDataPtr = (char *)(anchorTable + offset_value);
pDataPtr += sizeof(uint32_t);
int32_t cert_data_length = *((int32_t * )pDataPtr);
pDataPtr += sizeof(uint32_t);
CFDataRef cert_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)pDataPtr,
cert_data_length, kCFAllocatorNull);
if (NULL != cert_data)
{
CFArrayAppendValue(result, cert_data);
CFReleaseSafe(cert_data);
}
}
}
CFReleaseSafe(otapkiref);
return result;
}
static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets)
{
CFMutableArrayRef result = NULL;
CFArrayRef cert_data_array = CopyCertDataFromIndices(offsets);
if (NULL != cert_data_array)
{
result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFIndex num_cert_datas = CFArrayGetCount(cert_data_array);
for (CFIndex idx = 0; idx < num_cert_datas; idx++)
{
CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_data_array, idx);
if (NULL != cert_data)
{
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_data);
if (NULL != cert)
{
CFArrayAppendValue(result, cert);
CFRelease(cert);
}
}
}
CFRelease(cert_data_array);
}
return result;
}
bool SecCertificateSourceCopyParents(SecCertificateSourceRef source,
SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
return source->copyParents(source, certificate, context, callback);
}
CFArrayRef SecCertificateSourceCopyUsageConstraints(SecCertificateSourceRef source,
SecCertificateRef certificate) {
if (source->copyUsageConstraints) {
return source->copyUsageConstraints(source, certificate);
} else {
return NULL;
}
}
bool SecCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return source->contains(source, certificate);
}
struct SecItemCertificateSource {
struct SecCertificateSource base;
CFArrayRef accessGroups;
};
typedef struct SecItemCertificateSource *SecItemCertificateSourceRef;
static CF_RETURNS_RETAINED CFArrayRef _Nullable SecItemCertificateSourceResultsPost(CFTypeRef raw_results) {
CFMutableArrayRef result = NULL;
if (isArray(raw_results)) {
result = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(raw_results), &kCFTypeArrayCallBacks);
CFArrayForEach(raw_results, ^(const void *value) {
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, value);
if (cert) {
CFArrayAppendValue(result, cert);
CFRelease(cert);
}
});
} else if (isData(raw_results)) {
result = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(raw_results), &kCFTypeArrayCallBacks);
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)raw_results);
if (cert) {
CFArrayAppendValue(result, cert);
CFRelease(cert);
}
}
return result;
}
static bool SecItemCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
CFErrorRef localError = NULL;
CFArrayRef results = SecItemCopyParentCertificates_ios(normalizedIssuer, msource->accessGroups, &localError);
if (!results) {
if (localError && (CFErrorGetCode(localError) != errSecItemNotFound)) {
secdebug("trust", "SecItemCopyParentCertificates_ios: %@", localError);
}
CFReleaseSafe(localError);
}
CFArrayRef certs = SecItemCertificateSourceResultsPost(results);
CFReleaseSafe(results);
callback(context, certs);
CFReleaseSafe(certs);
return true;
}
static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
CFRetainSafe(normalizedIssuer);
CFDataRef serialNumber =
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
SecCertificateCopySerialNumber(certificate, NULL);
#else
SecCertificateCopySerialNumber(certificate);
#endif
CFErrorRef localError = NULL;
bool result = SecItemCertificateExists(normalizedIssuer, serialNumber, msource->accessGroups, &localError);
if (localError) {
if (CFErrorGetCode(localError) != errSecItemNotFound) {
secdebug("trust", "SecItemCertificateExists_ios: %@", localError);
}
CFReleaseSafe(localError);
}
CFReleaseSafe(serialNumber);
CFReleaseSafe(normalizedIssuer);
return result;
}
SecCertificateSourceRef SecItemCertificateSourceCreate(CFArrayRef accessGroups) {
SecItemCertificateSourceRef result = (SecItemCertificateSourceRef)malloc(sizeof(*result));
result->base.copyParents = SecItemCertificateSourceCopyParents;
result->base.copyUsageConstraints = NULL;
result->base.contains = SecItemCertificateSourceContains;
result->accessGroups = accessGroups;
CFRetainSafe(accessGroups);
return (SecCertificateSourceRef)result;
}
void SecItemCertificateSourceDestroy(SecCertificateSourceRef source) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFReleaseSafe(msource->accessGroups);
free(msource);
}
static bool SecSystemAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFArrayRef parents = NULL;
CFArrayRef anchors = NULL;
SecOTAPKIRef otapkiref = NULL;
CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate);
assert((unsigned long)CFDataGetLength(nic)<UINT_MAX);
otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
require_quiet(otapkiref, errOut);
anchors = subject_to_anchors(nic);
require_quiet(anchors, errOut);
parents = CopyCertsFromIndices(anchors);
errOut:
callback(context, parents);
CFReleaseSafe(parents);
CFReleaseSafe(otapkiref);
return true;
}
static CFArrayRef SecSystemAnchorSourceCopyUsageConstraints(SecCertificateSourceRef __unused source,
SecCertificateRef __unused certificate)
{
CFMutableArrayRef result = NULL;
CFMutableDictionaryRef options = NULL, strengthConstraints = NULL, trustRoot = NULL;
CFNumberRef trustResult = NULL;
require_quiet(options = CFDictionaryCreateMutable(NULL, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks),
out);
require_quiet(strengthConstraints = CFDictionaryCreateMutable(NULL, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks),
out);
require_quiet(trustRoot = CFDictionaryCreateMutable(NULL, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks),
out);
uint32_t temp = kSecTrustSettingsResultTrustRoot;
require_quiet(trustResult = CFNumberCreate(NULL, kCFNumberSInt32Type, &temp), out);
CFDictionaryAddValue(trustRoot, kSecTrustSettingsResult, trustResult);
CFDictionaryAddValue(options, kSecPolicyCheckSystemTrustedWeakHash, kCFBooleanTrue);
CFDictionaryAddValue(options, kSecPolicyCheckSystemTrustedWeakKey, kCFBooleanTrue);
CFDictionaryAddValue(strengthConstraints, kSecTrustSettingsPolicyOptions, options);
require_quiet(result = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks), out);
CFArrayAppendValue(result, strengthConstraints);
CFArrayAppendValue(result, trustRoot);
out:
CFReleaseNull(options);
CFReleaseNull(trustResult);
CFReleaseNull(trustRoot);
CFReleaseNull(strengthConstraints);
return result;
}
static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
bool result = false;
CFArrayRef anchors = NULL;
SecOTAPKIRef otapkiref = NULL;
CFArrayRef cert_datas = NULL;
CFDataRef nic = SecCertificateGetNormalizedSubjectContent(certificate);
assert((unsigned long)CFDataGetLength(nic)<UINT_MAX);
otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
require_quiet(otapkiref, errOut);
anchors = subject_to_anchors(nic);
require_quiet(anchors, errOut);
cert_datas = CopyCertDataFromIndices(anchors);
require_quiet(cert_datas, errOut);
CFIndex cert_length = SecCertificateGetLength(certificate);
const UInt8 *cert_data_ptr = SecCertificateGetBytePtr(certificate);
CFIndex num_cert_datas = CFArrayGetCount(cert_datas);
for (CFIndex idx = 0; idx < num_cert_datas; idx++)
{
CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_datas, idx);
if (NULL != cert_data)
{
if (CFGetTypeID(cert_data) == CFDataGetTypeID())
{
CFIndex aCert_Length = CFDataGetLength(cert_data);
const UInt8* aCert_Data_Ptr = CFDataGetBytePtr(cert_data);
if (aCert_Length == cert_length)
{
if (!memcmp(cert_data_ptr, aCert_Data_Ptr, cert_length))
{
result = true;
break;
}
}
}
}
}
errOut:
CFReleaseSafe(cert_datas);
CFReleaseSafe(otapkiref);
return result;
}
struct SecCertificateSource _kSecSystemAnchorSource = {
SecSystemAnchorSourceCopyParents,
SecSystemAnchorSourceCopyUsageConstraints,
SecSystemAnchorSourceContains
};
const SecCertificateSourceRef kSecSystemAnchorSource = &_kSecSystemAnchorSource;
#if TARGET_OS_IPHONE
static bool SecUserAnchorSourceCopyParents(SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFArrayRef parents = SecTrustStoreCopyParents(SecTrustStoreForDomain(kSecTrustStoreDomainUser),
certificate, NULL);
callback(context, parents);
CFReleaseSafe(parents);
return true;
}
static CFArrayRef SecUserAnchorSourceCopyUsageConstraints(SecCertificateSourceRef source,
SecCertificateRef certificate) {
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
if (!digest)
return NULL;
CFArrayRef usageConstraints = NULL;
bool ok = _SecTrustStoreCopyUsageConstraints(SecTrustStoreForDomain(kSecTrustStoreDomainUser),
digest, &usageConstraints, NULL);
if (ok) {
return usageConstraints;
} else {
CFReleaseNull(usageConstraints);
return NULL;
}
}
static bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return SecTrustStoreContains(SecTrustStoreForDomain(kSecTrustStoreDomainUser),
certificate);
}
struct SecCertificateSource _kSecUserAnchorSource = {
SecUserAnchorSourceCopyParents,
SecUserAnchorSourceCopyUsageConstraints,
SecUserAnchorSourceContains
};
const SecCertificateSourceRef kSecUserAnchorSource = &_kSecUserAnchorSource;
#endif
struct SecMemoryCertificateSource {
struct SecCertificateSource base;
CFMutableSetRef certificates;
CFMutableDictionaryRef subjects;
};
typedef struct SecMemoryCertificateSource *SecMemoryCertificateSourceRef;
static bool SecMemoryCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
CFDataRef normalizedIssuer =
SecCertificateGetNormalizedIssuerContent(certificate);
CFArrayRef parents = (normalizedIssuer) ? CFDictionaryGetValue(msource->subjects,
normalizedIssuer) : NULL;
secdebug("trust", "%@ parents -> %@", certificate, parents);
callback(context, parents);
return true;
}
static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
return CFSetContainsValue(msource->certificates, certificate);
}
static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict,
const void *key, const void *value) {
if (!key)
return;
CFMutableArrayRef values =
(CFMutableArrayRef)CFDictionaryGetValue(dict, key);
if (!values) {
values = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
CFDictionaryAddValue(dict, key, values);
CFRelease(values);
}
if (values)
CFArrayAppendValue(values, value);
}
static void SecMemoryCertificateSourceApplierFunction(const void *value,
void *context) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)context;
SecCertificateRef certificate = (SecCertificateRef)value;
if (CFSetContainsValue(msource->certificates, certificate))
return;
CFSetAddValue(msource->certificates, certificate);
CFDataRef key = SecCertificateGetNormalizedSubjectContent(certificate);
dictAddValueToArrayForKey(msource->subjects, key, value);
}
SecCertificateSourceRef SecMemoryCertificateSourceCreate(CFArrayRef certificates) {
SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
malloc(sizeof(*result));
result->base.copyParents = SecMemoryCertificateSourceCopyParents;
result->base.copyUsageConstraints = NULL;
result->base.contains = SecMemoryCertificateSourceContains;
CFIndex count = CFArrayGetCount(certificates);
result->certificates = CFSetCreateMutable(kCFAllocatorDefault, count,
&kCFTypeSetCallBacks);
result->subjects = CFDictionaryCreateMutable(kCFAllocatorDefault,
count, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRange range = { 0, count };
CFArrayApplyFunction(certificates, range,
SecMemoryCertificateSourceApplierFunction, result);
return (SecCertificateSourceRef)result;
}
void SecMemoryCertificateSourceDestroy(SecCertificateSourceRef source) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
CFRelease(msource->certificates);
CFRelease(msource->subjects);
free(msource);
}
static bool SecCAIssuerCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
return SecCAIssuerCopyParents(certificate, SecPathBuilderGetQueue((SecPathBuilderRef)context), context, callback);
}
static bool SecCAIssuerCertificateSourceContains(
SecCertificateSourceRef source, SecCertificateRef certificate) {
return false;
}
struct SecCertificateSource _kSecCAIssuerSource = {
SecCAIssuerCertificateSourceCopyParents,
NULL,
SecCAIssuerCertificateSourceContains
};
const SecCertificateSourceRef kSecCAIssuerSource = &_kSecCAIssuerSource;
#if TARGET_OS_OSX
#include <Security/SecItemPriv.h>
static bool SecLegacyCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFArrayRef parents = SecItemCopyParentCertificates_osx(certificate, NULL);
callback(context, parents);
CFReleaseSafe(parents);
return true;
}
static bool SecLegacyCertificateSourceContains(
SecCertificateSourceRef source, SecCertificateRef certificate) {
SecCertificateRef cert = SecItemCopyStoredCertificate(certificate, NULL);
bool result = (cert) ? true : false;
CFReleaseSafe(cert);
return result;
}
struct SecCertificateSource _kSecLegacyCertificateSource = {
SecLegacyCertificateSourceCopyParents,
NULL,
SecLegacyCertificateSourceContains
};
const SecCertificateSourceRef kSecLegacyCertificateSource = &_kSecLegacyCertificateSource;
#endif
#if TARGET_OS_OSX
static bool SecLegacyAnchorSourceCopyParents(SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFMutableArrayRef anchors = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFArrayRef parents = SecItemCopyParentCertificates_osx(certificate, NULL);
CFArrayRef trusted = NULL;
if (parents == NULL) {
goto finish;
}
OSStatus status = SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted);
if (status == errSecSuccess && trusted) {
CFIndex index, count = CFArrayGetCount(parents);
for (index = 0; index < count; index++) {
SecCertificateRef parent = (SecCertificateRef)CFArrayGetValueAtIndex(parents, index);
if (parent && CFArrayContainsValue(trusted, CFRangeMake(0, CFArrayGetCount(trusted)), parent)) {
CFArrayAppendValue(anchors, parent);
}
}
}
finish:
callback(context, anchors);
CFReleaseSafe(anchors);
CFReleaseSafe(parents);
CFReleaseSafe(trusted);
return true;
}
static CFArrayRef SecLegacyAnchorSourceCopyUsageConstraints(SecCertificateSourceRef source,
SecCertificateRef certificate) {
CFArrayRef result = NULL;
CFArrayRef userTrustSettings = NULL, adminTrustSettings = NULL;
OSStatus status = SecTrustSettingsCopyTrustSettings(certificate,
kSecTrustSettingsDomainUser,
&userTrustSettings);
if ((status == errSecSuccess) && (userTrustSettings != NULL)) {
result = CFRetain(userTrustSettings);
}
status = SecTrustSettingsCopyTrustSettings(certificate,
kSecTrustSettingsDomainAdmin,
&adminTrustSettings);
if ((status == errSecSuccess) && (adminTrustSettings != NULL) && (result == NULL)) {
result = CFRetain(adminTrustSettings);
}
CFReleaseNull(userTrustSettings);
CFReleaseNull(adminTrustSettings);
return result;
}
static bool SecLegacyAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
if (certificate == NULL) {
return false;
}
CFArrayRef trusted = NULL;
bool result = false;
OSStatus status = SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted);
if ((status == errSecSuccess) && (trusted != NULL)) {
CFIndex index, count = CFArrayGetCount(trusted);
for (index = 0; index < count; index++) {
SecCertificateRef anchor = (SecCertificateRef)CFArrayGetValueAtIndex(trusted, index);
if (anchor && CFEqual(anchor, certificate)) {
result = true;
break;
}
}
}
CFReleaseSafe(trusted);
return result;
}
struct SecCertificateSource _kSecLegacyAnchorSource = {
SecLegacyAnchorSourceCopyParents,
SecLegacyAnchorSourceCopyUsageConstraints,
SecLegacyAnchorSourceContains
};
const SecCertificateSourceRef kSecLegacyAnchorSource = &_kSecLegacyAnchorSource;
#endif