#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 <pthread.h>
#include <MacErrors.h>
#include "SecRSAKey.h"
#include <libDER/oids.h>
#include <security_utilities/debugging.h>
#include <Security/SecInternal.h>
#include <ipc/securityd_client.h>
#include "securityd_server.h"
CFStringRef kSecTrustInfoExtendedValidationKey = CFSTR("ExtendedValidation");
CFStringRef kSecTrustInfoCompanyNameKey = CFSTR("CompanyName");
CFStringRef kSecTrustInfoRevocationKey = CFSTR("Revocation");
CFStringRef kSecTrustInfoRevocationValidUntilKey =
CFSTR("RevocationValidUntil");
#pragma mark -
#pragma mark SecTrust
struct __SecTrust {
CFRuntimeBase _base;
CFArrayRef _certificates;
CFArrayRef _anchors;
CFTypeRef _policies;
CFDateRef _verifyDate;
SecCertificatePathRef _chain;
SecKeyRef _publicKey;
CFArrayRef _details;
CFDictionaryRef _info;
CFArrayRef _exceptions;
bool _anchorsOnly;
};
static pthread_once_t kSecTrustRegisterClass = PTHREAD_ONCE_INIT;
static CFTypeID kSecTrustTypeID = _kCFRuntimeNotATypeID;
static CFStringRef SecTrustDescribe(CFTypeRef cf);
static void SecTrustDestroy(CFTypeRef cf);
static CFStringRef SecTrustDescribe(CFTypeRef cf) {
SecTrustRef certificate = (SecTrustRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<SecTrustRef: %p>"), certificate);
}
static void SecTrustDestroy(CFTypeRef cf) {
SecTrustRef trust = (SecTrustRef)cf;
CFReleaseSafe(trust->_certificates);
CFReleaseSafe(trust->_policies);
CFReleaseSafe(trust->_verifyDate);
CFReleaseSafe(trust->_anchors);
CFReleaseSafe(trust->_chain);
CFReleaseSafe(trust->_publicKey);
CFReleaseSafe(trust->_details);
CFReleaseSafe(trust->_info);
CFReleaseSafe(trust->_exceptions);
}
static void SecTrustRegisterClass(void) {
static const CFRuntimeClass kSecTrustClass = {
0,
"SecTrust",
NULL,
NULL,
SecTrustDestroy,
NULL,
NULL,
NULL,
SecTrustDescribe
};
kSecTrustTypeID = _CFRuntimeRegisterClass(&kSecTrustClass);
}
CFTypeID SecTrustGetTypeID(void) {
pthread_once(&kSecTrustRegisterClass, SecTrustRegisterClass);
return kSecTrustTypeID;
}
OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates,
CFTypeRef policies, SecTrustRef *trustRef) {
OSStatus status = errSecParam;
CFAllocatorRef allocator = kCFAllocatorDefault;
CFArrayRef l_certs = NULL, l_policies = NULL;
SecTrustRef result = NULL;
check(certificates);
check(trustRef);
CFTypeID certType = CFGetTypeID(certificates);
if (certType == CFArrayGetTypeID()) {
require_quiet(CFArrayGetCount(certificates) > 0, errOut);
l_certs = CFArrayCreateCopy(allocator, certificates);
} else if (certType == SecCertificateGetTypeID()) {
l_certs = CFArrayCreate(allocator, &certificates, 1,
&kCFTypeArrayCallBacks);
} else {
goto errOut;
}
if (!l_certs) {
status = errSecAllocate;
goto errOut;
}
if (!policies) {
CFTypeRef policy = SecPolicyCreateBasicX509();
l_policies = CFArrayCreate(allocator, &policy, 1,
&kCFTypeArrayCallBacks);
CFRelease(policy);
} else if (CFGetTypeID(policies) == CFArrayGetTypeID()) {
l_policies = CFArrayCreateCopy(allocator, policies);
} else if (CFGetTypeID(policies) == SecPolicyGetTypeID()) {
l_policies = CFArrayCreate(allocator, &policies, 1,
&kCFTypeArrayCallBacks);
} else {
goto errOut;
}
if (!l_policies) {
status = errSecAllocate;
goto errOut;
}
CFIndex size = sizeof(struct __SecTrust);
require_quiet(result = (SecTrustRef)_CFRuntimeCreateInstance(allocator,
SecTrustGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
status = noErr;
errOut:
if (status) {
CFReleaseSafe(result);
CFReleaseSafe(l_certs);
CFReleaseSafe(l_policies);
} else {
result->_certificates = l_certs;
result->_policies = l_policies;
*trustRef = result;
}
return status;
}
OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust,
Boolean anchorCertificatesOnly) {
check(trust);
trust->_anchorsOnly = anchorCertificatesOnly;
return noErr;
}
OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust,
CFArrayRef anchorCertificates) {
check(trust);
check(anchorCertificates);
CFRetain(anchorCertificates);
if (trust->_anchors)
CFRelease(trust->_anchors);
trust->_anchors = anchorCertificates;
trust->_anchorsOnly = true;
return noErr;
}
OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate) {
check(trust);
check(verifyDate);
CFRetain(verifyDate);
if (trust->_verifyDate)
CFRelease(trust->_verifyDate);
trust->_verifyDate = verifyDate;
return noErr;
}
CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust) {
CFAbsoluteTime verifyTime;
if (trust->_verifyDate) {
verifyTime = CFDateGetAbsoluteTime(trust->_verifyDate);
} else {
verifyTime = CFAbsoluteTimeGetCurrent();
trust->_verifyDate = CFDateCreate(CFGetAllocator(trust), verifyTime);
}
return verifyTime;
}
CFArrayRef SecTrustGetDetails(SecTrustRef trust) {
return trust->_details;
}
static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest");
static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix) {
if (!trust->_exceptions || ix >= CFArrayGetCount(trust->_exceptions))
return NULL;
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_exceptions, ix);
if (CFGetTypeID(exception) != CFDictionaryGetTypeID())
return NULL;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
if (!certificate)
return NULL;
CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate);
CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest);
if (!digestValue || !CFEqual(sha1Digest, digestValue))
exception = NULL;
return exception;
}
struct SecTrustCheckExceptionContext {
CFDictionaryRef exception;
bool exceptionNotFound;
};
static void SecTrustCheckException(const void *key, const void *value, void *context) {
struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context;
if (cec->exception) {
CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key);
if (!exceptionValue || !CFEqual(value, exceptionValue)) {
cec->exceptionNotFound = true;
}
} else {
cec->exceptionNotFound = true;
}
}
OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
CFMutableDictionaryRef args_in = NULL;
CFTypeRef args_out = NULL;
OSStatus status;
check(trust);
check(result);
CFReleaseNull(trust->_chain);
CFReleaseNull(trust->_details);
CFReleaseNull(trust->_info);
args_in = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFArrayRef certificates = SecCertificateArrayCopyDataArray(trust->_certificates);
CFDictionaryAddValue(args_in, kSecTrustCertificatesKey, certificates);
CFRelease(certificates);
if (trust->_anchors) {
CFArrayRef anchors = SecCertificateArrayCopyDataArray(trust->_anchors);
CFDictionaryAddValue(args_in, kSecTrustAnchorsKey, anchors);
CFRelease(anchors);
}
if (trust->_anchorsOnly)
CFDictionaryAddValue(args_in, kSecTrustAnchorsOnlyKey, kCFBooleanTrue);
CFArrayRef serializedPolicies = SecPolicyArraySerialize(trust->_policies);
CFDictionaryAddValue(args_in, kSecTrustPoliciesKey, serializedPolicies);
CFRelease(serializedPolicies);
SecTrustGetVerifyTime(trust);
CFDictionaryAddValue(args_in, kSecTrustVerifyDateKey, trust->_verifyDate);
if (gSecurityd) {
status = gSecurityd->sec_trust_evaluate(args_in, &args_out);
} else {
status = ServerCommandSendReceive(sec_trust_evaluate_id,
args_in, &args_out);
}
if (status == errSecNotAvailable && CFArrayGetCount(trust->_certificates)) {
trust->_chain = SecCertificatePathCreate(NULL,
(SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0));
if (result)
*result = kSecTrustResultOtherError;
status = noErr;
goto errOut;
}
require_quiet(args_out, errOut);
CFArrayRef details = (CFArrayRef)CFDictionaryGetValue(args_out, kSecTrustDetailsKey);
if (details) {
require(CFGetTypeID(details) == CFArrayGetTypeID(), errOut);
CFRetain(details);
trust->_details = details;
}
CFDictionaryRef info = (CFDictionaryRef)CFDictionaryGetValue(args_out, kSecTrustInfoKey);
if (info) {
require(CFGetTypeID(info) == CFDictionaryGetTypeID(), errOut);
CFRetain(info);
trust->_info = info;
}
CFArrayRef chainArray = (CFArrayRef)CFDictionaryGetValue(args_out, kSecTrustChainKey);
require(chainArray && CFGetTypeID(chainArray) == CFArrayGetTypeID(), errOut);
trust->_chain = SecCertificatePathCreateWithArray(chainArray);
require(trust->_chain, errOut);
CFNumberRef cfResult = (CFNumberRef)CFDictionaryGetValue(args_out, kSecTrustResultKey);
require(cfResult && CFGetTypeID(cfResult) == CFNumberGetTypeID(), errOut);
if (result) {
SInt32 trustResult;
CFNumberGetValue(cfResult, kCFNumberSInt32Type, &trustResult);
if (trustResult == kSecTrustResultUnspecified) {
if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
trustResult = kSecTrustResultProceed;
} else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
CFIndex pathLength = CFArrayGetCount(details);
struct SecTrustCheckExceptionContext context = {};
CFIndex ix;
for (ix = 0; ix < pathLength; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf))
trustResult = kSecTrustResultFatalTrustFailure;
context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix);
CFDictionaryApplyFunction(detail, SecTrustCheckException, &context);
if (context.exceptionNotFound) {
break;
}
}
if (!context.exceptionNotFound)
trustResult = kSecTrustResultProceed;
}
*result = trustResult;
}
errOut:
CFReleaseSafe(args_out);
CFReleaseSafe(args_in);
return status;
}
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) {
if (!trust->_publicKey && trust->_chain) {
trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(
trust->_chain, 0);
}
if (trust->_publicKey)
CFRetain(trust->_publicKey);
return trust->_publicKey;
}
CFIndex SecTrustGetCertificateCount(SecTrustRef trust) {
return trust->_chain ? SecCertificatePathGetCount(trust->_chain) : 1;
}
SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust,
CFIndex ix) {
if (trust->_chain)
return SecCertificatePathGetCertificateAtIndex(trust->_chain, ix);
else if (ix == 0)
return (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0);
else
return NULL;
}
CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust) {
CFDictionaryRef info = trust->_info;
if (info)
CFRetain(info);
return info;
}
CFDataRef SecTrustCopyExceptions(SecTrustRef trust) {
CFArrayRef details = SecTrustGetDetails(trust);
CFIndex pathLength = CFArrayGetCount(details);
CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault, pathLength, &kCFTypeArrayCallBacks);
CFIndex ix;
for (ix = 0; ix < pathLength; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
CFIndex detailCount = CFDictionaryGetCount(detail);
CFMutableDictionaryRef exception;
if (ix == 0 || detailCount > 0) {
exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, detailCount + 1, detail);
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest);
} else {
exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
CFArrayAppendValue(exceptions, exception);
CFRelease(exception);
}
for (ix = pathLength - 1; ix > 0; --ix) {
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
if (CFDictionaryGetCount(exception) == 0) {
CFArrayRemoveValueAtIndex(exceptions, ix);
} else {
break;
}
}
CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
CFRelease(exceptions);
return encodedExceptions;
}
bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions) {
CFArrayRef exceptions;
exceptions = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, encodedExceptions, kCFPropertyListImmutable, NULL);
if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
CFRelease(exceptions);
exceptions = NULL;
}
CFReleaseSafe(trust->_exceptions);
trust->_exceptions = exceptions;
if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
return true;
CFReleaseNull(trust->_exceptions);
return false;
}
CFArrayRef SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
CFMutableArrayRef summary;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
summary = SecCertificateCopySummaryProperties(certificate,
SecTrustGetVerifyTime(trust));
return summary;
}
CFArrayRef SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
CFArrayRef summary;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
summary = SecCertificateCopyProperties(certificate);
return summary;
}
#if 0
CFStringRef kSecPolicyCheckIdLinkage = CFSTR("IdLinkage");
CFStringRef kSecPolicyCheckCriticalExtensions = CFSTR("CriticalExtensions");
CFStringRef kSecPolicyCheckQualifiedCertStatements = CFSTR("QualifiedCertStatements");
CFStringRef kSecPolicyCheckAnchorTrusted = CFSTR("AnchorTrusted");
CFStringRef kSecPolicyCheckAnchorSHA1 = CFSTR("AnchorSHA1");
CFStringRef kSecPolicyCheckSSLHostname = CFSTR("SSLHostname");
CFStringRef kSecPolicyCheckNonEmptySubject = CFSTR("NonEmptySubject");
CFStringRef kSecPolicyCheckBasicContraints = CFSTR("BasicContraints");
CFStringRef kSecPolicyCheckKeyUsage = CFSTR("KeyUsage");
CFStringRef kSecPolicyCheckExtendedKeyUsage = CFSTR("ExtendedKeyUsage");
CFStringRef kSecPolicyCheckIssuerCommonName = CFSTR("IssuerCommonName");
CFStringRef kSecPolicyCheckSubjectCommonNamePrefix = CFSTR("SubjectCommonNamePrefix");
CFStringRef kSecPolicyCheckChainLength = CFSTR("ChainLength");
CFStringRef kSecPolicyCheckNotValidBefore = CFSTR("NotValidBefore");
CFStringRef kSecPolicyCheckValidIntermediates = CFSTR("ValidIntermediates");
CFStringRef kSecPolicyCheckValidLeaf = CFSTR("ValidLeaf");
CFStringRef kSecPolicyCheckValidRoot = CFSTR("ValidRoot");
#endif
struct TrustFailures {
bool badLinkage;
bool unknownCritExtn;
bool untrustedAnchor;
bool hostnameMismatch;
bool policyFail;
bool invalidCert;
};
static void applyDetailProperty(const void *_key, const void *_value,
void *context) {
CFStringRef key = (CFStringRef)_key;
struct TrustFailures *tf = (struct TrustFailures *)context;
if (CFGetTypeID(_value) != CFBooleanGetTypeID()) {
return;
}
CFBooleanRef value = (CFBooleanRef)_value;
if (CFBooleanGetValue(value)) {
return;
}
if (CFEqual(key, kSecPolicyCheckIdLinkage)) {
tf->badLinkage = true;
} else if (CFEqual(key, kSecPolicyCheckCriticalExtensions)
|| CFEqual(key, kSecPolicyCheckQualifiedCertStatements)) {
tf->unknownCritExtn = true;
} else if (CFEqual(key, kSecPolicyCheckAnchorTrusted)
|| CFEqual(key, kSecPolicyCheckAnchorSHA1)) {
tf->untrustedAnchor = true;
} else if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
tf->hostnameMismatch = true;
} else if (CFEqual(key, kSecPolicyCheckValidIntermediates)
|| CFEqual(key, kSecPolicyCheckValidLeaf)
|| CFEqual(key, kSecPolicyCheckValidLeaf)) {
tf->invalidCert = true;
} else
#if 0
if (CFEqual(key, kSecPolicyCheckNonEmptySubject)
|| CFEqual(key, kSecPolicyCheckBasicContraints)
|| CFEqual(key, kSecPolicyCheckKeyUsage)
|| CFEqual(key, kSecPolicyCheckExtendedKeyUsage)
|| CFEqual(key, kSecPolicyCheckIssuerCommonName)
|| CFEqual(key, kSecPolicyCheckSubjectCommonNamePrefix)
|| CFEqual(key, kSecPolicyCheckChainLength)
|| CFEqual(key, kSecPolicyCheckNotValidBefore))
#endif
{
tf->policyFail = true;
}
}
static void appendError(CFMutableArrayRef properties, CFStringRef error) {
CFStringRef localizedError = SecFrameworkCopyLocalizedString(error,
CFSTR("SecCertificate"));
appendProperty(properties, kSecPropertyTypeError, NULL, NULL,
localizedError);
}
CFArrayRef SecTrustCopyProperties(SecTrustRef trust) {
CFArrayRef details = SecTrustGetDetails(trust);
if (!details)
return NULL;
struct TrustFailures tf = {};
CFIndex ix, count = CFArrayGetCount(details);
for (ix = 0; ix < count; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)
CFArrayGetValueAtIndex(details, ix);
CFDictionaryApplyFunction(detail, applyDetailProperty, &tf);
}
CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (tf.badLinkage) {
appendError(properties, CFSTR("Invalid certificate chain linkage."));
} else if (tf.unknownCritExtn) {
appendError(properties, CFSTR("One or more unsupported critical extensions found."));
} else {
if (tf.untrustedAnchor) {
appendError(properties, CFSTR("Root certificate is not trusted."));
}
if (tf.hostnameMismatch) {
appendError(properties, CFSTR("Hostname mismatch."));
}
if (tf.policyFail) {
appendError(properties, CFSTR("Policy requirements not met."));
}
if (tf.invalidCert) {
appendError(properties, CFSTR("One or more certificates have expired or are not valid yet."));
}
}
if (CFArrayGetCount(properties) == 0) {
CFReleaseNull(properties);
}
return properties;
}
#if 0
#pragma mark -
#pragma mark SecTrustNode
typedef uint8_t SecFetchingState;
enum {
kSecFetchingStatePassedIn = 0,
kSecFetchingStateLocal,
kSecFetchingStateFromURL,
kSecFetchingStateDone,
};
typedef uint8_t SecTrustState;
enum {
kSecTrustStateUnknown = 0,
kSecTrustStateNotSigner,
kSecTrustStateValidSigner,
};
typedef struct __SecTrustNode *SecTrustNodeRef;
struct __SecTrustNode {
SecTrustNodeRef _child;
SecCertificateRef _certificate;
bool _isAnchor;
bool _isSelfSigned;
CFMutableSet _certificates;
CFMutableSet _partials;
CFMutableSet _rejected_partials;
CFMutableSet _candidates;
CFMutableSet _rejected_candidates;
SecFetchingState _fetchingState;
SecTrustState _trustState;
};
typedef struct __SecTrustNode SecTrustNode;
static pthread_once_t kSecTrustNodeRegisterClass = PTHREAD_ONCE_INIT;
static CFTypeID kSecTrustNodeTypeID = _kCFRuntimeNotATypeID;
static CFStringRef SecTrustNodeDescribe(CFTypeRef cf);
static void SecTrustNodeDestroy(CFTypeRef cf);
static CFStringRef SecTrustNodeDescribe(CFTypeRef cf) {
SecTrustNodeRef node = (SecTrustNodeRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<SecTrustNodeRef: %p>"), node);
}
static void SecTrustNodeDestroy(CFTypeRef cf) {
SecTrustNodeRef trust = (SecTrustNodeRef)cf;
if (trust->_child) {
free(trust->_child);
}
if (trust->_certificate) {
free(trust->_certificate);
}
if (trust->_candidates) {
CFRelease(trust->_candidates);
}
}
static void SecTrustNodeRegisterClass(void) {
static const CFRuntimeClass kSecTrustNodeClass = {
0,
"SecTrustNode",
NULL,
NULL,
SecTrustNodeDestroy,
NULL,
NULL,
NULL,
SecTrustNodeDescribe
};
kSecTrustNodeTypeID = _CFRuntimeRegisterClass(&kSecTrustNodeClass);
}
CFTypeID SecTrustNodeGetTypeID(void) {
pthread_once(&kSecTrustNodeRegisterClass, SecTrustNodeRegisterClass);
return kSecTrustNodeTypeID;
}
SecTrustNodeRef SecTrustNodeCreate(SecTrustRef trust,
SecCertificateRef certificate, SecTrustNodeRef child) {
CFAllocatorRef allocator = kCFAllocatorDefault;
check(trust);
check(certificate);
CFIndex size = sizeof(struct __SecTrustNode);
SecTrustNodeRef result = (SecTrustNodeRef)_CFRuntimeCreateInstance(
allocator, SecTrustNodeGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
if (child) {
CFRetain(child);
result->_child = child;
}
CFRetain(certificate);
result->_certificate = certificate;
result->_isAnchor = SecTrustCertificateIsAnchor(certificate);
return result;
}
SecCertificateRef SecTrustGetCertificate(SecTrustNodeRef node) {
check(node);
return node->_certificate;
}
CFArrayRef SecTrustNodeCopyProperties(SecTrustNodeRef node,
SecTrustRef trust) {
check(node);
check(trust);
CFMutableArrayRef summary = SecCertificateCopySummaryProperties(
node->_certificate, SecTrustGetVerifyTime(trust));
return summary;
}
SecTrustState SecTrustNodeVerifySignatureChain(SecTrustNodeRef node) {
return kSecTrustStateUnknown;
}
SecTrustNodeRef SecTrustNodeCopyNextCandidate(SecTrustNodeRef node,
SecTrustRef trust, SecFetchingState fetchingState) {
check(node);
check(trust);
CFAbsoluteTime verifyTime = SecTrustGetVerifyTime(trust);
for (;;) {
while (node->_candidateIndex < CFArrayGetCount(node->_candidates)) {
SecCertificateRef candidate = (SecCertificateRef)
CFArrayGetValueAtIndex(node->_candidates, node->_candidateIndex);
if (node->_fetchingState != kSecFetchingStateDone) {
if (!SecCertificateIsValidOn(candidate, verifyTime)) {
node->_candidateIndex++;
continue;
}
}
SecTrustNodeRef parent = SecTrustNodeCreate(candidate, node);
CFArrayRemoveValueAtIndex(node->_candidates, node->_candidateIndex);
if (SecTrustNodeVerifySignatureChain(parent) ==
kSecTrustStateNotSigner) {
CFRelease(parent);
continue;
}
return parent;
}
SecCertificateRef certificate = node->_certificate;
switch (node->_fetchingState) {
case kSecFetchingStatePassedIn:
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
if (akid) {
SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
} else {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
}
node->_fetchingState = kSecFetchingStateLocal;
break;
case kSecFetchingStateLocal:
node->_fetchingState = kSecFetchingStateFromURL;
break;
case kSecFetchingStateFromURL:
node->_fetchingState = kSecFetchingStateCheckExpired;
break;
case kSecFetchingStateCheckExpired:
node->_candidateIndex = 0;
node->_fetchingState = kSecFetchingStateDone;
break;
case kSecFetchingStateDone;
return NULL;
}
}
CFAllocatorRef allocator = CFGetAllocator(node);
CFMutableArrayRef issuers = CFArrayCreateMutable(allocator, 0,
&kCFTypeArrayCallBacks);
certificate = node->_certificate;
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
CFTypeRef candidates = NULL;
if (akid) {
candidates = (CFTypeRef)CFDictionaryGetValueForKey(skidDict, akid);
if (candidates) {
addValidIssuersFrom(issuers, certificate, candidates, true);
}
}
if (!candidates) {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
candidates = (CFTypeRef)
CFDictionaryGetValueForKey(subjectDict, issuer);
addValidIssuersFrom(issuers, certificate, candidates, false);
}
if (CFArrayGetCount(issuers) == 0) {
}
return noErr;
}
CFArrayRef SecTrustNodeCopyNextChain(SecTrustNodeRef node,
SecTrustRef trust) {
switch (node->_fetchingState) {
case kSecFetchingStatePassedIn:
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
if (akid) {
SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
} else {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
}
node->_fetchingState = kSecFetchingStateLocal;
break;
case kSecFetchingStateLocal:
node->_fetchingState = kSecFetchingStateFromURL;
break;
case kSecFetchingStateFromURL:
node->_fetchingState = kSecFetchingStateCheckExpired;
break;
case kSecFetchingStateCheckExpired:
node->_candidateIndex = 0;
node->_fetchingState = kSecFetchingStateDone;
break;
case kSecFetchingStateDone;
return NULL;
}
}
class Source {
Iterator parentIterator(Cert);
};
class NodeCache {
Set nodes;
static bool unique(Node node) {
if (nodes.contains(node))
return false;
nodes.add(node);
return true;
}
static bool isAnchor(Cert cert);
};
class Node {
Cert cert;
Node child;
int nextSource;
Iterator parentIterator;
Node(Cert inCert) : child(nil), cert(inCert), nextSource(0) {}
Node(Node inChild, Cert inCert) : child(inChild), cert(inCert),
nextSource(0) {}
CertPath certPath() {
CertPath path;
Node node = this;
while (node) {
path.add(node.cert);
node = node.child;
}
return path;
}
void contains(Cert cert) {
Node node = this;
while (node) {
if (cert == node.cert)
return true;
node = node.child;
}
return false;
}
Node nextParent(Array currentSources) {
for (;;) {
if (!nextSource ||
parentIterator == currentSources[nextSource - 1].end()) {
if (nextSource == currentSources.count) {
return nil;
}
parentIterator = currentSources[nextSource++].begin();
}
Certificate cert = *parentIterator++;
if (!contains(cert)) {
Node node = Node(this, parent);
if (!NodeCache.unique(node))
return node;
}
}
}
};
class PathBuilder {
List nodes;
List rejects;
Array currentSources;
Iterator nit;
Array allSources;
Iterator sourceIT;
CertPath chain;
PathBuilder(Cert cert) {
nodes.append(Node(cert));
nit = nodes.begin();
sourceIT = allSources.begin();
}
nextAnchoredPath() {
if (nit == nodes.end()) {
if (sourceIT == allSources.end()) {
}
currentSources += *sourceIT++;
Nodes.sortBySize();
nit = nodes.begin();
}
while (Node node = *nit) {
Node candidate = node.nextParent(currentSources);
if (!candidate) {
nit++;
continue;
}
if (candidate.isAnchored) {
candidates.append(candidate);
} else
nodes.insert(candidate, nit);
}
}
findValidPath() {
while (Node node = nextAnchoredPath()) {
if (node.isValid()) {
chain = node.certPath;
break;
}
rejects.append(node);
}
}
}
#endif