#include "trust/trustd/SecTrustServer.h"
#include "trust/trustd/SecTrustStoreServer.h"
#include "trust/trustd/SecPolicyServer.h"
#include "trust/trustd/SecTrustLoggingServer.h"
#include "trust/trustd/SecCertificateSource.h"
#include "trust/trustd/SecRevocationServer.h"
#include "trust/trustd/SecCertificateServer.h"
#include "trust/trustd/SecPinningDb.h"
#include "trust/trustd/md.h"
#include <utilities/SecIOFormat.h>
#include <utilities/SecDispatchRelease.h>
#include <utilities/SecAppleAnchorPriv.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecTrustSettingsPriv.h>
#include <Security/SecTask.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 <stdatomic.h>
#include <limits.h>
#include <sys/codesign.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 <ipc/securityd_client.h>
#include <CommonCrypto/CommonDigest.h>
#include "OTATrustUtilities.h"
#include "personalization.h"
#include <utilities/SecInternalReleasePriv.h>
#include <mach/mach_time.h>
#include <dispatch/private.h>
#if TARGET_OS_OSX
#include <Security/SecTaskPriv.h>
#endif
#define MAX_CHAIN_LENGTH 15
#define MAX_NUM_CHAINS 100
#define ACCEPT_PATH_SCORE 10000000
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
struct SecPathBuilder {
dispatch_queue_t queue;
uint64_t startTime;
CFDataRef clientAuditToken;
SecCertificateSourceRef certificateSource;
SecCertificateSourceRef itemCertificateSource;
SecCertificateSourceRef anchorSource;
SecCertificateSourceRef appleAnchorSource;
CFMutableArrayRef anchorSources;
CFIndex nextParentSource;
CFMutableArrayRef parentSources;
CFArrayRef ocspResponses; CFArrayRef signedCertificateTimestamps; CFDictionaryRef trustedLogs; CFAbsoluteTime verifyTime;
CFArrayRef exceptions;
CFMutableSetRef allPaths;
CFMutableArrayRef partialPaths;
CFMutableArrayRef rejectedPaths;
CFMutableArrayRef candidatePaths;
CFIndex partialIX;
bool considerRejected;
bool considerPartials;
bool canAccessNetwork;
SecPVCRef * pvcs;
CFIndex pvcCount;
SecCertificatePathVCRef path;
_Atomic unsigned int asyncJobCount;
bool online_revocation;
bool trusted_revocation;
CFStringRef revocation_check_method;
SecCertificatePathVCRef bestPath;
CFMutableDictionaryRef info;
CFIndex activations;
bool (*state)(SecPathBuilderRef);
SecPathBuilderCompleted completed;
const void *context;
TrustAnalyticsBuilder * analyticsData;
};
static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder);
static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate, SecCertificateSourceRef *foundInSource);
static void SecPathBuilderInit(SecPathBuilderRef builder, dispatch_queue_t builderQueue,
CFDataRef clientAuditToken, CFArrayRef certificates,
CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed,
CFArrayRef policies, CFArrayRef ocspResponses,
CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions,
SecPathBuilderCompleted completed, const void *context) {
secdebug("alloc", "builder %p", builder);
CFAllocatorRef allocator = kCFAllocatorDefault;
builder->analyticsData = calloc(1, sizeof(TrustAnalyticsBuilder));
builder->analyticsData->start_time = mach_absolute_time();
builder->clientAuditToken = (CFDataRef)
((clientAuditToken) ? CFRetain(clientAuditToken) : NULL);
if (!builderQueue) {
builder->queue = dispatch_queue_create("com.apple.trustd.evaluation.builder", DISPATCH_QUEUE_SERIAL);
} else {
dispatch_retain_safe(builderQueue);
builder->queue = builderQueue;
}
builder->nextParentSource = 1;
#if !TARGET_OS_WATCH
builder->canAccessNetwork = true;
#endif
atomic_init(&builder->asyncJobCount, 0);
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->pvcs = malloc(sizeof(SecPVCRef));
builder->pvcs[0] = malloc(sizeof(struct OpaqueSecPVC));
SecPVCInit(builder->pvcs[0], builder, policies);
builder->pvcCount = 1;
builder->verifyTime = verifyTime;
builder->exceptions = CFRetainSafe(exceptions);
builder->certificateSource =
SecMemoryCertificateSourceCreate(certificates);
if (anchors) {
builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
}
bool allowNonProduction = false;
builder->appleAnchorSource = SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction));
#if !TARGET_OS_BRIDGE
CFArrayAppendValue(builder->parentSources, builder->certificateSource);
builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
if (keychainsAllowed) {
CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
#if TARGET_OS_OSX
if (kSecLegacyCertificateSource->contains && kSecLegacyCertificateSource->copyParents) {
CFArrayAppendValue(builder->parentSources, kSecLegacyCertificateSource);
}
#endif
}
if (anchorsOnly) {
CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
CFArrayAppendValue(builder->parentSources, kSecSystemAnchorSource);
CFArrayAppendValue(builder->parentSources, kSecUserAnchorSource);
}
if (keychainsAllowed && builder->canAccessNetwork) {
CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
}
#else
CFArrayAppendValue(builder->parentSources, builder->certificateSource);
if (anchorsOnly) {
CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
}
#endif
#if !TARGET_OS_BRIDGE
if (builder->anchorSource) {
CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
}
if (!anchorsOnly) {
CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
if (keychainsAllowed) {
#if TARGET_OS_OSX
if (kSecLegacyAnchorSource->contains && kSecLegacyAnchorSource->copyParents) {
CFArrayAppendValue(builder->anchorSources, kSecLegacyAnchorSource);
}
#endif
CFArrayAppendValue(builder->anchorSources, kSecUserAnchorSource);
}
CFArrayAppendValue(builder->anchorSources, kSecSystemAnchorSource);
}
#else
if (builder->anchorSource) {
CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
}
if (!anchorsOnly) {
CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
}
#endif
builder->ocspResponses = CFRetainSafe(ocspResponses);
builder->signedCertificateTimestamps = CFRetainSafe(signedCertificateTimestamps);
if(trustedLogs) {
builder->trustedLogs = SecOTAPKICreateTrustedCTLogsDictionaryFromArray(trustedLogs);
}
SecCertificateRef leaf =
(SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
SecCertificatePathVCRef path = SecCertificatePathVCCreate(NULL, leaf, NULL);
CFSetAddValue(builder->allPaths, path);
CFArrayAppendValue(builder->partialPaths, path);
builder->path = CFRetainSafe(path);
SecPathBuilderSetPath(builder, path);
CFRelease(path);
builder->state = SecPathBuilderProcessLeaf;
builder->completed = completed;
builder->context = context;
}
SecPathBuilderRef SecPathBuilderCreate(dispatch_queue_t builderQueue, CFDataRef clientAuditToken,
CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
bool keychainsAllowed, CFArrayRef policies, CFArrayRef ocspResponses,
CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions,
SecPathBuilderCompleted completed, const void *context) {
SecPathBuilderRef builder = malloc(sizeof(*builder));
memset(builder, 0, sizeof(*builder));
SecPathBuilderInit(builder, builderQueue, clientAuditToken, certificates,
anchors, anchorsOnly, keychainsAllowed, policies, ocspResponses,
signedCertificateTimestamps, trustedLogs, verifyTime,
accessGroups, exceptions, completed, context);
return builder;
}
static void SecPathBuilderForEachPVC(SecPathBuilderRef builder,void (^operation)(SecPVCRef pvc, bool *stop)) {
if (!builder->pvcs) { return; }
bool stop = false;
CFIndex ix;
for (ix = 0; ix < builder->pvcCount; ix++) {
if (!builder->pvcs[ix]) { continue; }
operation(builder->pvcs[ix], &stop);
if (stop) { break; }
}
}
void SecPathBuilderDestroy(SecPathBuilderRef builder) {
secdebug("alloc", "destroy builder %p", builder);
dispatch_release_null(builder->queue);
if (builder->anchorSource) {
SecMemoryCertificateSourceDestroy(builder->anchorSource);
builder->anchorSource = NULL;
}
if (builder->certificateSource) {
SecMemoryCertificateSourceDestroy(builder->certificateSource);
builder->certificateSource = NULL;
}
if (builder->itemCertificateSource) {
SecItemCertificateSourceDestroy(builder->itemCertificateSource);
builder->itemCertificateSource = NULL;
}
if (builder->appleAnchorSource) {
SecMemoryCertificateSourceDestroy(builder->appleAnchorSource);
builder->appleAnchorSource = NULL;
}
CFReleaseNull(builder->clientAuditToken);
CFReleaseNull(builder->anchorSources);
CFReleaseNull(builder->parentSources);
CFReleaseNull(builder->allPaths);
CFReleaseNull(builder->partialPaths);
CFReleaseNull(builder->rejectedPaths);
CFReleaseNull(builder->candidatePaths);
CFReleaseNull(builder->ocspResponses);
CFReleaseNull(builder->signedCertificateTimestamps);
CFReleaseNull(builder->trustedLogs);
CFReleaseNull(builder->path);
CFReleaseNull(builder->revocation_check_method);
CFReleaseNull(builder->info);
CFReleaseNull(builder->exceptions);
free(builder->analyticsData);
builder->analyticsData = NULL;
if (builder->pvcs) {
CFIndex ix;
for (ix = 0; ix < builder->pvcCount; ix++) {
if (builder->pvcs[ix]) {
SecPVCDelete(builder->pvcs[ix]);
free(builder->pvcs[ix]);
}
}
free(builder->pvcs);
builder->pvcs = NULL;
}
}
void SecPathBuilderSetPath(SecPathBuilderRef builder, SecCertificatePathVCRef path) {
bool samePath = ((!path && !builder->path) || (path && builder->path && CFEqual(path, builder->path)));
if (!samePath) {
CFRetainAssign(builder->path, path);
}
CFReleaseNull(builder->info);
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCSetPath(pvc, path);
});
}
bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
return builder->canAccessNetwork;
}
void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
if (builder->canAccessNetwork != allow) {
builder->canAccessNetwork = allow;
if (allow) {
#if !TARGET_OS_WATCH
secinfo("http", "network access re-enabled by policy");
CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
#else
secnotice("http", "network access not allowed on WatchOS");
builder->canAccessNetwork = false;
#endif
} else {
secinfo("http", "network access disabled by policy");
CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
kSecCAIssuerSource);
if (ix >= 0)
CFArrayRemoveValueAtIndex(builder->parentSources, ix);
}
}
}
CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
{
return CFRetainSafe(builder->ocspResponses);
}
CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
{
return CFRetainSafe(builder->signedCertificateTimestamps);
}
CFDictionaryRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
{
return CFRetainSafe(builder->trustedLogs);
}
SecCertificateSourceRef SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder)
{
return builder->anchorSource;
}
CFSetRef SecPathBuilderGetAllPaths(SecPathBuilderRef builder)
{
return builder->allPaths;
}
TrustAnalyticsBuilder *SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder)
{
if (!builder) {
return NULL;
}
return builder->analyticsData;
}
SecCertificatePathVCRef SecPathBuilderGetBestPath(SecPathBuilderRef builder)
{
return builder->bestPath;
}
SecCertificatePathVCRef SecPathBuilderGetPath(SecPathBuilderRef builder) {
return builder->path;
}
CFAbsoluteTime SecPathBuilderGetVerifyTime(SecPathBuilderRef builder) {
return builder->verifyTime;
}
bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder) {
__block bool validIntermediates = false;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
CFArrayForEach(pvc->policies, ^(const void *value) {
SecPolicyRef policy = (SecPolicyRef)value;
if (CFDictionaryContainsKey(policy->_options, kSecPolicyCheckTemporalValidity)) {
validIntermediates = true;
*stop = true;
}
});
});
return validIntermediates;
}
CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder) {
return SecCertificatePathVCGetCount(builder->path);
}
SecCertificateRef SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder, CFIndex ix) {
return SecCertificatePathVCGetCertificateAtIndex(builder->path, ix);
}
bool SecPathBuilderIsAnchored(SecPathBuilderRef builder) {
return SecCertificatePathVCIsAnchored(builder->path);
}
unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder) {
unsigned int result = atomic_fetch_sub(&builder->asyncJobCount, 1);
secdebug("rvc", "%p: decrement asyncJobCount from %d", builder, result);
return --result;
}
void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder, unsigned int jobCount) {
atomic_store(&builder->asyncJobCount, jobCount);
secdebug("rvc", "%p: set asyncJobCount to %d", builder, jobCount);
}
unsigned int SecPathBuilderGetAsyncJobCount(SecPathBuilderRef builder) {
unsigned int count = atomic_load(&builder->asyncJobCount);
secdebug("rvc", "%p: current asyncJobCount is %d", builder, count);
return count;
}
CFMutableDictionaryRef SecPathBuilderGetInfo(SecPathBuilderRef builder) {
return builder->info;
}
CFStringRef SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder) {
return builder->revocation_check_method;
}
void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder, CFStringRef method) {
CFRetainAssign(builder->revocation_check_method, method);
secdebug("rvc", "deferred revocation checking enabled using %@ method", method);
}
bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder) {
return builder->online_revocation;
}
void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder) {
builder->online_revocation = true;
secdebug("rvc", "revocation force online check");
}
bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder) {
return builder->trusted_revocation;
}
void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder) {
builder->trusted_revocation = true;
secdebug("rvc", "revocation check only if trusted");
}
CFArrayRef SecPathBuilderGetExceptions(SecPathBuilderRef builder) {
return builder->exceptions;
}
CFIndex SecPathBuilderGetPVCCount(SecPathBuilderRef builder) {
return builder->pvcCount;
}
SecPVCRef SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder, CFIndex ix) {
if (ix > (builder->pvcCount - 1)) {
return NULL;
}
return builder->pvcs[ix];
}
void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder, CFStringRef key,
CFIndex ix, CFTypeRef result, bool force) {
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCSetResultForced(pvc, key, ix, result, force);
});
}
static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder) {
__block bool acceptPath = false;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
acceptPath |= SecPVCIsOkResult(pvc);
});
return acceptPath;
}
SecPVCRef SecPathBuilderGetResultPVC(SecPathBuilderRef builder) {
__block SecPVCRef resultPVC = NULL;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
if (SecPVCIsOkResult(pvc)) {
resultPVC = pvc;
*stop = true;
}
});
if (resultPVC) { return resultPVC; }
return builder->pvcs[0];
}
static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder, SecCertificateSourceRef source,
SecCertificateRef certificate) {
__block bool result = false;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
if (SecPVCIsAnchorPerConstraints(pvc, source, certificate)) {
result = true;
*stop = true;
}
});
return result;
}
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate, SecCertificateSourceRef *foundInSource) {
CFIndex count = CFArrayGetCount(builder->anchorSources);
CFIndex ix;
for (ix = 0; ix < count; ++ix) {
SecCertificateSourceRef source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->anchorSources, ix);
if (SecCertificateSourceContains(source, certificate)) {
if (foundInSource)
*foundInSource = source;
if (SecPathBuilderIsAnchorPerConstraints(builder, source, certificate)) {
return true;
}
}
}
return false;
}
bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder, SecCertificateSourceRef source) {
CFIndex anchorCount = CFArrayGetCount(builder->anchorSources);
return CFArrayContainsValue(builder->anchorSources, CFRangeMake(0,anchorCount), source);
}
static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
SecCertificatePathVCRef path) {
SecPathBuilderSetPath(builder, path);
__block bool parentChecksFail = true;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
parentChecksFail &= !SecPVCParentCertificateChecks(pvc,
SecCertificatePathVCGetCount(path) - 1);
});
if (!builder->considerRejected && parentChecksFail) {
secdebug("trust", "Found rejected path %@", path);
CFArrayAppendValue(builder->rejectedPaths, path);
return false;
}
SecPathVerifyStatus vstatus = SecCertificatePathVCVerify(path);
if (vstatus == kSecPathVerifyFailed) {
secdebug("trust", "Verify failed for path %@", path);
return false;
}
if (vstatus == kSecPathVerifySuccess) {
if (SecCertificatePathVCIsAnchored(path)) {
secdebug("trust", "Adding candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
}
if (((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
(SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) ||
SecCertificatePathVCIsCycleInGraph(path)) {
if (!builder->considerRejected) {
secdebug("trust", "Adding non-partial non-anchored reject %@", path);
CFArrayAppendValue(builder->rejectedPaths, path);
} else {
secdebug("trust", "Adding non-partial non-anchored candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
}
return false;
}
}
return true;
}
static void addOptionsToPolicy(SecPolicyRef policy, CFDictionaryRef newOptions) {
__block CFMutableDictionaryRef oldOptions = CFDictionaryCreateMutableCopy(NULL, 0, policy->_options);
CFDictionaryForEach(newOptions, ^(const void *key, const void *value) {
CFDictionaryAddValue(oldOptions, key, value);
});
CFAssignRetained(policy->_options, oldOptions);
}
static CF_RETURNS_RETAINED CFArrayRef addTransparentConnectionsRules(CFArrayRef rules, CFNumberRef transparentConnection) {
int transparentConnectionValue = 0;
if (!transparentConnection || !CFNumberGetValue(transparentConnection, kCFNumberIntType, &transparentConnectionValue) || transparentConnectionValue != 1) {
return CFRetainSafe(rules);
}
CFArrayRef pins = _SecTrustStoreCopyTransparentConnectionPins(NULL, NULL);
if (!pins || CFArrayGetCount(pins) == 0) {
CFReleaseNull(pins);
return CFRetainSafe(rules);
}
CFMutableDictionaryRef transparentConnectionOptions = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableArrayRef caSPKIsha256s = CFArrayCreateMutable(NULL, CFArrayGetCount(pins), &kCFTypeArrayCallBacks);
CFArrayForEach(pins, ^(const void *value) {
CFDictionaryRef pin = (CFDictionaryRef)value;
CFStringRef hashAlgorithm = CFDictionaryGetValue(pin, kSecTrustStoreHashAlgorithmKey);
if (!hashAlgorithm || !isString(hashAlgorithm) ||
CFStringCompare(CFSTR("sha256"), hashAlgorithm, 0) != kCFCompareEqualTo) {
return;
}
CFDataRef hash = CFDictionaryGetValue(pin, kSecTrustStoreSPKIHashKey);
if (!hash || !isData(hash)) {
return;
}
CFArrayAppendValue(caSPKIsha256s, hash);
});
if (CFArrayGetCount(caSPKIsha256s) == 0) {
CFReleaseNull(caSPKIsha256s);
CFReleaseNull(transparentConnectionOptions);
CFReleaseNull(pins);
return CFRetainSafe(rules);
}
secnotice("SecPinningDb", "Adding %lu CA pins for Transparent Connection", CFArrayGetCount(caSPKIsha256s));
CFDictionaryAddValue(transparentConnectionOptions, kSecPolicyCheckCAspkiSHA256, caSPKIsha256s);
CFReleaseNull(caSPKIsha256s);
CFMutableArrayRef newRules = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(rules) + 1, rules);
CFArrayAppendValue(newRules, transparentConnectionOptions);
CFReleaseNull(transparentConnectionOptions);
CFReleaseNull(pins);
return newRules;
}
static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder) {
CFIndex ix, initialPVCCount = builder->pvcCount;
for (ix = 0; ix < initialPVCCount; ix++) {
CFArrayRef policies = CFRetainSafe(builder->pvcs[ix]->policies);
CFIndex policyIX;
for (policyIX = 0; policyIX < CFArrayGetCount(policies); policyIX++) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
CFStringRef policyName = SecPolicyGetName(policy);
CFStringRef hostname = CFDictionaryGetValue(policy->_options, kSecPolicyCheckSSLHostname);
if (!hostname) { continue; }
CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(query, kSecPinningDbKeyPolicyName, policyName);
CFDictionaryAddValue(query, kSecPinningDbKeyHostname, hostname);
CFDictionaryRef results = SecPinningDbCopyMatching(query);
CFReleaseNull(query);
if (!results) { continue; }
CFArrayRef newRules = CFDictionaryGetValue(results, kSecPinningDbKeyRules);
CFStringRef dbPolicyName = CFDictionaryGetValue(results, kSecPinningDbKeyPolicyName);
CFNumberRef transparentConnection = CFDictionaryGetValue(results, kSecPinningDbKeyTransparentConnection);
secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
(unsigned long)CFArrayGetCount(newRules), dbPolicyName, hostname, policyName);
newRules = addTransparentConnectionsRules(newRules, transparentConnection);
CFIndex newRulesIX;
for (newRulesIX = 0; newRulesIX < CFArrayGetCount(newRules); newRulesIX++) {
if (!isDictionary(CFArrayGetValueAtIndex(newRules, newRulesIX))) {
continue;
}
CFDictionaryRef newOptions = (CFDictionaryRef)CFArrayGetValueAtIndex(newRules, newRulesIX);
SecPolicyRef newPolicy = SecPolicyCreateSSL(true, hostname);
if (!newPolicy) { continue; }
addOptionsToPolicy(newPolicy, newOptions);
SecPolicySetName(newPolicy, dbPolicyName);
CFMutableArrayRef newPolicies = CFArrayCreateMutableCopy(NULL, 0, policies);
if (!newPolicies) { CFReleaseNull(newPolicy); continue; }
CFArrayReplaceValues(newPolicies, CFRangeMake(policyIX, 1), (const void **)&newPolicy, 1);
if (newRulesIX == 0) {
CFRetainAssign(builder->pvcs[ix]->policies, newPolicies);
} else {
builder->pvcs = realloc(builder->pvcs, (builder->pvcCount + 1) * sizeof(SecPVCRef));
builder->pvcs[builder->pvcCount] = malloc(sizeof(struct OpaqueSecPVC));
SecPVCInit(builder->pvcs[builder->pvcCount], builder, newPolicies);
builder->pvcCount++;
}
CFReleaseNull(newPolicy);
CFReleaseNull(newPolicies);
}
CFReleaseNull(results);
CFReleaseNull(newRules);
}
CFReleaseNull(policies);
}
}
static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder) {
SecPathBuilderAddPinningPolicies(builder);
SecCertificatePathVCRef path = builder->path;
SecCertificateRef leaf = SecCertificatePathVCGetCertificateAtIndex(path, 0);
SecCertificateSourceRef source = NULL;
bool isAnchor = false;
CFArrayRef constraints = NULL;
if (SecPathBuilderIsAnchor(builder, leaf, &source)) {
isAnchor = true;
}
if (source) {
constraints = SecCertificateSourceCopyUsageConstraints(source, leaf);
}
SecCertificatePathVCSetUsageConstraintsAtIndex(path, constraints, 0);
CFReleaseSafe(constraints);
if (isAnchor) {
SecCertificatePathVCSetIsAnchored(path);
CFArrayAppendValue(builder->candidatePaths, path);
}
__block bool leafChecksFail = true;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCLeafChecks(pvc);
leafChecksFail &= !SecPVCIsOkResult(pvc);
});
builder->considerRejected = leafChecksFail;
builder->state = SecPathBuilderGetNext;
return true;
}
static void SecPathBuilderProcessParents(SecPathBuilderRef builder,
SecCertificatePathVCRef partial, CFArrayRef parents) {
CFIndex rootIX = SecCertificatePathVCGetCount(partial) - 1;
CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
CFIndex parentIX;
for (parentIX = 0; parentIX < num_parents; ++parentIX) {
SecCertificateRef parent = (SecCertificateRef)
CFArrayGetValueAtIndex(parents, parentIX);
CFIndex ixOfParent = SecCertificatePathVCGetIndexOfCertificate(partial,
parent);
if (ixOfParent != kCFNotFound) {
if (ixOfParent == rootIX) {
SecCertificatePathVCSetSelfIssued(partial);
}
continue;
}
SecCertificateSourceRef source = NULL;
bool is_anchor = SecPathBuilderIsAnchor(builder, parent, &source);
CFArrayRef constraints = (source) ? SecCertificateSourceCopyUsageConstraints(source, parent) : NULL;
SecCertificatePathVCRef path = SecCertificatePathVCCreate(partial, parent, constraints);
CFReleaseSafe(constraints);
if (!path)
continue;
if (!CFSetContainsValue(builder->allPaths, path)) {
CFSetAddValue(builder->allPaths, path);
if (is_anchor)
SecCertificatePathVCSetIsAnchored(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;
SecCertificatePathVCRef partial = (SecCertificatePathVCRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
secdebug("async", "%@ parents %@", partial, parents);
SecPathBuilderProcessParents(builder, partial, parents);
builder->state = SecPathBuilderGetNext;
SecPathBuilderStep(builder);
}
static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
if (CFArrayGetCount(builder->candidatePaths)) {
SecCertificatePathVCRef path = (SecCertificatePathVCRef)
CFArrayGetValueAtIndex(builder->candidatePaths, 0);
CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
path);
SecPathBuilderSetPath(builder, path);
builder->state = SecPathBuilderValidatePath;
return true;
}
if (builder->considerRejected) {
CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
if (rejectedIX) {
rejectedIX--;
SecCertificatePathVCRef path = (SecCertificatePathVCRef)
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;
}
SecCertificatePathVCRef partial = (SecCertificatePathVCRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
if (builder->considerPartials) {
--builder->partialIX;
SecPathBuilderSetPath(builder, partial);
builder->state = SecPathBuilderValidatePath;
return true;
}
if (CFSetGetCount(builder->allPaths) > MAX_NUM_CHAINS) {
secnotice("trust", "not building any more paths, already have %" PRIdCFIndex,
CFSetGetCount(builder->allPaths));
builder->partialIX = -1;
return true;
}
secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
partial);
CFIndex sourceIX = SecCertificatePathVCGetNextSourceIndex(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);
}
SecCertificatePathVCSetNextSourceIndex(partial, sourceIX + 1);
SecCertificateRef root = SecCertificatePathVCGetRoot(partial);
return SecCertificateSourceCopyParents(source, root,
builder, SecPathBuilderExtendPaths);
} else {
--builder->partialIX;
}
return true;
}
static void SecPathBuilderReject(SecPathBuilderRef builder) {
check(builder);
builder->state = SecPathBuilderGetNext;
bool bestPathIsEV = SecCertificatePathVCIsEV(builder->bestPath);
bool isEV = SecCertificatePathVCIsEV(builder->path);
if (bestPathIsEV && !isEV) {
return;
}
CFIndex bestPathScore = SecCertificatePathVCGetScore(builder->bestPath);
CFIndex score = SecCertificatePathVCScore(builder->path, builder->verifyTime);
SecCertificatePathVCSetScore(builder->path, score);
if (isEV && !bestPathIsEV) {
bestPathScore = 0;
}
if (!builder->bestPath || score > bestPathScore) {
if (builder->bestPath) {
secinfo("reject",
"replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
(bestPathIsEV ? "" : "non "),
(bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
bestPathScore,
(isEV ? "" : "non "), (long)score, builder->path);
} else {
secinfo("reject", "%sev score: %" PRIdCFIndex " %@",
(isEV ? "" : "non "), score, builder->path);
}
builder->bestPath = builder->path;
} else {
secinfo("reject", "%sev score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
(isEV ? "" : "non "), score, bestPathScore, builder->path);
}
}
static void SecPathBuilderAccept(SecPathBuilderRef builder) {
if (!builder) { return; }
bool isSHA2 = !SecCertificatePathVCHasWeakHash(builder->path);
bool isOptionallySHA2 = !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder, 0));
bool isEV = SecCertificatePathVCIsEV(builder->path);
bool isOptionallyEV = SecCertificatePathVCIsOptionallyEV(builder->path);
CFIndex bestScore = SecCertificatePathVCGetScore(builder->bestPath);
CFIndex currScore = (SecCertificatePathVCScore(builder->path, builder->verifyTime) +
ACCEPT_PATH_SCORE + (isEV ? 1000000 : 0)); SecCertificatePathVCSetScore(builder->path, currScore);
if (currScore > bestScore) {
secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
(SecCertificatePathVCIsEV(builder->bestPath) ? "" : "non "),
(bestScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
bestScore,
(isEV ? "" : "non "), (long)currScore, builder->path);
builder->bestPath = builder->path;
}
if ((isEV || !isOptionallyEV) && (isSHA2 || !isOptionallySHA2))
builder->state = SecPathBuilderComputeDetails;
else
builder->state = SecPathBuilderGetNext;
}
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
if (builder->considerRejected) {
SecPathBuilderReject(builder);
return true;
}
builder->state = SecPathBuilderDidValidatePath;
bool completed = SecPathBuilderCheckRevocation(builder);
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCPathChecks(pvc);
});
return completed;
}
bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCPathCheckRevocationResponsesReceived(pvc);
});
if (SecPathBuilderIsOkResult(builder)) {
SecPathBuilderAccept(builder);
} else {
SecPathBuilderReject(builder);
}
assert(builder->state != SecPathBuilderDidValidatePath);
return true;
}
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
SecPathBuilderSetPath(builder, builder->bestPath);
__block CFIndex ix, pathLength = SecCertificatePathVCGetCount(builder->bestPath);
__block bool completed = true;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCComputeDetails(pvc, builder->bestPath);
completed &= SecPathBuilderCheckRevocation(builder);
for (ix = 1; ix < pathLength; ++ix) {
SecPVCParentCertificateChecks(pvc, ix);
}
SecPVCPathChecks(pvc);
});
builder->state = SecPathBuilderReportResult;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCPathCheckRevocationResponsesReceived(pvc);
});
if (SecCertificatePathVCGetScore(builder->bestPath) > ACCEPT_PATH_SCORE && !SecPathBuilderIsOkResult(builder)) {
SecCertificatePathVCResetScore(builder->bestPath);
secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
}
return completed;
}
static CFAbsoluteTime SecPathBuilderCalculateTrustNotBefore(SecPathBuilderRef builder) {
CFAbsoluteTime latestNotBefore = SecCertificatePathVCGetMaximumNotBefore(builder->bestPath);
if (SecCertificatePathVCIsRevocationDone(builder->bestPath)) {
CFAbsoluteTime thisUpdate = SecCertificatePathVCGetLatestThisUpdate(builder->bestPath);
if (thisUpdate > latestNotBefore) {
latestNotBefore = thisUpdate;
}
}
CFAbsoluteTime before_leeway = CFAbsoluteTimeGetCurrent() - TRUST_TIME_LEEWAY;
if (before_leeway > latestNotBefore) {
latestNotBefore = before_leeway;
}
return latestNotBefore;
}
static CFAbsoluteTime SecPathBuilderCalculateTrustNotAfter(SecPathBuilderRef builder) {
CFAbsoluteTime earliestNotAfter = SecCertificatePathVCGetMinimumNotAfter(builder->bestPath);
if (SecCertificatePathVCIsRevocationDone(builder->bestPath)) {
CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
if (nextUpdate != 0 && nextUpdate < earliestNotAfter) { earliestNotAfter = nextUpdate;
}
}
CFAbsoluteTime after_leeway = CFAbsoluteTimeGetCurrent() + TRUST_TIME_LEEWAY;
if (after_leeway < earliestNotAfter) {
earliestNotAfter = after_leeway;
}
return earliestNotAfter;
}
static void SecPathBuilderReportTrustValidityPeriod(SecPathBuilderRef builder) {
if (!builder->info) {
return;
}
CFAbsoluteTime resultNotBefore = SecPathBuilderCalculateTrustNotBefore(builder);
CFAbsoluteTime resultNotAfter = SecPathBuilderCalculateTrustNotAfter(builder);
if (resultNotBefore > resultNotAfter) {
if (CFAbsoluteTimeGetCurrent() < resultNotAfter) {
resultNotBefore = CFAbsoluteTimeGetCurrent();
} else if (CFAbsoluteTimeGetCurrent() < resultNotBefore) {
CFAbsoluteTime temp = resultNotBefore;
resultNotBefore = resultNotAfter;
resultNotAfter = temp;
} else {
resultNotAfter = CFAbsoluteTimeGetCurrent() + TRUST_TIME_LEEWAY;
}
}
if (resultNotAfter < CFAbsoluteTimeGetCurrent()) {
resultNotBefore = resultNotAfter;
resultNotAfter = CFAbsoluteTimeGetCurrent() + TRUST_TIME_LEEWAY;
}
CFDateRef notBeforeDate = CFDateCreate(NULL, resultNotBefore);
CFDateRef notAfterDate = CFDateCreate(NULL, resultNotAfter);
CFDictionarySetValue(builder->info, kSecTrustInfoResultNotBefore, notBeforeDate);
CFDictionarySetValue(builder->info, kSecTrustInfoResultNotAfter, notAfterDate);
CFReleaseNull(notBeforeDate);
CFReleaseNull(notAfterDate);
}
bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
builder->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
SecPathBuilderReportTrustValidityPeriod(builder);
if (builder->info && SecCertificatePathVCIsEV(builder->bestPath) && SecPathBuilderIsOkResult(builder)) {
#if !TARGET_OS_WATCH
if (SecCertificatePathVCIsRevocationDone(builder->bestPath))
#endif
{
#if !TARGET_OS_WATCH
CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
if (nextUpdate != 0)
#endif
{
CFDictionarySetValue(builder->info, kSecTrustInfoExtendedValidationKey,
kCFBooleanTrue);
CFDictionarySetValue(builder->info, kSecTrustExtendedValidation,
kCFBooleanTrue);
SecCertificateRef leaf = SecPathBuilderGetCertificateAtIndex(builder, 0);
CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
if (leafCompanyName) {
CFDictionarySetValue(builder->info, kSecTrustInfoCompanyNameKey,
leafCompanyName);
CFDictionarySetValue(builder->info, kSecTrustOrganizationName,
leafCompanyName);
CFRelease(leafCompanyName);
}
}
}
}
if (builder->info && SecPathBuilderIsOkResult(builder) && SecCertificatePathVCIsRevocationDone(builder->bestPath)) {
CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
if (nextUpdate != 0) {
CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
CFDictionarySetValue(builder->info, kSecTrustInfoRevocationValidUntilKey,
validUntil);
CFDictionarySetValue(builder->info, kSecTrustRevocationValidUntilDate,
validUntil);
CFRelease(validUntil);
CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
kCFBooleanTrue);
CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
kCFBooleanTrue);
} else if (SecCertificatePathVCIsEV(builder->bestPath)) {
CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
kCFBooleanFalse);
CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
kCFBooleanFalse);
} else if (SecCertificatePathVCRevocationCheckedAllCerts(builder->bestPath)) {
CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
kCFBooleanTrue);
CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
kCFBooleanTrue);
}
}
if (builder->info && !SecPathBuilderIsOkResult(builder) && SecCertificatePathVCIsRevocationDone(builder->bestPath)
&& SecCertificatePathVCGetRevocationReason(builder->bestPath)) {
CFNumberRef reason = SecCertificatePathVCGetRevocationReason(builder->bestPath);
CFDictionarySetValue(builder->info, kSecTrustRevocationReason, reason);
}
if (builder->info && SecCertificatePathVCIsCT(builder->bestPath) && SecPathBuilderIsOkResult(builder)) {
CFDictionarySetValue(builder->info, kSecTrustInfoCertificateTransparencyKey,
kCFBooleanTrue);
}
builder->state = NULL;
return false;
}
bool SecPathBuilderStep(SecPathBuilderRef builder) {
secdebug("async", "step builder %p", 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;
}
SecPVCRef pvc = SecPathBuilderGetResultPVC(builder);
SecTrustResultType result = pvc->result;
if (builder->exceptions && pvc->result == kSecTrustResultUnspecified) {
result = kSecTrustResultProceed;
}
secinfo("trust", "completed: %@ details: %@ result: %d",
builder->bestPath, pvc->details, result);
if (builder->completed) {
CFArrayRef resultPath = SecCertificatePathVCCopyCertificates(builder->bestPath);
CFDictionaryRef info = CFRetainSafe(builder->info);
CFArrayRef details = CFRetainSafe(pvc->details);
const void *context = builder->context;
SecPathBuilderCompleted completed = builder->completed;
secdebug("async", "free builder");
SecPathBuilderDestroy(builder);
free(builder);
secdebug("async", "returning to caller");
completed(context, resultPath, details, info, result);
CFReleaseNull(resultPath);
CFReleaseNull(info);
CFReleaseNull(details);
} else {
SecPathBuilderDestroy(builder);
free(builder);
}
return false;
}
dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
return (builder) ? builder->queue : NULL;
}
CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
}
typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error);
static void
SecTrustServerEvaluateCompleted(const void *userData,
CFArrayRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
TrustdHealthAnalyticsLogEvaluationCompleted();
evaluated(result, details, info, chain, NULL);
Block_release(evaluated);
}
void
SecTrustServerEvaluateBlock(dispatch_queue_t builderQueue, CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error)) {
if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
evaluated(kSecTrustResultInvalid, NULL, NULL, NULL, certError);
CFReleaseSafe(certError);
return;
}
SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
SecPathBuilderRef builder = SecPathBuilderCreate(builderQueue, clientAuditToken,
certificates, anchors,
anchorsOnly, keychainsAllowed, policies,
responses, SCTs, trustedLogs,
verifyTime, accessGroups, exceptions,
SecTrustServerEvaluateCompleted, userData);
SecPathBuilderStep(builder);
}
static CFDataRef SecTrustServerCopySelfAuditToken(void)
{
audit_token_t token;
kern_return_t kr;
mach_msg_type_number_t token_size = TASK_AUDIT_TOKEN_COUNT;
kr = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)&token, &token_size);
if (kr != KERN_SUCCESS) {
secwarning("failed to get audit token for ourselves");
return NULL;
}
return CFDataCreate(NULL, (uint8_t *)&token, sizeof(token));
}
SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef exceptions, CFArrayRef *pdetails, CFDictionaryRef *pinfo, CFArrayRef *pchain, CFErrorRef *perror) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
__block SecTrustResultType result = kSecTrustResultInvalid;
__block dispatch_queue_t queue = dispatch_queue_create("com.apple.trustd.evaluation.recursive", DISPATCH_QUEUE_SERIAL);
CFDataRef audit_token = SecTrustServerCopySelfAuditToken();
dispatch_async(queue, ^{
SecTrustServerEvaluateBlock(queue, audit_token, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, exceptions, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef 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);
dispatch_release(done);
dispatch_release_null(queue);
CFReleaseNull(audit_token);
return result;
}