#include <securityd/SecTrustServer.h>
#include <securityd/SecPolicyServer.h>
#include <securityd/SecTrustStoreServer.h>
#include <securityd/SecCAIssuerRequest.h>
#include <securityd/SecItemServer.h>
#include <utilities/SecIOFormat.h>
#include <utilities/SecDispatchRelease.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecCertificatePath.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFPropertyList.h>
#include <AssertMacros.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <Security/SecBase.h>
#include "SecRSAKey.h"
#include <libDER/oids.h>
#include <utilities/debugging.h>
#include <utilities/SecCFWrappers.h>
#include <Security/SecInternal.h>
#include "securityd_client.h"
#include <CommonCrypto/CommonDigest.h>
#include "OTATrustUtilities.h"
#ifndef SECITEM_SHIM_OSX
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;
}
#endif
#define MAX_CHAIN_LENGTH 15
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
typedef struct SecCertificateSource *SecCertificateSourceRef;
typedef void(*SecCertificateSourceParents)(void *, CFArrayRef);
typedef bool(*CopyParents)(SecCertificateSourceRef source,
SecCertificateRef certificate, void *context, SecCertificateSourceParents);
typedef bool(*Contains)(SecCertificateSourceRef source,
SecCertificateRef certificate);
struct SecCertificateSource {
CopyParents copyParents;
Contains contains;
};
static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source,
SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
return source->copyParents(source, certificate, context, callback);
}
static bool SecCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return source->contains(source, certificate);
}
struct SecItemCertificateSource {
struct SecCertificateSource base;
CFArrayRef accessGroups;
};
typedef struct SecItemCertificateSource *SecItemCertificateSourceRef;
static CFTypeRef SecItemCertificateSourceResultsPost(CFTypeRef raw_results) {
if (isArray(raw_results)) {
CFMutableArrayRef 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);
}
});
return result;
} else if (isData(raw_results)) {
return SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)raw_results);
}
return NULL;
}
static bool SecItemCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFDataRef normalizedIssuer =
SecCertificateGetNormalizedIssuerContent(certificate);
const void *keys[] = {
kSecClass,
kSecReturnData,
kSecMatchLimit,
kSecAttrSubject
},
*values[] = {
kSecClassCertificate,
kCFBooleanTrue,
kSecMatchLimitAll,
normalizedIssuer
};
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4,
NULL, NULL);
CFTypeRef results = NULL;
CFErrorRef localError = NULL;
if (!_SecItemCopyMatching(query, msource->accessGroups, &results, &localError)) {
if (CFErrorGetCode(localError) != errSecItemNotFound) {
secdebug("trust", "_SecItemCopyMatching: %@", localError);
}
CFRelease(localError);
}
CFRelease(query);
CFTypeRef certs = SecItemCertificateSourceResultsPost(results);
CFReleaseSafe(results);
callback(context, certs);
CFReleaseSafe(certs);
return true;
}
static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFDataRef normalizedSubject =
SecCertificateGetNormalizedSubjectContent(certificate);
CFDataRef serialNumber =
SecCertificateCopySerialNumber(certificate);
const void *keys[] = {
kSecClass,
kSecMatchLimit,
kSecAttrIssuer,
kSecAttrSerialNumber
},
*values[] = {
kSecClassCertificate,
kSecMatchLimitOne,
normalizedSubject,
serialNumber
};
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5,
NULL, NULL);
CFErrorRef localError = NULL;
CFTypeRef results = NULL;
bool ok = _SecItemCopyMatching(query, msource->accessGroups, &results, &localError);
CFRelease(query);
CFRelease(serialNumber);
CFReleaseSafe(results);
if (!ok) {
if (CFErrorGetCode(localError) != errSecItemNotFound) {
secdebug("trust", "_SecItemCopyMatching: %@", localError);
}
CFRelease(localError);
return false;
}
return true;
}
static SecCertificateSourceRef SecItemCertificateSourceCreate(CFArrayRef accessGroups) {
SecItemCertificateSourceRef result = (SecItemCertificateSourceRef)malloc(sizeof(*result));
result->base.copyParents = SecItemCertificateSourceCopyParents;
result->base.contains = SecItemCertificateSourceContains;
result->accessGroups = accessGroups;
CFRetainSafe(accessGroups);
return (SecCertificateSourceRef)result;
}
static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
CFReleaseSafe(msource->accessGroups);
free(msource);
}
static bool SecSystemAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
#ifndef SECITEM_SHIM_OSX
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);
#endif
return true;
}
static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
bool result = false;
#ifndef SECITEM_SHIM_OSX
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);
#endif
return result;
}
struct SecCertificateSource kSecSystemAnchorSource = {
SecSystemAnchorSourceCopyParents,
SecSystemAnchorSourceContains
};
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 bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return SecTrustStoreContains(
SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate);
}
struct SecCertificateSource kSecUserAnchorSource = {
SecUserAnchorSourceCopyParents,
SecUserAnchorSourceContains
};
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 = CFDictionaryGetValue(msource->subjects,
normalizedIssuer);
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);
}
static SecCertificateSourceRef SecMemoryCertificateSourceCreate(
CFArrayRef certificates) {
SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
malloc(sizeof(*result));
result->base.copyParents = SecMemoryCertificateSourceCopyParents;
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;
}
static 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,
SecCAIssuerCertificateSourceContains
};
struct SecPathBuilder {
dispatch_queue_t queue;
SecCertificateSourceRef certificateSource;
SecCertificateSourceRef itemCertificateSource;
SecCertificateSourceRef anchorSource;
CFMutableArrayRef anchorSources;
CFIndex nextParentSource;
CFMutableArrayRef parentSources;
CFMutableSetRef allPaths;
CFMutableArrayRef partialPaths;
CFMutableArrayRef rejectedPaths;
CFMutableArrayRef candidatePaths;
CFIndex partialIX;
CFArrayRef leafDetails;
CFIndex rejectScore;
bool considerRejected;
bool considerPartials;
bool canAccessNetwork;
struct OpaqueSecPVC path;
SecCertificatePathRef bestPath;
bool bestPathIsEV;
CFIndex activations;
bool (*state)(SecPathBuilderRef);
SecPathBuilderCompleted completed;
const void *context;
};
static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate);
static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
SecCertificatePathRef path) {
CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
builder->leafDetails = CFArrayCreate(kCFAllocatorDefault,
(const void **)&certDetail, 1, &kCFTypeArrayCallBacks);
CFRelease(certDetail);
SecPVCRef pvc = &builder->path;
SecPVCSetPath(pvc, path, builder->leafDetails);
builder->considerRejected = !SecPVCLeafChecks(pvc);
}
static void SecPathBuilderInit(SecPathBuilderRef builder,
CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
CFArrayRef policies, CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
SecPathBuilderCompleted completed, const void *context) {
secdebug("alloc", "%p", builder);
CFAllocatorRef allocator = kCFAllocatorDefault;
builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
builder->nextParentSource = 1;
builder->considerPartials = false;
builder->canAccessNetwork = true;
builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
builder->allPaths = CFSetCreateMutable(allocator, 0,
&kCFTypeSetCallBacks);
builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->partialIX = 0;
SecPVCInit(&builder->path, builder, policies, verifyTime);
builder->bestPath = NULL;
builder->bestPathIsEV = false;
builder->rejectScore = 0;
builder->certificateSource =
SecMemoryCertificateSourceCreate(certificates);
if (anchors)
builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
else
builder->anchorSource = NULL;
CFArrayAppendValue(builder->parentSources, builder->certificateSource);
if (builder->anchorSource) {
CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
}
builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
if (anchorsOnly) {
CFArrayAppendValue(builder->parentSources, &kSecSystemAnchorSource);
CFArrayAppendValue(builder->parentSources, &kSecUserAnchorSource);
} else {
CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
}
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
SecCertificateRef leaf =
(SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf);
CFSetAddValue(builder->allPaths, path);
CFArrayAppendValue(builder->partialPaths, path);
if (SecPathBuilderIsAnchor(builder, leaf)) {
SecCertificatePathSetIsAnchored(path);
CFArrayAppendValue(builder->candidatePaths, path);
}
SecPathBuilderLeafCertificateChecks(builder, path);
CFRelease(path);
builder->activations = 0;
builder->state = SecPathBuilderGetNext;
builder->completed = completed;
builder->context = context;
}
SecPathBuilderRef SecPathBuilderCreate(CFArrayRef certificates,
CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies,
CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
SecPathBuilderCompleted completed, const void *context) {
SecPathBuilderRef builder = malloc(sizeof(*builder));
SecPathBuilderInit(builder, certificates, anchors, anchorsOnly,
policies, verifyTime, accessGroups, completed, context);
return builder;
}
static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
secdebug("alloc", "%p", builder);
dispatch_release_null(builder->queue);
if (builder->anchorSource)
SecMemoryCertificateSourceDestroy(builder->anchorSource);
if (builder->certificateSource)
SecMemoryCertificateSourceDestroy(builder->certificateSource);
if (builder->itemCertificateSource)
SecItemCertificateSourceDestroy(builder->itemCertificateSource);
CFReleaseSafe(builder->anchorSources);
CFReleaseSafe(builder->parentSources);
CFReleaseSafe(builder->allPaths);
CFReleaseSafe(builder->partialPaths);
CFReleaseSafe(builder->rejectedPaths);
CFReleaseSafe(builder->candidatePaths);
CFReleaseSafe(builder->leafDetails);
SecPVCDelete(&builder->path);
}
bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
return builder->canAccessNetwork;
}
void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
if (builder->canAccessNetwork != allow) {
builder->canAccessNetwork = allow;
if (allow) {
secdebug("http", "network access re-enabled by policy");
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
} else {
secdebug("http", "network access disabled by policy");
CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
&kSecCAIssuerSource);
if (ix >= 0)
CFArrayRemoveValueAtIndex(builder->parentSources, ix);
}
}
}
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate) {
CFIndex count = CFArrayGetCount(builder->anchorSources);
CFIndex ix;
for (ix = 0; ix < count; ++ix) {
SecCertificateSourceRef source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->anchorSources, ix);
if (SecCertificateSourceContains(source, certificate)) {
return true;
}
}
return false;
}
static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
SecCertificatePathRef path) {
SecPVCRef pvc = &builder->path;
SecPVCSetPath(pvc, path, NULL);
if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
SecPVCGetCertificateCount(pvc) - 1)) {
secdebug("trust", "Found rejected path %@", path);
CFArrayAppendValue(builder->rejectedPaths, path);
return false;
}
SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
if (vstatus == kSecPathVerifyFailed) {
secdebug("trust", "Verify failed for path %@", path);
return false;
}
if (vstatus == kSecPathVerifySuccess) {
if (SecCertificatePathIsAnchored(path)) {
secdebug("trust", "Adding candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
return false;
}
}
return true;
}
static void SecPathBuilderProccessParents(SecPathBuilderRef builder,
SecCertificatePathRef partial, CFArrayRef parents) {
CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
CFIndex parentIX;
bool is_anchor = SecCertificatePathGetNextSourceIndex(partial) <=
CFArrayGetCount(builder->anchorSources);
secdebug("trust", "found %" PRIdCFIndex " candidate %s", num_parents,
(is_anchor ? "anchors" : "parents"));
for (parentIX = 0; parentIX < num_parents; ++parentIX) {
SecCertificateRef parent = (SecCertificateRef)
CFArrayGetValueAtIndex(parents, parentIX);
CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
parent);
if (ixOfParent != kCFNotFound) {
if (ixOfParent == rootIX) {
SecCertificatePathSetSelfIssued(partial);
}
continue;
}
SecCertificatePathRef path = SecCertificatePathCreate(partial, parent);
if (!path)
continue;
if (!CFSetContainsValue(builder->allPaths, path)) {
CFSetAddValue(builder->allPaths, path);
if (is_anchor)
SecCertificatePathSetIsAnchored(path);
if (SecPathBuilderIsPartial(builder, path)) {
CFArrayInsertValueAtIndex(builder->partialPaths,
++builder->partialIX, path);
secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
parentIX + 1, num_parents, path);
}
secdebug("trust", "found new path %@", path);
}
CFRelease(path);
}
}
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
SecPathBuilderRef builder = (SecPathBuilderRef)context;
SecCertificatePathRef partial = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
secdebug("async", "%@ parents %@", partial, parents);
SecPathBuilderProccessParents(builder, partial, parents);
builder->state = SecPathBuilderGetNext;
SecPathBuilderStep(builder);
}
static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
if (CFArrayGetCount(builder->candidatePaths)) {
SecCertificatePathRef path = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->candidatePaths, 0);
CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
path);
SecPVCSetPath(&builder->path, path, NULL);
builder->state = SecPathBuilderValidatePath;
return true;
}
if (builder->considerRejected) {
CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
if (rejectedIX) {
rejectedIX--;
SecCertificatePathRef path = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
if (SecPathBuilderIsPartial(builder, path)) {
CFArrayInsertValueAtIndex(builder->partialPaths,
++builder->partialIX, path);
}
CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
return true;
}
}
if (builder->partialIX < 0) {
CFIndex num_sources = CFArrayGetCount(builder->parentSources);
if (builder->nextParentSource < num_sources) {
builder->nextParentSource++;
secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
builder->nextParentSource, num_sources);
} else {
if (!builder->considerRejected) {
builder->considerRejected = true;
secdebug("trust", "considering rejected paths");
} else if (!builder->considerPartials) {
builder->considerPartials = true;
secdebug("trust", "considering partials");
} else {
builder->state = SecPathBuilderComputeDetails;
return true;
}
}
builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
return true;
}
SecCertificatePathRef partial = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
if (builder->considerPartials) {
--builder->partialIX;
SecPVCSetPath(&builder->path, partial, NULL);
builder->state = SecPathBuilderValidatePath;
return true;
}
secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
partial);
CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
if (sourceIX < num_anchor_sources + builder->nextParentSource) {
SecCertificateSourceRef source;
if (sourceIX < num_anchor_sources) {
source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
num_anchor_sources);
} else {
CFIndex parentIX = sourceIX - num_anchor_sources;
source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->parentSources, parentIX);
secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
builder->nextParentSource);
}
SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
SecCertificateRef root = SecCertificatePathGetRoot(partial);
return SecCertificateSourceCopyParents(source, root,
builder, SecPathBuilderExtendPaths);
} else {
--builder->partialIX;
}
return true;
}
static void SecPathBuilderReject(SecPathBuilderRef builder) {
check(builder);
SecPVCRef pvc = &builder->path;
builder->state = SecPathBuilderGetNext;
if (builder->bestPathIsEV && !pvc->is_ev) {
return;
}
CFIndex rejectScore = builder->rejectScore;
CFIndex score = SecCertificatePathScore(builder->path.path,
SecPVCGetVerifyTime(&builder->path));
if (pvc->is_ev && !builder->bestPathIsEV) {
rejectScore = 0;
}
#if 0
if (pvc->is_ev) {
builder->state = SecPathBuilderComputeDetails;
}
#endif
if (!builder->bestPath || score > rejectScore) {
if (builder->bestPath) {
secdebug("reject",
"replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex " %@",
(builder->bestPathIsEV ? "" : "non "),
(builder->rejectScore == INTPTR_MAX ? "accept" : "reject"),
builder->rejectScore,
(pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
} else {
secdebug("reject", "%sev reject score: %" PRIdCFIndex " %@",
(pvc->is_ev ? "" : "non "), score, builder->path.path);
}
builder->rejectScore = score;
builder->bestPath = pvc->path;
builder->bestPathIsEV = pvc->is_ev;
} else {
secdebug("reject", "%sev reject score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
(pvc->is_ev ? "" : "non "), score, rejectScore, builder->path.path);
}
}
static void SecPathBuilderAccept(SecPathBuilderRef builder) {
check(builder);
SecPVCRef pvc = &builder->path;
if (pvc->is_ev || !builder->bestPathIsEV) {
secdebug("accept", "replacing %sev accept with %sev %@",
(builder->bestPathIsEV ? "" : "non "),
(pvc->is_ev ? "" : "non "), builder->path.path);
builder->rejectScore = INTPTR_MAX;
builder->bestPathIsEV = pvc->is_ev;
builder->bestPath = pvc->path;
}
if (pvc->is_ev || !pvc->optionally_ev)
builder->state = SecPathBuilderComputeDetails;
else
builder->state = SecPathBuilderGetNext;
}
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (builder->considerRejected) {
SecPathBuilderReject(builder);
return true;
}
builder->state = SecPathBuilderDidValidatePath;
return SecPVCPathChecks(pvc);
}
static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (pvc->result) {
SecPathBuilderAccept(builder);
} else {
SecPathBuilderReject(builder);
}
assert(builder->state != SecPathBuilderDidValidatePath);
return true;
}
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
#if 0
if (!builder->caller_wants_details) {
SecPVCSetPath(pvc, builder->bestPath, NULL);
pvc->result = builder->rejectScore == INTPTR_MAX;
builder->state = SecPathBuilderReportResult;
return true;
}
#endif
CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
pathLength, builder->leafDetails);
SecPVCSetPath(pvc, builder->bestPath, details);
pvc->optionally_ev = builder->bestPathIsEV;
pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (ix = 1; ix < pathLength; ++ix) {
CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(details, certDetail);
CFRelease(certDetail);
SecPVCParentCertificateChecks(pvc, ix);
SecPVCGrayListedKeyChecks(pvc, ix);
SecPVCBlackListedKeyChecks(pvc, ix);
}
builder->state = SecPathBuilderReportResult;
bool completed = SecPVCPathChecks(pvc);
if (builder->rejectScore == INTPTR_MAX && !pvc->result) {
builder->rejectScore = 0;
}
return completed;
}
static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (pvc->info && pvc->is_ev && pvc->result) {
CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
kCFBooleanTrue);
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
if (leafCompanyName) {
CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
leafCompanyName);
CFRelease(leafCompanyName);
}
if (pvc->rvcs) {
CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
if (nextUpdate == 0) {
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
kCFBooleanFalse);
} else {
CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
validUntil);
CFRelease(validUntil);
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
kCFBooleanTrue);
}
}
}
builder->state = NULL;
return false;
}
bool SecPathBuilderStep(SecPathBuilderRef builder) {
if (builder->activations) {
secdebug("async", "activations: %lu returning true",
builder->activations);
return true;
}
secdebug("async", "activations: %lu", builder->activations);
builder->activations++;
while (builder->state && builder->state(builder));
--builder->activations;
if (builder->state) {
secdebug("async", "waiting for async reply, exiting");
return true;
}
if (builder->activations) {
return false;
}
SecTrustResultType result = (builder->rejectScore == INTPTR_MAX
? kSecTrustResultUnspecified : kSecTrustResultRecoverableTrustFailure);
secdebug("trust", "completed: %@ details: %@ result: %d",
builder->bestPath, builder->path.details, result);
if (builder->completed) {
builder->completed(builder->context, builder->bestPath,
builder->path.details, builder->path.info, result);
}
SecPathBuilderDestroy(builder);
free(builder);
return false;
}
dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
return builder->queue;
}
typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
static void
SecTrustServerEvaluateCompleted(const void *userData,
SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
evaluated(result, details, info, chain, NULL);
Block_release(evaluated);
}
void
SecTrustServerEvaluateBlock(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
SecPathBuilderRef builder = SecPathBuilderCreate(certificates, anchors,
anchorsOnly, policies,
verifyTime, accessGroups,
SecTrustServerEvaluateCompleted, userData);
dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
}
SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
__block SecTrustResultType result = kSecTrustResultInvalid;
SecTrustServerEvaluateBlock(certificates, anchors, anchorsOnly, policies, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
result = tr;
if (tr == kSecTrustResultInvalid) {
if (perror) {
*perror = error;
CFRetainSafe(error);
}
} else {
if (pdetails) {
*pdetails = details;
CFRetainSafe(details);
}
if (pinfo) {
*pinfo = info;
CFRetainSafe(info);
}
if (pchain) {
*pchain = chain;
CFRetainSafe(chain);
}
}
dispatch_semaphore_signal(done);
});
dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
return result;
}