#include <securityd/SecPolicyServer.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTask.h>
#include <utilities/SecIOFormat.h>
#include <securityd/asynchttp.h>
#include <securityd/policytree.h>
#include <securityd/nameconstraints.h>
#include <CoreFoundation/CFTimeZone.h>
#include <wctype.h>
#include <libDER/oidsPriv.h>
#include <CoreFoundation/CFNumber.h>
#include <Security/SecCertificateInternal.h>
#include <AssertMacros.h>
#include <utilities/debugging.h>
#include <utilities/SecInternalReleasePriv.h>
#include <security_asn1/SecAsn1Coder.h>
#include <security_asn1/ocspTemplates.h>
#include <security_asn1/oidsalg.h>
#include <security_asn1/oidsocsp.h>
#include <CommonCrypto/CommonDigest.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecTrustSettings.h>
#include <Security/SecInternal.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecTask.h>
#include <CFNetwork/CFHTTPMessage.h>
#include <CFNetwork/CFHTTPStream.h>
#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
#include <asl.h>
#include <securityd/SecOCSPRequest.h>
#include <securityd/SecOCSPResponse.h>
#include <securityd/asynchttp.h>
#include <securityd/SecTrustServer.h>
#include <securityd/SecOCSPCache.h>
#include <utilities/array_size.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecAppleAnchorPriv.h>
#include "OTATrustUtilities.h"
#include "personalization.h"
#include <sys/codesign.h>
#if !TARGET_OS_IPHONE
#include <Security/SecTaskPriv.h>
#endif
#ifndef DUMP_OCSPRESPONSES
#define DUMP_OCSPRESPONSES 0
#endif
#if DUMP_OCSPRESPONSES
#include <unistd.h>
#include <fcntl.h>
static void secdumpdata(CFDataRef data, const char *name) {
int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0666);
write(fd, CFDataGetBytePtr(data), CFDataGetLength(data));
close(fd);
}
#endif
static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL;
static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL;
static CFArrayRef SecPolicyAnchorDigestsForEVPolicy(const DERItem *policyOID)
{
CFArrayRef result = NULL;
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiRef)
{
return result;
}
CFDictionaryRef evToPolicyAnchorDigest = SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef);
CFRelease(otapkiRef);
if (NULL == evToPolicyAnchorDigest)
{
return result;
}
CFArrayRef roots = NULL;
CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, policyOID);
if (oid && evToPolicyAnchorDigest)
{
result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid);
if (roots && CFGetTypeID(result) != CFArrayGetTypeID())
{
secerror("EVRoot.plist has non array value");
result = NULL;
}
CFRelease(oid);
}
CFReleaseSafe(evToPolicyAnchorDigest);
return result;
}
static bool SecPolicyIsEVPolicy(const DERItem *policyOID) {
return SecPolicyAnchorDigestsForEVPolicy(policyOID);
}
static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate,
policy_set_t valid_policies) {
CFDictionaryRef keySizes = NULL;
CFNumberRef rsaSize = NULL, ecSize = NULL;
bool isEV = false;
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
policy_set_t ix;
bool good_ev_anchor = false;
for (ix = valid_policies; ix; ix = ix->oid_next) {
CFArrayRef digests = SecPolicyAnchorDigestsForEVPolicy(&ix->oid);
if (digests && CFArrayContainsValue(digests,
CFRangeMake(0, CFArrayGetCount(digests)), digest)) {
secdebug("ev", "found anchor for policy oid");
good_ev_anchor = true;
break;
}
}
require_action_quiet(good_ev_anchor, notEV, secnotice("ev", "anchor not in plist"));
CFAbsoluteTime october2006 = 178761600;
if (SecCertificateNotValidBefore(certificate) >= october2006) {
require_action_quiet(SecCertificateVersion(certificate) >= 3, notEV,
secnotice("ev", "Anchor issued after October 2006 and is not v3"));
}
if (SecCertificateVersion(certificate) >= 3
&& SecCertificateNotValidBefore(certificate) >= october2006) {
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
require_action_quiet(bc && bc->isCA == true, notEV,
secnotice("ev", "Anchor has invalid basic constraints"));
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
== (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV,
secnotice("ev", "Anchor has invalid key usage %u", ku));
}
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV);
require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV);
const void *keys[] = { kSecAttrKeyTypeRSA, kSecAttrKeyTypeEC };
const void *values[] = { rsaSize, ecSize };
require_quiet(keySizes = CFDictionaryCreate(NULL, keys, values, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), notEV);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "Anchor's public key is too weak for EV"));
isEV = true;
notEV:
CFReleaseNull(rsaSize);
CFReleaseNull(ecSize);
CFReleaseNull(keySizes);
return isEV;
}
static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate) {
CFMutableDictionaryRef keySizes = NULL;
CFNumberRef rsaSize = NULL, ecSize = NULL;
bool isEV = false;
const SecCECertificatePolicies *cp;
cp = SecCertificateGetCertificatePolicies(certificate);
require_action_quiet(cp && cp->numPolicies > 0, notEV,
secnotice("ev", "SubCA missing certificate policies"));
CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
require_action_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV,
secnotice("ev", "SubCA missing CRLDP"));
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
require_action_quiet(bc && bc->isCA == true, notEV,
secnotice("ev", "SubCA has invalid basic constraints"));
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
== (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV,
secnotice("ev", "SubCA has invalid key usage %u", ku));
CFAbsoluteTime jan2011 = 315532800;
CFAbsoluteTime jan2014 = 410227200;
require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV);
require_quiet(keySizes = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeEC, ecSize);
if (SecCertificateNotValidBefore(certificate) < jan2011 ||
SecCertificateNotValidAfter(certificate) < jan2014) {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 1024), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
} else {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
}
CFAbsoluteTime jan2016 = 473299200;
if (SecCertificateNotValidBefore(certificate) > jan2016) {
require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1,
notEV, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
}
isEV = true;
notEV:
CFReleaseNull(rsaSize);
CFReleaseNull(ecSize);
CFReleaseNull(keySizes);
return isEV;
}
bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate) {
CFMutableDictionaryRef keySizes = NULL;
CFNumberRef rsaSize = NULL, ecSize = NULL;
bool isEV = false;
const SecCECertificatePolicies *cp;
cp = SecCertificateGetCertificatePolicies(certificate);
require_quiet(cp && cp->numPolicies > 0, notEV);
uint32_t ix = 0;
bool found_ev_anchor_for_leaf_policy = false;
for (ix = 0; ix < cp->numPolicies; ++ix) {
if (SecPolicyIsEVPolicy(&cp->policies[ix].policyIdentifier)) {
found_ev_anchor_for_leaf_policy = true;
}
}
require_quiet(found_ev_anchor_for_leaf_policy, notEV);
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
if (bc) {
require_action_quiet(bc->isCA == false, notEV,
secnotice("ev", "Leaf has invalid basic constraints"));
}
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
if (ku) {
require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV,
secnotice("ev", "Leaf has invalid key usage %u", ku));
}
#if 0
SecCertificateCopyExtendedKeyUsage(certificate);
#endif
CFAbsoluteTime jan2014 = 410227200;
require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV);
require_quiet(keySizes = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeEC, ecSize);
if (SecCertificateNotValidBefore(certificate) < jan2014) {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 1024), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
} else {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
}
CFAbsoluteTime jul2016 = 489024000;
CFAbsoluteTime notAfter = SecCertificateNotValidAfter(certificate);
CFAbsoluteTime notBefore = SecCertificateNotValidBefore(certificate);
if (SecCertificateNotValidBefore(certificate) < jul2016) {
CFAbsoluteTime maxPeriod = 60*60*24*(365*5+2);
require_action_quiet(notAfter - notBefore <= maxPeriod, notEV,
secnotice("ev", "Leaf's validity period is more than 60 months"));
} else {
CFAbsoluteTime maxPeriod = 60*60*24*(365*3+2*31+30+1);
require_action_quiet(notAfter - notBefore <= maxPeriod, notEV,
secnotice("ev", "Leaf has validity period longer than 39 months and issued after 30 June 2016"));
}
CFAbsoluteTime jan2016 = 473299200;
if (SecCertificateNotValidBefore(certificate) > jan2016) {
require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1,
notEV, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
}
isEV = true;
notEV:
CFReleaseNull(rsaSize);
CFReleaseNull(ecSize);
CFReleaseNull(keySizes);
return isEV;
}
static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc,
CFStringRef key) {
}
static void SecPolicyCheckIdLinkage(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
CFDataRef parentSubjectKeyID = NULL;
for (ix = count - 1; ix >= 0; --ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (parentSubjectKeyID) {
CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyID(cert);
if (authorityKeyID) {
if (!CFEqual(parentSubjectKeyID, authorityKeyID)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
parentSubjectKeyID = SecCertificateGetSubjectKeyID(cert);
}
}
static void SecPolicyCheckKeyUsage(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef xku = CFDictionaryGetValue(policy->_options, key);
if (!SecPolicyCheckCertKeyUsage(leaf, xku)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc, CFStringRef key) {
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef xeku = CFDictionaryGetValue(policy->_options, key);
if (!SecPolicyCheckCertExtendedKeyUsage(leaf, xeku)){
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
#if 0
static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc,
CFStringRef key, bool strict) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
const SecCEBasicConstraints *bc =
SecCertificateGetBasicConstraints(cert);
if (bc) {
if (strict) {
if (ix == 0) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
} else if (!bc->critical) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
if (ix > 0 || count == 1) {
if (!bc->isCA) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
if (bc->pathLenConstraintPresent) {
if (bc->pathLenConstraint < (uint32_t)(ix - 1)) {
#if 0
CFIndex ssix = SecCertificatePathSelfSignedIndex(path);
if (ssix >= 0 && ix >= ssix) {
} else
#endif
{
if (!SecPVCSetResult(pvc, key, ix,
kCFBooleanFalse))
return;
}
}
}
}
} else if (strict && ix > 0) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
#endif
static void SecPolicyCheckBasicConstraints(SecPVCRef pvc,
CFStringRef key) {
}
static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecCertificateHasSubject(cert)) {
if (ix == 0 && count > 1) {
if (!SecCertificateHasCriticalSubjectAltName(cert)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
} else {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
}
static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc,
CFStringRef key) {
}
static bool SecDomainSuffixMatch(CFStringRef hostname, CFStringRef domain) {
CFStringInlineBuffer hbuf = {}, dbuf = {};
UniChar hch, dch;
CFIndex hix, dix,
hlength = CFStringGetLength(hostname),
dlength = CFStringGetLength(domain);
CFRange hrange = { 0, hlength }, drange = { 0, dlength };
CFStringInitInlineBuffer(hostname, &hbuf, hrange);
CFStringInitInlineBuffer(domain, &dbuf, drange);
if((hlength == 0) || (dlength == 0)) {
return (hlength == dlength) ? true : false;
}
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hlength-1);
dch = CFStringGetCharacterFromInlineBuffer(&dbuf, dlength-1);
if(hch == '.') {
hrange.length = --hlength;
}
if(dch == '.') {
drange.length = --dlength;
}
dch = CFStringGetCharacterFromInlineBuffer(&dbuf, 0);
if((dlength > 0) && (dch == '.')) {
drange.location++;
drange.length = --dlength;
}
if(hlength < dlength) {
return false;
}
for (hix = (hlength-dlength),
dix = drange.location; dix < drange.length; dix++) {
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix);
dch = CFStringGetCharacterFromInlineBuffer(&dbuf, dix);
if (towlower(hch) != towlower(dch)) {
return false;
}
}
if(hlength > dlength) {
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, (hlength-(dlength+1)));
if(hch != '.') {
return false;
}
}
return true;
}
#define kSecPolicySHA1Size 20
static const UInt8 kAppleCorpCASHA1[kSecPolicySHA1Size] = {
0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
};
static bool SecPolicyCheckDomain(SecPVCRef pvc, CFStringRef hostname)
{
CFIndex count = SecPVCGetCertificateCount(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
CFDataRef corpSHA1 = CFDataCreateWithBytesNoCopy(NULL,
kAppleCorpCASHA1, kSecPolicySHA1Size, kCFAllocatorNull);
bool isCorpSHA1 = (corpSHA1 && CFEqual(anchorSHA1, corpSHA1));
CFReleaseSafe(corpSHA1);
if (isCorpSHA1) {
const CFStringRef dnlist[] = {
CFSTR("apple.com"),
CFSTR("icloud.com"),
};
unsigned int idx, dncount=2;
for (idx = 0; idx < dncount; idx++) {
if (SecDomainSuffixMatch(hostname, dnlist[idx])) {
return true;
}
}
return false;
}
return true;
}
static void SecPolicyCheckSSLHostname(SecPVCRef pvc,
CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef hostName = (CFStringRef)
CFDictionaryGetValue(policy->_options, key);
if (!isString(hostName)) {
return;
}
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
bool dnsMatch = SecPolicyCheckCertSSLHostname(leaf, hostName);
if (!dnsMatch) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
else if (!SecPolicyCheckDomain(pvc, hostName)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
if ((dnsMatch || pvc->details)
&& SecPolicySubscriberCertificateCouldBeEV(leaf)) {
secdebug("policy", "enabling optionally_ev");
pvc->optionally_ev = true;
}
}
static void SecPolicyCheckEmail(SecPVCRef pvc, CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef email = (CFStringRef)CFDictionaryGetValue(policy->_options, key);
if (!isString(email)) {
return;
}
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
if (!SecPolicyCheckCertEmail(leaf, email)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckValidIntermediates(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
for (ix = 1; ix < count - 1; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecCertificateIsValid(cert, verifyTime)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
static void SecPolicyCheckValidLeaf(SecPVCRef pvc,
CFStringRef key) {
CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
if (!SecCertificateIsValid(cert, verifyTime)) {
if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
return;
}
}
static void SecPolicyCheckValidRoot(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
ix = count - 1;
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecCertificateIsValid(cert, verifyTime)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
if (count < 2) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
return;
}
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 1);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef commonName =
(CFStringRef)CFDictionaryGetValue(policy->_options, key);
if (!isString(commonName)) {
return;
}
if (!SecPolicyCheckCertSubjectCommonName(cert, commonName)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options,
key);
if (!isString(common_name)) {
return;
}
if (!SecPolicyCheckCertSubjectCommonName(cert, common_name)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef prefix = (CFStringRef)CFDictionaryGetValue(policy->_options,
key);
if (!isString(prefix)) {
return;
}
if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert, prefix)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options,
key);
if (!isString(common_name)) {
return;
}
if (!SecPolicyCheckCertSubjectCommonNameTEST(cert, common_name)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckNotValidBefore(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFDateRef date = (CFDateRef)CFDictionaryGetValue(policy->_options, key);
if (!isDate(date)) {
return;
}
if (!SecPolicyCheckCertNotValidBefore(cert, date)) {
if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
return;
}
}
static void SecPolicyCheckChainLength(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFNumberRef chainLength =
(CFNumberRef)CFDictionaryGetValue(policy->_options, key);
CFIndex value;
if (!chainLength || CFGetTypeID(chainLength) != CFNumberGetTypeID() ||
!CFNumberGetValue(chainLength, kCFNumberCFIndexType, &value)) {
return;
}
if (value != count) {
if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
return;
}
}
static bool isDigestInPolicy(SecPVCRef pvc, CFStringRef key, CFDataRef digest) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
bool foundMatch = false;
if (isData(value))
foundMatch = CFEqual(digest, value);
else if (isArray(value))
foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), digest);
else {
assert(false);
}
return foundMatch;
}
static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc, CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
CFDataRef anchorSHA256 = NULL;
anchorSHA256 = SecCertificateCopySHA256Digest(cert);
if (!isDigestInPolicy(pvc, key, anchorSHA256)) {
SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA256, count-1, kCFBooleanFalse);
}
CFReleaseNull(anchorSHA256);
return;
}
static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
if (!isDigestInPolicy(pvc, key, anchorSHA1))
if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, count-1, kCFBooleanFalse))
return;
return;
}
static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = NULL;
CFDataRef digest = NULL;
if (SecPVCGetCertificateCount(pvc) < 2) {
SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 0, kCFBooleanFalse);
return;
}
cert = SecPVCGetCertificateAtIndex(pvc, 1);
digest = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert);
if (!isDigestInPolicy(pvc, key, digest)) {
SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 1, kCFBooleanFalse);
}
CFReleaseNull(digest);
}
static void SecPolicyCheckAnchorApple(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
SecAppleTrustAnchorFlags flags = 0;
if (isDictionary(value)) {
if (CFDictionaryGetValue(value, kSecPolicyAppleAnchorIncludeTestRoots)) {
flags |= kSecAppleTrustAnchorFlagsIncludeTestAnchors;
}
}
bool foundMatch = SecIsAppleTrustAnchor(cert, flags);
if (!foundMatch)
if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorApple, 0, kCFBooleanFalse))
return;
return;
}
static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef org = (CFStringRef)CFDictionaryGetValue(policy->_options,
key);
if (!isString(org)) {
return;
}
if (!SecPolicyCheckCertSubjectOrganization(cert, org)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef orgUnit = (CFStringRef)CFDictionaryGetValue(policy->_options,
key);
if (!isString(orgUnit)) {
return;
}
if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert, orgUnit)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc,
CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFArrayRef trustedServerNames = (CFArrayRef)
CFDictionaryGetValue(policy->_options, key);
if (!trustedServerNames)
return;
if (!isArray(trustedServerNames)) {
return;
}
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
if (!SecPolicyCheckCertEAPTrustedServerNames(leaf, trustedServerNames)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static const unsigned char UTN_USERFirst_Hardware_Serial[][16] = {
{ 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
{ 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
{ 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
{ 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
{ 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
{ 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
{ 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
{ 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
{ 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer[] = {
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
};
static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len = 151;
static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
CFDataRef issuer = cert ? SecCertificateGetNormalizedIssuerContent(cert) : NULL;
if (issuer && (CFDataGetLength(issuer) == (CFIndex)UTN_USERFirst_Hardware_Normalized_Issuer_len) &&
(0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer, CFDataGetBytePtr(issuer),
UTN_USERFirst_Hardware_Normalized_Issuer_len)))
{
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
CFDataRef serial = SecCertificateCopySerialNumber(cert, NULL);
#else
CFDataRef serial = SecCertificateCopySerialNumber(cert);
#endif
if (serial) {
CFIndex serial_length = CFDataGetLength(serial);
const uint8_t *serial_ptr = CFDataGetBytePtr(serial);
while ((serial_length > 0) && (*serial_ptr == 0)) {
serial_ptr++;
serial_length--;
}
if (serial_length == (CFIndex)sizeof(*UTN_USERFirst_Hardware_Serial)) {
unsigned int i;
for (i = 0; i < array_size(UTN_USERFirst_Hardware_Serial); i++)
{
if (0 == memcmp(UTN_USERFirst_Hardware_Serial[i],
serial_ptr, sizeof(*UTN_USERFirst_Hardware_Serial)))
{
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
CFReleaseSafe(serial);
return;
}
}
}
CFRelease(serial);
}
}
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != otapkiRef)
{
CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef);
CFRelease(otapkiRef);
if (NULL != blackListedKeys)
{
CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
if (dgst)
{
if (CFSetContainsValue(blackListedKeys, dgst))
{
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFRelease(dgst);
}
CFRelease(blackListedKeys);
}
}
}
static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc, CFStringRef key)
{
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != otapkiRef)
{
CFSetRef grayListedKeys = SecOTAPKICopyGrayList(otapkiRef);
CFRelease(otapkiRef);
if (NULL != grayListedKeys)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
if (dgst)
{
if (CFSetContainsValue(grayListedKeys, dgst))
{
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFRelease(dgst);
}
CFRelease(grayListedKeys);
}
}
}
static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc, CFStringRef key)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
if (!SecPolicyCheckCertLeafMarkerOid(cert, value)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc, CFStringRef key)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert, value)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key)
{
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
for (ix = 1; ix < count - 1; ix++) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (SecCertificateHasMarkerExtension(cert, value))
return;
}
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc, CFStringRef key)
{
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef peku = CFDictionaryGetValue(policy->_options, key);
for (ix = 1; ix < count - 1; ix++) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecPolicyCheckCertExtendedKeyUsage(cert, peku)) {
SecPVCSetResult(pvc, key, ix, kCFBooleanFalse);
}
}
}
static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc)
{
bool result = false;
CFIndex ix = 0, count = SecPVCGetCertificateCount(pvc);
CFStringRef authKey = NULL;
SecOTAPKIRef otapkiRef = NULL;
CFDictionaryRef allowList = NULL;
if (count < 1) {
return result;
}
SecCertificateRef lastCert = SecPVCGetCertificateAtIndex(pvc, count - 1);
CFDataRef authKeyID = SecCertificateGetAuthorityKeyID(lastCert);
if (NULL == authKeyID) {
return result;
}
authKey = CFDataCopyHexString(authKeyID);
if (NULL == authKey) {
goto errout;
}
otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiRef) {
goto errout;
}
allowList = SecOTAPKICopyAllowList(otapkiRef);
if (NULL == allowList) {
goto errout;
}
CFArrayRef allowedCerts = CFDictionaryGetValue(allowList, authKey);
if (!allowedCerts || !CFArrayGetCount(allowedCerts)) {
goto errout;
}
CFRange range = CFRangeMake(0, CFArrayGetCount(allowedCerts));
for (ix = 0; ix < count; ix++) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!cert) {
goto errout;
}
CFDataRef certHash = SecCertificateCopySHA256Digest(cert);
if (!certHash) {
goto errout;
}
CFIndex position = CFArrayBSearchValues(allowedCerts, range, certHash,
(CFComparatorFunction)CFDataCompare, NULL);
if (position < CFArrayGetCount(allowedCerts)) {
CFDataRef possibleMatch = CFArrayGetValueAtIndex(allowedCerts, position);
if (!CFDataCompare(certHash, possibleMatch)) {
result = true;
}
}
CFRelease(certHash);
}
errout:
CFReleaseNull(authKey);
CFReleaseNull(otapkiRef);
CFReleaseNull(allowList);
return result;
}
#define DCMP(_idx_) memcmp(data+(8*_idx_), digest, 8)
static bool SecPVCCheckCTWhiteListedLeaf(SecPVCRef pvc)
{
SecOTAPKIRef otapkiRef = NULL;
CFDataRef whiteList = NULL;
SecCertificateRef cert = NULL;
CFDataRef dgst = NULL;
bool result = false;
const uint8_t *digest = NULL;
const uint8_t *data = NULL;
require(otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(), out);
require(whiteList = SecOTAPKICopyCTWhiteList(otapkiRef), out);
require(cert = SecPVCGetCertificateAtIndex(pvc, 0), out);
require(dgst = SecCertificateCopySHA256Digest(cert), out);
digest = CFDataGetBytePtr(dgst);
data = CFDataGetBytePtr(whiteList);
CFIndex l = 0;
CFIndex h = CFDataGetLength(whiteList)/8-1;
if(DCMP(l)==0 || DCMP(h)==0) {
result = true;
goto out;
}
if(DCMP(l)>0 || DCMP(h)<0) {
goto out;
}
while((h-l)>1) {
CFIndex i = (h+l)/
2;
int s = DCMP(i);
if(s == 0) {
result = true;
goto out;
} else if(s < 0) {
l = i;
} else {
h = i;
}
}
out:
CFReleaseSafe(dgst);
CFReleaseSafe(whiteList);
CFReleaseSafe(otapkiRef);
return result;
}
#if 0
typedef struct cert_path *cert_path_t;
struct cert_path {
int length;
};
typedef struct x500_name *x500_name_t;
struct x500_name {
};
typedef struct algorithm_id *algorithm_id_t;
struct algorithm_id {
oid_t algorithm_oid;
der_t parameters;
};
typedef struct trust_anchor *trust_anchor_t;
struct trust_anchor {
x500_name_t issuer_name;
algorithm_id_t public_key_algorithm;
SecKeyRef public_key;
};
typedef struct certificate_policy *certificate_policy_t;
struct certificate_policy {
policy_qualifier_t qualifiers;
oid_t oid;
SLIST_ENTRY(certificate_policy) policies;
};
typedef struct policy_mapping *policy_mapping_t;
struct policy_mapping {
SLIST_ENTRY(policy_mapping) mappings;
oid_t issuer_domain_policy;
oid_t subject_domain_policy;
};
typedef struct root_name *root_name_t;
struct root_name {
};
#endif
struct policy_tree_add_ctx {
oid_t p_oid;
policy_qualifier_t p_q;
};
static bool policy_tree_add_if_match(policy_tree_t node, void *ctx) {
struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
policy_set_t policy_set;
for (policy_set = node->expected_policy_set;
policy_set;
policy_set = policy_set->oid_next) {
if (oid_equal(policy_set->oid, info->p_oid)) {
policy_tree_add_child(node, &info->p_oid, info->p_q);
return true;
}
}
return false;
}
static bool policy_tree_add_if_any(policy_tree_t node, void *ctx) {
struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
if (oid_equal(node->valid_policy, oidAnyPolicy)) {
policy_tree_add_child(node, &info->p_oid, info->p_q);
return true;
}
return false;
}
static bool policy_tree_has_child_with_oid(policy_tree_t node,
const oid_t *oid) {
policy_tree_t child;
for (child = node->children; child; child = child->siblings) {
if (oid_equal(child->valid_policy, (*oid))) {
return true;
}
}
return false;
}
static bool policy_tree_add_expected(policy_tree_t node, void *ctx) {
policy_qualifier_t p_q = (policy_qualifier_t)ctx;
policy_set_t policy_set;
bool added_node = false;
for (policy_set = node->expected_policy_set;
policy_set;
policy_set = policy_set->oid_next) {
if (!policy_tree_has_child_with_oid(node, &policy_set->oid)) {
policy_tree_add_child(node, &policy_set->oid, p_q);
added_node = true;
}
}
return added_node;
}
#if 0
static bool policy_tree_map(policy_tree_t node, void *ctx) {
if (oid_equal(node->valid_policy, oidAnyPolicy))
return false;
const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
uint32_t mapping_ix, mapping_count = pm->numMappings;
policy_set_t policy_set = NULL;
for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) {
policy_set_t p_node = (policy_set_t)malloc(sizeof(*policy_set));
p_node->oid = mapping->subjectDomainPolicy;
p_node->oid_next = policy_set ? policy_set : NULL;
policy_set = p_node;
}
}
if (policy_set) {
policy_tree_set_expected_policy(node, policy_set);
return true;
}
return false;
}
#endif
#define POLICY_MAPPING 0
#define POLICY_SUBTREES 1
static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
assert((unsigned long)count<=UINT32_MAX);
uint32_t n = (uint32_t)count;
bool is_anchored = SecPVCIsAnchored(pvc);
if (is_anchored) {
n--;
} else {
if (!SecPVCCheckCertificateAllowList(pvc)) {
if (SecPVCSetResultForced(pvc, kSecPolicyCheckAnchorTrusted,
n - 1, kCFBooleanFalse, true))
return;
}
}
CFAbsoluteTime verify_time = SecPVCGetVerifyTime(pvc);
bool initial_policy_mapping_inhibit = false;
bool initial_explicit_policy = false;
bool initial_any_policy_inhibit = false;
pvc->valid_policy_tree = policy_tree_create(&oidAnyPolicy, NULL);
#if POLICY_SUBTREES
CFMutableArrayRef permitted_subtrees = NULL;
CFMutableArrayRef excluded_subtrees = NULL;
permitted_subtrees = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
excluded_subtrees = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
require_action_quiet(permitted_subtrees != NULL, errOut,
SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true));
require_action_quiet(excluded_subtrees != NULL, errOut,
SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true));
#endif
uint32_t explicit_policy = initial_explicit_policy ? 0 : n + 1;
uint32_t inhibit_any_policy = initial_any_policy_inhibit ? 0 : n + 1;
uint32_t policy_mapping = initial_policy_mapping_inhibit ? 0 : n + 1;
#if 0
algorithm_id_t working_public_key_algorithm = anchor->public_key_algorithm;
SecKeyRef working_public_key = anchor->public_key;
x500_name_t working_issuer_name = anchor->issuer_name;
#endif
uint32_t i, max_path_length = n;
SecCertificateRef cert = NULL;
for (i = 1; i <= n; ++i) {
cert = SecPVCGetCertificateAtIndex(pvc, n - i);
bool is_self_issued = SecPVCIsCertificateAtIndexSelfSigned(pvc, n - i);
#if 1
if (!SecCertificateIsValid(cert, verify_time)) {
CFStringRef fail_key = i == n ? kSecPolicyCheckValidLeaf : kSecPolicyCheckValidIntermediates;
if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) {
goto errOut;
}
}
if (SecCertificateIsWeak(cert)) {
CFStringRef fail_key = i == n ? kSecPolicyCheckWeakLeaf : kSecPolicyCheckWeakIntermediates;
if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) {
goto errOut;
}
}
#endif
#if POLICY_SUBTREES
if (!is_self_issued || i == n) {
bool found = false;
if(excluded_subtrees && CFArrayGetCount(excluded_subtrees)) {
if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, excluded_subtrees, &found, false)) || found) {
secnotice("policy", "name in excluded subtrees");
if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; }
}
}
if(permitted_subtrees && CFArrayGetCount(permitted_subtrees)) {
if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, permitted_subtrees, &found, true)) || !found) {
secnotice("policy", "name not in permitted subtrees");
if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; }
}
}
}
#endif
if (pvc->valid_policy_tree) {
const SecCECertificatePolicies *cp =
SecCertificateGetCertificatePolicies(cert);
size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
oid_t p_oid = policy->policyIdentifier;
policy_qualifier_t p_q = &policy->policyQualifiers;
struct policy_tree_add_ctx ctx = { p_oid, p_q };
if (!oid_equal(p_oid, oidAnyPolicy)) {
if (!policy_tree_walk_depth(pvc->valid_policy_tree, i - 1,
policy_tree_add_if_match, &ctx)) {
policy_tree_walk_depth(pvc->valid_policy_tree, i - 1,
policy_tree_add_if_any, &ctx);
}
}
}
if (inhibit_any_policy > 0 || (i < n && is_self_issued)) {
for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
oid_t p_oid = policy->policyIdentifier;
policy_qualifier_t p_q = &policy->policyQualifiers;
if (oid_equal(p_oid, oidAnyPolicy)) {
policy_tree_walk_depth(pvc->valid_policy_tree, i - 1,
policy_tree_add_expected, (void *)p_q);
}
}
}
policy_tree_prune_childless(&pvc->valid_policy_tree, i - 1);
if (!cp) {
if (pvc->valid_policy_tree)
policy_tree_prune(&pvc->valid_policy_tree);
}
}
if (!pvc->valid_policy_tree && explicit_policy == 0) {
secnotice("policy", "policy tree failure");
if (!SecPVCSetResultForced(pvc, key , n - i, kCFBooleanFalse, true)) {
goto errOut;
}
}
if (i == n)
break;
#if POLICY_MAPPING
CFDictionaryRef pm = SecCertificateGetPolicyMappings(cert);
if (pm) {
uint32_t mapping_ix, mapping_count = pm->numMappings;
for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
if (oid_equal(mapping->issuerDomainPolicy, oidAnyPolicy)
|| oid_equal(mapping->subjectDomainPolicy, oidAnyPolicy)) {
if (!SecPVCSetResultForced(pvc, key , n - i, kCFBooleanFalse)) {
goto errOut;
}
}
}
if (policy_mapping > 0) {
if (!policy_tree_walk_depth(pvc->valid_policy_tree, i,
policy_tree_map, (void *)pm)) {
}
} else {
#if 0
struct policy_tree_map_ctx ctx = { idp_oid, sdp_oid };
policy_tree_walk_depth(pvc->valid_policy_tree, i,
policy_tree_delete_if_match, &ctx);
#endif
policy_tree_prune_childless(&pvc->valid_policy_tree, i - 1);
}
}
#endif
#if POLICY_SUBTREES
CFArrayRef permitted_subtrees_in_cert = SecCertificateGetPermittedSubtrees(cert);
if (permitted_subtrees_in_cert) {
SecNameConstraintsIntersectSubtrees(permitted_subtrees, permitted_subtrees_in_cert);
}
CFArrayRef excluded_subtrees_in_cert = SecCertificateGetExcludedSubtrees(cert);
if (excluded_subtrees_in_cert) {
CFIndex num_trees = CFArrayGetCount(excluded_subtrees_in_cert);
CFRange range = { 0, num_trees };
CFArrayAppendArray(excluded_subtrees, excluded_subtrees_in_cert, range);
}
#endif
if (!is_self_issued) {
if (explicit_policy)
explicit_policy--;
if (policy_mapping)
policy_mapping--;
if (inhibit_any_policy)
inhibit_any_policy--;
}
const SecCEPolicyConstraints *pc =
SecCertificateGetPolicyConstraints(cert);
if (pc) {
if (pc->requireExplicitPolicyPresent
&& pc->requireExplicitPolicy < explicit_policy) {
explicit_policy = pc->requireExplicitPolicy;
}
if (pc->inhibitPolicyMappingPresent
&& pc->inhibitPolicyMapping < policy_mapping) {
policy_mapping = pc->inhibitPolicyMapping;
}
}
uint32_t iap = SecCertificateGetInhibitAnyPolicySkipCerts(cert);
if (iap < inhibit_any_policy) {
inhibit_any_policy = iap;
}
const SecCEBasicConstraints *bc =
SecCertificateGetBasicConstraints(cert);
#if 0
if (!bc || !bc->isCA) {
if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints,
n - i, kCFBooleanFalse)) {
goto errOut;
}
}
#endif
if (!is_self_issued) {
if (max_path_length > 0) {
max_path_length--;
} else {
if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints,
n - i, kCFBooleanFalse)) {
goto errOut;
}
}
}
if (bc && bc->pathLenConstraintPresent
&& bc->pathLenConstraint < max_path_length) {
max_path_length = bc->pathLenConstraint;
}
#if 0
SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage,
n - i, kCFBooleanFalse, true)) {
goto errOut;
}
}
#endif
if (SecCertificateHasUnknownCriticalExtension(cert)) {
if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions,
n - i, kCFBooleanFalse)) {
goto errOut;
}
}
}
cert = SecPVCGetCertificateAtIndex(pvc, 0);
if (explicit_policy)
explicit_policy--;
const SecCEPolicyConstraints *pc = SecCertificateGetPolicyConstraints(cert);
if (pc) {
if (pc->requireExplicitPolicyPresent
&& pc->requireExplicitPolicy == 0) {
explicit_policy = 0;
}
}
if (SecCertificateHasUnknownCriticalExtension(cert)) {
if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions,
0, kCFBooleanFalse)) {
goto errOut;
}
}
if (pvc->valid_policy_tree) {
#if !defined(NDEBUG)
policy_tree_dump(pvc->valid_policy_tree);
#endif
}
if (!pvc->valid_policy_tree && explicit_policy == 0) {
secnotice("policy", "policy tree failure");
if (!SecPVCSetResultForced(pvc, key , 0, kCFBooleanFalse, true)) {
goto errOut;
}
}
errOut:
CFReleaseNull(permitted_subtrees);
CFReleaseNull(excluded_subtrees);
}
static policy_set_t policies_for_cert(SecCertificateRef cert) {
policy_set_t policies = NULL;
const SecCECertificatePolicies *cp =
SecCertificateGetCertificatePolicies(cert);
size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
policy_set_add(&policies, &cp->policies[policy_ix].policyIdentifier);
}
return policies;
}
static void SecPolicyCheckEV(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
policy_set_t valid_policies = NULL;
if (count) {
CFAbsoluteTime jul2016 = 489024000;
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
if (SecCertificateNotValidBefore(leaf) > jul2016 && count < 3) {
if (SecPVCSetResultForced(pvc, key,
0, kCFBooleanFalse, true)) {
return;
}
}
}
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
policy_set_t policies = policies_for_cert(cert);
if (ix == 0) {
if (!policy_set_contains(policies, &oidAnyPolicy)) {
valid_policies = policies;
policies = NULL;
}
} else if (ix < count - 1) {
if (!SecPolicySubordinateCACertificateCouldBeEV(cert)) {
secnotice("ev", "subordinate certificate is not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
policy_set_free(valid_policies);
policy_set_free(policies);
return;
}
}
policy_set_intersect(&valid_policies, policies);
} else {
if (!SecPolicyRootCACertificateIsEV(cert, valid_policies)) {
secnotice("ev", "anchor certificate is not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
policy_set_free(valid_policies);
policy_set_free(policies);
return;
}
}
}
policy_set_free(policies);
if (!valid_policies) {
secnotice("ev", "valid_policies set is empty: chain not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
return;
}
}
}
policy_set_free(valid_policies);
}
#include <Security/SecureTransportPriv.h>
static const
SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg)
{
switch(alg) {
case SSL_SignatureAlgorithmRSA:
switch (hash) {
case SSL_HashAlgorithmSHA1:
return &CSSMOID_SHA1WithRSA;
case SSL_HashAlgorithmSHA256:
return &CSSMOID_SHA256WithRSA;
case SSL_HashAlgorithmSHA384:
return &CSSMOID_SHA384WithRSA;
default:
break;
}
case SSL_SignatureAlgorithmECDSA:
switch (hash) {
case SSL_HashAlgorithmSHA1:
return &CSSMOID_ECDSA_WithSHA1;
case SSL_HashAlgorithmSHA256:
return &CSSMOID_ECDSA_WithSHA256;
case SSL_HashAlgorithmSHA384:
return &CSSMOID_ECDSA_WithSHA384;
default:
break;
}
default:
break;
}
return NULL;
}
static size_t SSLDecodeUint16(const uint8_t *p)
{
return (p[0]<<8 | p[1]);
}
static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len)
{
p[0] = (len >> 8)&0xff;
p[1] = (len & 0xff);
return p+2;
}
static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len)
{
p[0] = (len >> 16)&0xff;
p[1] = (len >> 8)&0xff;
p[2] = (len & 0xff);
return p+3;
}
static
uint64_t SSLDecodeUint64(const uint8_t *p)
{
uint64_t u = 0;
for(int i=0; i<8; i++) {
u=(u<<8)|p[0];
p++;
}
return u;
}
#include <libDER/DER_CertCrl.h>
#include <libDER/DER_Encode.h>
#include <libDER/asn1Types.h>
static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc)
{
SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert));
CFDataSetLength(data, 3+SecCertificateGetLength(leafCert));
uint8_t *q = CFDataGetMutableBytePtr(data);
q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert));
memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert));
return data;
}
static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc)
{
SecCertificateRef leafCert = NULL;
SecCertificateRef issuer = NULL;
CFDataRef issuerKeyHash = NULL;
CFDataRef tbs_precert = NULL;
CFMutableDataRef data= NULL;
require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
issuer = SecPVCGetCertificateAtIndex(pvc, 1);
require(leafCert, out);
require(issuer, out); issuerKeyHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer);
tbs_precert = SecCertificateCopyPrecertTBS(leafCert);
require(issuerKeyHash, out);
require(tbs_precert, out);
data = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
CFDataSetLength(data, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
uint8_t *q = CFDataGetMutableBytePtr(data);
memcpy(q, CFDataGetBytePtr(issuerKeyHash), CFDataGetLength(issuerKeyHash)); q += CFDataGetLength(issuerKeyHash); q = SSLEncodeUint24(q, CFDataGetLength(tbs_precert));
memcpy(q, CFDataGetBytePtr(tbs_precert), CFDataGetLength(tbs_precert));
out:
CFReleaseSafe(issuerKeyHash);
CFReleaseSafe(tbs_precert);
return data;
}
static
CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
{
return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
}
static
uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
{
return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
}
static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFArrayRef trustedLogs, CFAbsoluteTime *sct_at)
{
uint8_t version;
const uint8_t *logID;
const uint8_t *timestampData;
uint64_t timestamp;
size_t extensionsLen;
const uint8_t *extensionsData;
uint8_t hashAlg;
uint8_t sigAlg;
size_t signatureLen;
const uint8_t *signatureData;
SecKeyRef pubKey = NULL;
uint8_t *signed_data = NULL;
const SecAsn1Oid *oid = NULL;
SecAsn1AlgId algId;
CFDataRef logIDData = NULL;
CFDictionaryRef result = 0;
const uint8_t *p = CFDataGetBytePtr(sct);
size_t len = CFDataGetLength(sct);
require(len>=43, out);
version = p[0]; p++; len--;
logID = p; p+=32; len-=32;
timestampData = p; p+=8; len-=8;
extensionsLen = SSLDecodeUint16(p); p+=2; len-=2;
require(len>=extensionsLen, out);
extensionsData = p; p+=extensionsLen; len-=extensionsLen;
require(len>=4, out);
hashAlg=p[0]; p++; len--;
sigAlg=p[0]; p++; len--;
signatureLen = SSLDecodeUint16(p); p+=2; len-=2;
require(len==signatureLen, out);
signatureData = p;
if(version!=0) {
secerror("SCT version unsupported: %d\n", version);
goto out;
}
timestamp = SSLDecodeUint64(timestampData);
if(timestamp > vt) {
secerror("SCT is in the future: %llu > %llu\n", timestamp, vt);
goto out;
}
uint8_t *q;
size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ;
signed_data = malloc(signed_data_len);
require(signed_data, out);
q = signed_data;
*q++ = version;
*q++ = 0; memcpy(q, timestampData, 8); q+=8;
q = SSLEncodeUint16(q, entry_type); memcpy(q, CFDataGetBytePtr(entry), CFDataGetLength(entry)); q += CFDataGetLength(entry);
q = SSLEncodeUint16(q, extensionsLen);
memcpy(q, extensionsData, extensionsLen);
logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
CFDictionaryRef logData = CFArrayGetValueMatching(trustedLogs, ^bool(const void *dict) {
const void *key_data;
if(!isDictionary(dict)) return false;
if(!CFDictionaryGetValueIfPresent(dict, CFSTR("key"), &key_data)) return false;
if(!isData(key_data)) return false;
CFDataRef valueID = SecSHA256DigestCreateFromData(kCFAllocatorDefault, (CFDataRef)key_data);
bool result = (bool)(CFDataCompare(logIDData, valueID)==kCFCompareEqualTo);
CFReleaseSafe(valueID);
return result;
});
require(logData, out);
if(entry_type==0) {
require(!CFDictionaryContainsKey(logData, CFSTR("expiry")), out);
}
CFDataRef logKeyData = CFDictionaryGetValue(logData, CFSTR("key"));
require(logKeyData, out); pubKey = SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault, logKeyData);
require(pubKey, out);
oid = oidForSigAlg(hashAlg, sigAlg);
require(oid, out);
algId.algorithm = *oid;
algId.parameters.Data = NULL;
algId.parameters.Length = 0;
if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
*sct_at = TimestampToCFAbsoluteTime(timestamp);
result = logData;
} else {
secerror("SCT signature failed (log=%@)\n", logData);
}
out:
CFReleaseSafe(logIDData);
CFReleaseSafe(pubKey);
free(signed_data);
return result;
}
static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at)
{
CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log);
if(validated_time==NULL || (sct_at < CFDateGetAbsoluteTime(validated_time))) {
CFDateRef sct_time = CFDateCreate(kCFAllocatorDefault, sct_at);
CFDictionarySetValue(validatingLogs, log, sct_time);
CFReleaseSafe(sct_time);
}
}
static CFArrayRef copy_ocsp_scts(SecPVCRef pvc)
{
CFMutableArrayRef SCTs = NULL;
SecCertificateRef leafCert = NULL;
SecCertificateRef issuer = NULL;
CFArrayRef ocspResponsesData = NULL;
SecOCSPRequestRef ocspRequest = NULL;
ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
require_quiet(ocspResponsesData, out);
require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
issuer = SecPVCGetCertificateAtIndex(pvc, 1);
require(leafCert, out);
require(issuer, out); ocspRequest = SecOCSPRequestCreate(leafCert, issuer);
SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
require(SCTs, out);
CFArrayForEach(ocspResponsesData, ^(const void *value) {
SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
if(ocspResponse && SecOCSPGetResponseStatus(ocspResponse)==kSecOCSPSuccess) {
SecOCSPSingleResponseRef ocspSingleResponse = SecOCSPResponseCopySingleResponse(ocspResponse, ocspRequest);
if(ocspSingleResponse) {
CFArrayRef singleResponseSCTs = SecOCSPSingleResponseCopySCTs(ocspSingleResponse);
if(singleResponseSCTs) {
CFArrayAppendArray(SCTs, singleResponseSCTs, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs)));
CFRelease(singleResponseSCTs);
}
SecOCSPSingleResponseDestroy(ocspSingleResponse);
}
}
if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
});
if(CFArrayGetCount(SCTs)==0) {
CFReleaseNull(SCTs);
}
out:
CFReleaseSafe(ocspResponsesData);
if(ocspRequest)
SecOCSPRequestFinalize(ocspRequest);
return SCTs;
}
static void SecPolicyCheckCT(SecPVCRef pvc, CFStringRef key)
{
SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
CFArrayRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
CFArrayRef ocspScts = copy_ocsp_scts(pvc);
CFDataRef precertEntry = copy_precert_entry_from_chain(pvc);
CFDataRef x509Entry = copy_x509_entry_from_chain(pvc);
CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
uint64_t vt = TimestampFromCFAbsoluteTime(pvc->verifyTime);
__block bool at_least_one_currently_valid_external = 0;
__block bool at_least_one_currently_valid_embedded = 0;
require(logsValidatingEmbeddedScts, out);
require(currentLogsValidatingScts, out);
if(trustedLogs) { if(embeddedScts && precertEntry) { CFArrayForEach(embeddedScts, ^(const void *value){
CFAbsoluteTime sct_at;
CFDictionaryRef log = getSCTValidatingLog(value, 1, precertEntry, vt, trustedLogs, &sct_at);
if(log) {
addValidatingLog(logsValidatingEmbeddedScts, log, sct_at);
if(!CFDictionaryContainsKey(log, CFSTR("expiry"))) {
addValidatingLog(currentLogsValidatingScts, log, sct_at);
at_least_one_currently_valid_embedded = true;
}
}
});
}
if(builderScts && x509Entry) { CFArrayForEach(builderScts, ^(const void *value){
CFAbsoluteTime sct_at;
CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, trustedLogs, &sct_at);
if(log) {
addValidatingLog(currentLogsValidatingScts, log, sct_at);
at_least_one_currently_valid_external = true;
}
});
}
if(ocspScts && x509Entry) {
CFArrayForEach(ocspScts, ^(const void *value){
CFAbsoluteTime sct_at;
CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, trustedLogs, &sct_at);
if(log) {
addValidatingLog(currentLogsValidatingScts, log, sct_at);
at_least_one_currently_valid_external = true;
}
});
}
}
pvc->is_ct = false;
if(at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2) {
pvc->is_ct = true;
} else if(at_least_one_currently_valid_embedded) {
__block CFAbsoluteTime issuanceTime = pvc->verifyTime;
__block int lifetime; __block unsigned once_or_current_qualified_embedded = 0;
CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) {
CFDictionaryRef log = key;
if(!CFDictionaryContainsKey(log, CFSTR("expiry"))) {
CFDateRef ts = (CFDateRef) value;
CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts);
if(timestamp < issuanceTime) {
issuanceTime = timestamp;
}
}
});
CFDictionaryForEach(logsValidatingEmbeddedScts, ^(const void *key, const void *value) {
CFDictionaryRef log = key;
CFDateRef ts = value;
CFDateRef expiry = CFDictionaryGetValue(log, CFSTR("expiry"));
if(expiry == NULL || CFDateCompare(ts, expiry, NULL) == kCFCompareLessThan) {
once_or_current_qualified_embedded++;
}
});
SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
int _lifetime;
CFCalendarGetComponentDifference(zuluCalendar,
SecCertificateNotValidBefore(leafCert),
SecCertificateNotValidAfter(leafCert),
0, "M", &_lifetime);
lifetime = _lifetime;
});
unsigned requiredEmbeddedSctsCount;
if (lifetime < 15) {
requiredEmbeddedSctsCount = 2;
} else if (lifetime <= 27) {
requiredEmbeddedSctsCount = 3;
} else if (lifetime <= 39) {
requiredEmbeddedSctsCount = 4;
} else {
requiredEmbeddedSctsCount = 5;
}
if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
pvc->is_ct = true;
}
}
out:
CFReleaseSafe(logsValidatingEmbeddedScts);
CFReleaseSafe(currentLogsValidatingScts);
CFReleaseSafe(builderScts);
CFReleaseSafe(embeddedScts);
CFReleaseSafe(ocspScts);
CFReleaseSafe(precertEntry);
CFReleaseSafe(trustedLogs);
CFReleaseSafe(x509Entry);
}
static bool checkPolicyOidData(SecPVCRef pvc, CFDataRef oid) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
DERItem key_value;
key_value.data = (DERByte *)CFDataGetBytePtr(oid);
key_value.length = (DERSize)CFDataGetLength(oid);
for (ix = 0; ix < count; ix++) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
policy_set_t policies = policies_for_cert(cert);
if (policy_set_contains(policies, &key_value)) {
return true;
}
}
return false;
}
static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc, CFStringRef key)
{
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
bool result = false;
if (CFGetTypeID(value) == CFDataGetTypeID())
{
result = checkPolicyOidData(pvc, value);
} else if (CFGetTypeID(value) == CFStringGetTypeID()) {
CFDataRef dataOid = SecCertificateCreateOidDataFromString(NULL, value);
if (dataOid) {
result = checkPolicyOidData(pvc, dataOid);
CFRelease(dataOid);
}
}
if(!result) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckRevocation(SecPVCRef pvc,
CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
if (isString(value)) {
SecPVCSetCheckRevocation(pvc, value);
}
}
static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc,
CFStringRef key) {
SecPVCSetCheckRevocationResponseRequired(pvc);
}
static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc,
CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
if (value == kCFBooleanTrue) {
SecPathBuilderSetCanAccessNetwork(pvc->builder, false);
} else {
SecPathBuilderSetCanAccessNetwork(pvc->builder, true);
}
}
static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
for (ix = 1; ix < count - 1; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (cert && SecCertificateIsWeak(cert)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
static void SecPolicyCheckWeakLeaf(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
if (cert && SecCertificateIsWeak(cert)) {
if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
return;
}
}
static void SecPolicyCheckWeakRoot(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
ix = count - 1;
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (cert && SecCertificateIsWeak(cert)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
static void SecPolicyCheckKeySize(SecPVCRef pvc, CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFDictionaryRef keySizes = CFDictionaryGetValue(policy->_options, key);
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecCertificateIsAtLeastMinKeySize(cert, keySizes)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFSetRef disallowedHashAlgorithms = CFDictionaryGetValue(policy->_options, key);
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
if (!SecPolicyCheckCertSignatureHashAlgorithms(cert, disallowedHashAlgorithms)) {
if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
return;
}
}
}
#define ENABLE_CRLS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
typedef struct OpaqueSecORVC *SecORVCRef;
#if ENABLE_CRLS
typedef struct OpaqueSecCRVC *SecCRVCRef;
#endif
struct OpaqueSecRVC {
SecPVCRef pvc;
CFIndex certIX;
SecORVCRef orvc;
#if ENABLE_CRLS
SecCRVCRef crvc;
#endif
bool done;
};
typedef struct OpaqueSecRVC *SecRVCRef;
const CFAbsoluteTime kSecDefaultOCSPResponseTTL = 24.0 * 60.0 * 60.0;
#define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
struct OpaqueSecORVC {
asynchttp_t http;
SecPVCRef pvc;
SecRVCRef rvc;
SecOCSPRequestRef ocspRequest;
SecOCSPResponseRef ocspResponse;
SecOCSPSingleResponseRef ocspSingleResponse;
CFIndex certIX;
CFIndex responderIX;
CFURLRef responder;
CFAbsoluteTime nextUpdate;
bool done;
};
static void SecORVCFinish(SecORVCRef orvc) {
secdebug("alloc", "%p", orvc);
asynchttp_free(&orvc->http);
if (orvc->ocspRequest) {
SecOCSPRequestFinalize(orvc->ocspRequest);
orvc->ocspRequest = NULL;
}
if (orvc->ocspResponse) {
SecOCSPResponseFinalize(orvc->ocspResponse);
orvc->ocspResponse = NULL;
if (orvc->ocspSingleResponse) {
SecOCSPSingleResponseDestroy(orvc->ocspSingleResponse);
orvc->ocspSingleResponse = NULL;
}
}
}
static CFURLRef SecORVCGetNextResponder(SecORVCRef rvc) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if (ocspResponders) {
CFIndex responderCount = CFArrayGetCount(ocspResponders);
while (rvc->responderIX < responderCount) {
CFURLRef responder = CFArrayGetValueAtIndex(ocspResponders, rvc->responderIX);
rvc->responderIX++;
CFStringRef scheme = CFURLCopyScheme(responder);
if (scheme) {
bool valid_responder = (CFEqual(CFSTR("http"), scheme) ||
CFEqual(CFSTR("https"), scheme));
CFRelease(scheme);
if (valid_responder)
return responder;
}
}
}
return NULL;
}
static bool SecORVCFetchNext(SecORVCRef rvc) {
while ((rvc->responder = SecORVCGetNextResponder(rvc))) {
CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest);
if (!request)
goto errOut;
secinfo("rvc", "Sending http ocsp request for cert %ld", rvc->certIX);
if (!asyncHttpPost(rvc->responder, request, OCSP_RESPONSE_TIMEOUT, &rvc->http)) {
return false;
}
}
errOut:
rvc->done = true;
return true;
}
static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef this,
SecORVCRef rvc) {
bool processed;
switch (this->certStatus) {
case CS_Good:
secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
rvc->nextUpdate = this->nextUpdate == NULL_TIME ? this->thisUpdate + kSecDefaultOCSPResponseTTL : this->nextUpdate;
processed = true;
break;
case CS_Revoked:
secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex, rvc->certIX);
SInt32 reason = this->crlReason;
CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
SecPVCSetResultForced(rvc->pvc, kSecPolicyCheckRevocation, rvc->certIX,
cfreason, true);
if (rvc->pvc && rvc->pvc->info) {
CFDictionarySetValue(rvc->pvc->info, kSecTrustRevocationReason, cfreason);
}
CFRelease(cfreason);
processed = true;
break;
case CS_Unknown:
secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
processed = false;
break;
default:
secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
(int)this->certStatus, rvc->certIX);
processed = false;
break;
}
return processed;
}
static void SecORVCUpdatePVC(SecORVCRef rvc) {
if (rvc->ocspSingleResponse) {
SecOCSPSingleResponseProcess(rvc->ocspSingleResponse, rvc);
}
if (rvc->ocspResponse) {
rvc->nextUpdate = SecOCSPResponseGetExpirationTime(rvc->ocspResponse);
}
}
typedef void (^SecOCSPEvaluationCompleted)(SecTrustResultType tr);
static void
SecOCSPEvaluateCompleted(const void *userData,
SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
SecOCSPEvaluationCompleted evaluated = (SecOCSPEvaluationCompleted)userData;
evaluated(result);
Block_release(evaluated);
}
static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc, CFArrayRef signers, CFArrayRef issuers, CFAbsoluteTime verifyTime) {
__block bool evaluated = false;
bool trusted = false;
if (!signers || !issuers) {
return trusted;
}
const void *ocspSigner = SecPolicyCreateOCSPSigner();
CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
&ocspSigner, 1, &kCFTypeArrayCallBacks);
CFRelease(ocspSigner);
SecOCSPEvaluationCompleted completed = Block_copy(^(SecTrustResultType result) {
if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
evaluated = true;
}
});
CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->pvc->builder);
SecPathBuilderRef oBuilder = SecPathBuilderCreate(clientAuditToken,
signers, issuers, true, false,
policies, NULL, NULL, NULL,
verifyTime, NULL,
SecOCSPEvaluateCompleted, completed);
SecPathBuilderStep(oBuilder);
CFReleaseNull(clientAuditToken);
CFReleaseNull(policies);
if (evaluated) {
SecCertificateRef issuer = NULL, signer = NULL;
SecKeyRef issuerPubKey = NULL;
issuer = (SecCertificateRef)CFArrayGetValueAtIndex(issuers, 0);
signer = (SecCertificateRef)CFArrayGetValueAtIndex(signers, 0);
if (issuer) {
#if TARGET_OS_IPHONE
issuerPubKey = SecCertificateCopyPublicKey(issuer);
#else
issuerPubKey = SecCertificateCopyPublicKey_ios(issuer);
#endif
}
if (signer && issuerPubKey && (errSecSuccess == SecCertificateIsSignedBy(signer, issuerPubKey))) {
trusted = true;
} else {
secnotice("ocsp", "ocsp signer cert not signed by issuer");
}
CFReleaseNull(issuerPubKey);
}
return trusted;
}
static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecORVCRef rvc, CFAbsoluteTime verifyTime) {
bool trusted;
SecCertificatePathRef issuers = SecCertificatePathCopyFromParent(rvc->pvc->path, rvc->certIX + 1);
SecCertificateRef issuer = issuers ? CFRetainSafe(SecCertificatePathGetCertificateAtIndex(issuers, 0)) : NULL;
CFArrayRef signers = SecOCSPResponseCopySigners(ocspResponse);
SecCertificateRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
if (signer && signers) {
if (issuer && CFEqual(signer, issuer)) {
secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
rvc->responder);
trusted = true;
} else {
secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
rvc->responder);
CFMutableArrayRef signerCerts = NULL;
CFArrayRef issuerCerts = NULL;
signerCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(signerCerts, signer);
CFArrayAppendArray(signerCerts, signers, CFRangeMake(0, CFArrayGetCount(signers)));
if (issuers) {
issuerCerts = SecCertificatePathCopyCertificates(issuers, NULL);
}
if (SecOCSPResponseEvaluateSigner(rvc, signerCerts, issuerCerts, verifyTime)) {
secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
rvc->responder);
trusted = true;
} else {
secnotice("ocsp", "ocsp response signed by certificate which "
"does not satisfy ocspSigner policy");
trusted = false;
}
CFReleaseNull(signerCerts);
CFReleaseNull(issuerCerts);
}
} else {
secnotice("ocsp", "ocsp responder: %@ no signer found for response",
rvc->responder);
trusted = false;
}
#if DUMP_OCSPRESPONSES
char buf[40];
snprintf(buf, 40, "/tmp/ocspresponse%ld%s.der",
rvc->certIX, (trusted ? "t" : "u"));
secdumpdata(ocspResponse->data, buf);
#endif
CFReleaseNull(issuers);
CFReleaseNull(issuer);
CFReleaseNull(signers);
CFReleaseNull(signer);
return trusted;
}
static void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse , CFTimeInterval maxAge, bool updateCache) {
SecOCSPSingleResponseRef sr = NULL;
require_quiet(ocspResponse, errOut);
SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
require_action_quiet(orStatus == kSecOCSPSuccess, errOut,
secnotice("ocsp", "responder: %@ returned status: %d", rvc->responder, orStatus));
require_action_quiet(sr = SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest), errOut,
secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc->responder));
require_quiet(!rvc->ocspSingleResponse || rvc->ocspSingleResponse->thisUpdate < sr->thisUpdate, errOut);
CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
require_quiet(SecOCSPResponseVerify(ocspResponse, rvc,
sr->certStatus == CS_Revoked ? SecOCSPResponseProducedAt(ocspResponse) : verifyTime), errOut);
bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
if (sr->certStatus == CS_Good) {
require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
} else if (sr->certStatus == CS_Revoked) {
ocspResponse->expireTime = SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX));
}
if (updateCache)
SecOCSPCacheReplaceResponse(rvc->ocspResponse, ocspResponse, rvc->responder, verifyTime);
if (rvc->ocspResponse) SecOCSPResponseFinalize(rvc->ocspResponse);
rvc->ocspResponse = ocspResponse;
ocspResponse = NULL;
if (rvc->ocspSingleResponse) SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
rvc->ocspSingleResponse = sr;
sr = NULL;
rvc->done = sr_valid;
errOut:
if (sr) SecOCSPSingleResponseDestroy(sr);
if (ocspResponse) SecOCSPResponseFinalize(ocspResponse);
}
static void SecOCSPFetchCompleted(asynchttp_t *http, CFTimeInterval maxAge) {
SecORVCRef rvc = (SecORVCRef)http->info;
SecPVCRef pvc = rvc->pvc;
SecOCSPResponseRef ocspResponse = NULL;
if (http->response) {
CFDataRef data = CFHTTPMessageCopyBody(http->response);
if (data) {
ocspResponse = SecOCSPResponseCreate(data);
CFRelease(data);
}
}
SecORVCConsumeOCSPResponse(rvc, ocspResponse, maxAge, true);
if (!rvc->done) {
asynchttp_free(http);
SecORVCFetchNext(rvc);
}
if (rvc->done) {
secdebug("rvc", "got OCSP response for cert: %ld", rvc->certIX);
SecORVCUpdatePVC(rvc);
SecORVCFinish(rvc);
if (!--pvc->asyncJobCount) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(pvc->builder);
}
}
}
static SecORVCRef SecORVCCreate(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) {
SecORVCRef orvc = NULL;
orvc = malloc(sizeof(struct OpaqueSecORVC));
if (orvc) {
memset(orvc, 0, sizeof(struct OpaqueSecORVC));
orvc->pvc = pvc;
orvc->rvc = rvc;
orvc->certIX = certIX;
orvc->http.queue = SecPathBuilderGetQueue(pvc->builder);
orvc->http.token = SecPathBuilderCopyClientAuditToken(pvc->builder);
orvc->http.completed = SecOCSPFetchCompleted;
orvc->http.info = orvc;
orvc->ocspRequest = NULL;
orvc->responderIX = 0;
orvc->responder = NULL;
orvc->nextUpdate = NULL_TIME;
orvc->ocspResponse = NULL;
orvc->ocspSingleResponse = NULL;
orvc->done = false;
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, certIX);
SecCertificateRef issuer = SecPVCGetCertificateAtIndex(pvc, certIX + 1);
orvc->ocspRequest = SecOCSPRequestCreate(cert, issuer);
}
return orvc;
}
static void SecORVCProcessStapledResponses(SecORVCRef rvc) {
CFArrayRef ocspResponsesData = SecPathBuilderCopyOCSPResponses(rvc->pvc->builder);
if(ocspResponsesData) {
secdebug("rvc", "Checking stapled responses for cert %ld", rvc->certIX);
CFArrayForEach(ocspResponsesData, ^(const void *value) {
SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
SecORVCConsumeOCSPResponse(rvc, ocspResponse, NULL_TIME, false);
});
CFRelease(ocspResponsesData);
}
}
#if ENABLE_CRLS
#include <../trustd/SecTrustOSXEntryPoints.h>
OSStatus errSecCertificateRevoked = -67820;
#define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
struct OpaqueSecCRVC {
async_ocspd_t async_ocspd;
SecPVCRef pvc;
SecRVCRef rvc;
OSStatus status;
CFIndex certIX;
CFIndex distributionPointIX;
CFURLRef distributionPoint;
CFAbsoluteTime nextUpdate;
bool done;
};
static void SecCRVCFinish(SecCRVCRef crvc) {
}
static CFURLRef SecCRVCGetNextDistributionPoint(SecCRVCRef rvc) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef crlDPs = SecCertificateGetCRLDistributionPoints(cert);
if (crlDPs) {
CFIndex crlDPCount = CFArrayGetCount(crlDPs);
while (rvc->distributionPointIX < crlDPCount) {
CFURLRef distributionPoint = CFArrayGetValueAtIndex(crlDPs, rvc->distributionPointIX);
rvc->distributionPointIX++;
CFStringRef scheme = CFURLCopyScheme(distributionPoint);
if (scheme) {
bool valid_DP = (CFEqual(CFSTR("http"), scheme) ||
CFEqual(CFSTR("https"), scheme) ||
CFEqual(CFSTR("ldap"), scheme));
CFRelease(scheme);
if (valid_DP)
return distributionPoint;
}
}
}
return NULL;
}
static void SecCRVCGetCRLStatus(SecCRVCRef rvc) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
SecCertificatePathRef path = rvc->pvc->path;
CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(path, NULL);
secdebug("rvc", "searching CRL cache for cert: %ld", rvc->certIX);
rvc->status = SecTrustLegacyCRLStatus(cert, serializedCertPath, rvc->distributionPoint);
CFReleaseNull(serializedCertPath);
if (rvc->status == errSecSuccess || rvc->status == errSecCertificateRevoked) {
rvc->done = true;
rvc->nextUpdate = SecPVCGetVerifyTime(rvc->pvc) + kSecDefaultCRLTTL;
}
}
static void SecCRVCCheckRevocationCache(SecCRVCRef rvc) {
while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
SecCRVCGetCRLStatus(rvc);
if (rvc->status == errSecCertificateRevoked) {
return;
}
}
}
static bool SecCRVCFetchNext(SecCRVCRef rvc) {
while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
SecCertificatePathRef path = rvc->pvc->path;
CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(path, NULL);
secinfo("rvc", "fetching CRL for cert: %ld", rvc->certIX);
if (!SecTrustLegacyCRLFetch(&rvc->async_ocspd, rvc->distributionPoint,
CFAbsoluteTimeGetCurrent(), cert, serializedCertPath)) {
CFDataRef clientAuditToken = NULL;
SecTaskRef task = NULL;
audit_token_t auditToken = {};
clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->pvc->builder);
require(clientAuditToken, out);
require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out);
secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
rvc->distributionPoint, task);
out:
CFReleaseNull(clientAuditToken);
CFReleaseNull(task);
return false;
}
}
rvc->done = true;
return true;
}
static void SecCRVCUpdatePVC(SecCRVCRef rvc) {
if (rvc->status == errSecCertificateRevoked) {
secdebug("rvc", "CRL revoked cert %" PRIdCFIndex, rvc->certIX);
SInt32 reason = 0; CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
SecPVCSetResultForced(rvc->pvc, kSecPolicyCheckRevocation, rvc->certIX,
cfreason, true);
if (rvc->pvc && rvc->pvc->info) {
CFDictionarySetValue(rvc->pvc->info, kSecTrustRevocationReason, cfreason);
}
CFReleaseNull(cfreason);
}
}
static void SecCRVCFetchCompleted(async_ocspd_t *ocspd) {
SecCRVCRef rvc = ocspd->info;
SecPVCRef pvc = rvc->pvc;
if (ocspd->response == errSecSuccess || ocspd->response == errSecCertificateRevoked) {
rvc->status = ocspd->response;
rvc->done = true;
rvc->nextUpdate = SecPVCGetVerifyTime(rvc->pvc) + kSecDefaultCRLTTL;
secdebug("rvc", "got CRL response for cert: %ld", rvc->certIX);
SecCRVCUpdatePVC(rvc);
SecCRVCFinish(rvc);
if (!--pvc->asyncJobCount) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(pvc->builder);
}
} else {
if(SecCRVCFetchNext(rvc)) {
if (!--pvc->asyncJobCount) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(pvc->builder);
}
}
}
}
static SecCRVCRef SecCRVCCreate(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) {
SecCRVCRef crvc = NULL;
crvc = malloc(sizeof(struct OpaqueSecCRVC));
if (crvc) {
memset(crvc, 0, sizeof(struct OpaqueSecCRVC));
crvc->pvc = pvc;
crvc->rvc = rvc;
crvc->certIX = certIX;
crvc->status = errSecInternal;
crvc->distributionPointIX = 0;
crvc->distributionPoint = NULL;
crvc->nextUpdate = NULL_TIME;
crvc->async_ocspd.queue = SecPathBuilderGetQueue(pvc->builder);
crvc->async_ocspd.completed = SecCRVCFetchCompleted;
crvc->async_ocspd.response = errSecInternal;
crvc->async_ocspd.info = crvc;
crvc->done = false;
}
return crvc;
}
static bool SecRVCShouldCheckCRL(SecRVCRef rvc) {
if (rvc->pvc->check_revocation &&
CFEqual(kSecPolicyCheckRevocationCRL, rvc->pvc->check_revocation)) {
secinfo("rvc", "client told us to check CRL");
return true;
}
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0) &&
(rvc->pvc->check_revocation && !CFEqual(kSecPolicyCheckRevocationOCSP, rvc->pvc->check_revocation))) {
secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc->certIX);
return true;
}
return false;
}
#endif
static void SecRVCFinish(SecRVCRef rvc) {
if (rvc->orvc) {
SecORVCFinish(rvc->orvc);
}
#if ENABLE_CRLS
if (rvc->crvc) {
SecCRVCFinish(rvc->crvc);
}
#endif
}
static void SecRVCDelete(SecRVCRef rvc) {
if (rvc->orvc) {
SecORVCFinish(rvc->orvc);
free(rvc->orvc);
}
#if ENABLE_CRLS
if (rvc->crvc) {
SecCRVCFinish(rvc->crvc);
free(rvc->crvc);
}
#endif
}
static void SecRVCInit(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) {
secdebug("alloc", "%p", rvc);
rvc->pvc = pvc;
rvc->certIX = certIX;
rvc->orvc = SecORVCCreate(rvc, pvc, certIX);
#if ENABLE_CRLS
rvc->crvc = SecCRVCCreate(rvc, pvc, certIX);
#endif
rvc->done = false;
}
static void SecRVCUpdatePVC(SecRVCRef rvc) {
SecORVCUpdatePVC(rvc->orvc);
#if ENABLE_CRLS
SecCRVCUpdatePVC(rvc->crvc);
#endif
}
#if ENABLE_CRLS
static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
if (!rvc->pvc->check_revocation
|| !CFEqual(rvc->pvc->check_revocation, kSecPolicyCheckRevocationCRL)) {
return true;
}
return false;
}
#else
static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
return true;
}
#endif
static void SecRVCCheckRevocationCaches(SecRVCRef rvc) {
if (SecRVCShouldCheckOCSP(rvc)) {
secdebug("ocsp", "Checking cached responses for cert %ld", rvc->certIX);
SecORVCConsumeOCSPResponse(rvc->orvc,
SecOCSPCacheCopyMatching(rvc->orvc->ocspRequest, NULL),
NULL_TIME, false);
}
#if ENABLE_CRLS
if (SecRVCShouldCheckCRL(rvc)) {
SecCRVCCheckRevocationCache(rvc->crvc);
}
#endif
}
static bool SecRVCFetchNext(SecRVCRef rvc) {
bool OCSP_fetch_finished = true;
if (SecRVCShouldCheckOCSP(rvc)) {
OCSP_fetch_finished &= SecORVCFetchNext(rvc->orvc);
}
if (OCSP_fetch_finished) {
rvc->pvc->asyncJobCount--;
}
#if ENABLE_CRLS
bool CRL_fetch_finished = true;
if (SecRVCShouldCheckCRL(rvc)) {
rvc->crvc->distributionPointIX = 0;
CRL_fetch_finished &= SecCRVCFetchNext(rvc->crvc);
}
if (CRL_fetch_finished) {
rvc->pvc->asyncJobCount--;
}
OCSP_fetch_finished &= CRL_fetch_finished;
#endif
return OCSP_fetch_finished;
}
static bool SecPVCCheckRevocation(SecPVCRef pvc) {
secdebug("rvc", "checking revocation");
CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
bool completed = true;
if (certCount <= 1) {
return completed;
}
certCount--;
if (pvc->rvcs) {
secdebug("rvc", "Not rechecking revocation");
return completed;
}
pvc->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount);
#if !ENABLE_CRLS
pvc->asyncJobCount = (unsigned int) certCount;
#else
pvc->asyncJobCount = 2 * (unsigned int)certCount;
#endif
secdebug("rvc", "set asyncJobCount to %d", pvc->asyncJobCount);
for (certIX = 0; certIX < certCount; ++certIX) {
secdebug("rvc", "checking revocation for cert: %ld", certIX);
SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX];
SecRVCInit(rvc, pvc, certIX);
if (rvc->done){
continue;
}
if (SecRVCShouldCheckOCSP(rvc)) {
SecORVCProcessStapledResponses(rvc->orvc);
}
SecRVCCheckRevocationCaches(rvc);
if (rvc->orvc->done
#if ENABLE_CRLS
|| rvc->orvc->done
#endif
) {
secdebug("rvc", "found cached response for cert: %ld", certIX);
rvc->done = true;
}
bool allow_fetch = SecPathBuilderCanAccessNetwork(pvc->builder) && (pvc->is_ev || pvc->check_revocation);
bool fetch_done = true;
if (rvc->done || !allow_fetch) {
SecRVCUpdatePVC(rvc);
SecRVCFinish(rvc);
pvc->asyncJobCount--;
#if ENABLE_CRLS
pvc->asyncJobCount--;
#endif
secdebug("rvc", "not fetching and job count is %d for cert %ld", pvc->asyncJobCount, certIX);
} else {
fetch_done = SecRVCFetchNext(rvc);
}
if (!fetch_done) {
secdebug("rvc", "waiting on background fetch for cert %ld", certIX);
completed = false;
}
}
return completed;
}
static CFAbsoluteTime SecRVCGetEarliestNextUpdate(SecRVCRef rvc) {
CFAbsoluteTime enu = NULL_TIME;
enu = rvc->orvc->nextUpdate;
#if ENABLE_CRLS
CFAbsoluteTime crlNextUpdate = rvc->crvc->nextUpdate;
if (enu == NULL_TIME ||
((crlNextUpdate > NULL_TIME) && (enu > crlNextUpdate))) {
enu = crlNextUpdate;
}
#endif
return enu;
}
void SecPolicyServerInitalize(void) {
gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, NULL);
gSecPolicyPathCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, NULL);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckBasicCertificateProcessing,
SecPolicyCheckBasicCertificateProcessing);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckCriticalExtensions,
SecPolicyCheckCriticalExtensions);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIdLinkage,
SecPolicyCheckIdLinkage);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckKeyUsage,
SecPolicyCheckKeyUsage);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckExtendedKeyUsage,
SecPolicyCheckExtendedKeyUsage);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckBasicConstraints,
SecPolicyCheckBasicConstraints);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckNonEmptySubject,
SecPolicyCheckNonEmptySubject);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckQualifiedCertStatements,
SecPolicyCheckQualifiedCertStatements);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSSLHostname,
SecPolicyCheckSSLHostname);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckEmail,
SecPolicyCheckEmail);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckValidIntermediates,
SecPolicyCheckValidIntermediates);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckValidLeaf,
SecPolicyCheckValidLeaf);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckValidRoot,
SecPolicyCheckValidRoot);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIssuerCommonName,
SecPolicyCheckIssuerCommonName);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectCommonNamePrefix,
SecPolicyCheckSubjectCommonNamePrefix);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectCommonName,
SecPolicyCheckSubjectCommonName);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckNotValidBefore,
SecPolicyCheckNotValidBefore);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckChainLength,
SecPolicyCheckChainLength);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckAnchorSHA1,
SecPolicyCheckAnchorSHA1);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckAnchorSHA256,
SecPolicyCheckAnchorSHA256);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckAnchorApple,
SecPolicyCheckAnchorApple);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectOrganization,
SecPolicyCheckSubjectOrganization);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectOrganizationalUnit,
SecPolicyCheckSubjectOrganizationalUnit);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckEAPTrustedServerNames,
SecPolicyCheckEAPTrustedServerNames);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectCommonNameTEST,
SecPolicyCheckSubjectCommonNameTEST);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckRevocation,
SecPolicyCheckRevocation);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckRevocationResponseRequired,
SecPolicyCheckRevocationResponseRequired);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckNoNetworkAccess,
SecPolicyCheckNoNetworkAccess);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckBlackListedLeaf,
SecPolicyCheckBlackListedLeaf);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckGrayListedLeaf,
SecPolicyCheckGrayListedLeaf);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckLeafMarkerOid,
SecPolicyCheckLeafMarkerOid);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckLeafMarkerOidWithoutValueCheck,
SecPolicyCheckLeafMarkerOidWithoutValueCheck);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIntermediateSPKISHA256,
SecPolicyCheckIntermediateSPKISHA256);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIntermediateEKU,
SecPolicyCheckIntermediateEKU);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIntermediateMarkerOid,
SecPolicyCheckIntermediateMarkerOid);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckCertificatePolicy,
SecPolicyCheckCertificatePolicyOid);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckWeakIntermediates,
SecPolicyCheckWeakIntermediates);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckWeakLeaf,
SecPolicyCheckWeakLeaf);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckWeakRoot,
SecPolicyCheckWeakRoot);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckKeySize,
SecPolicyCheckKeySize);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckSignatureHashAlgorithms,
SecPolicyCheckSignatureHashAlgorithms);
}
void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies,
CFAbsoluteTime verifyTime) {
secdebug("alloc", "%p", pvc);
pvc->builder = builder;
pvc->policies = policies;
if (policies)
CFRetain(policies);
pvc->verifyTime = verifyTime;
pvc->path = NULL;
pvc->details = NULL;
pvc->info = NULL;
pvc->valid_policy_tree = NULL;
pvc->callbacks = NULL;
pvc->policyIX = 0;
pvc->rvcs = NULL;
pvc->asyncJobCount = 0;
pvc->check_revocation = NULL;
pvc->response_required = false;
pvc->optionally_ev = false;
pvc->is_ev = false;
pvc->result = true;
}
static void SecPVCDeleteRVCs(SecPVCRef pvc) {
secdebug("alloc", "%p", pvc);
if (pvc->rvcs) {
CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc) - 1;
for (certIX = 0; certIX < certCount; ++certIX) {
SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX];
SecRVCDelete(rvc);
}
free(pvc->rvcs);
pvc->rvcs = NULL;
}
}
void SecPVCDelete(SecPVCRef pvc) {
secdebug("alloc", "%p", pvc);
CFReleaseNull(pvc->policies);
CFReleaseNull(pvc->details);
CFReleaseNull(pvc->info);
if (pvc->valid_policy_tree) {
policy_tree_prune(&pvc->valid_policy_tree);
}
SecPVCDeleteRVCs(pvc);
CFReleaseNull(pvc->path);
}
void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathRef path,
CF_CONSUMED CFArrayRef details) {
secdebug("policy", "%@", path);
bool samePath = ((!path && !pvc->path) || (path && pvc->path && CFEqual(path, pvc->path)));
if (!samePath) {
SecPVCDeleteRVCs(pvc);
CFReleaseSafe(pvc->path);
pvc->path = CFRetainSafe(path);
}
pvc->details = details;
CFReleaseNull(pvc->info);
if (pvc->valid_policy_tree) {
policy_tree_prune(&pvc->valid_policy_tree);
}
pvc->policyIX = 0;
pvc->result = true;
}
SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) {
return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
}
CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
return SecCertificatePathGetCount(pvc->path);
}
SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
return SecCertificatePathGetCertificateAtIndex(pvc->path, ix);
}
bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc, CFIndex ix) {
return SecCertificatePathSelfSignedIndex(pvc->path) == ix;
}
void SecPVCSetCheckRevocation(SecPVCRef pvc, CFStringRef method) {
pvc->check_revocation = method;
secdebug("rvc", "deferred revocation checking enabled using %@ method", method);
}
void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc) {
pvc->response_required = true;
secdebug("rvc", "revocation response required");
}
bool SecPVCIsAnchored(SecPVCRef pvc) {
return SecCertificatePathIsAnchored(pvc->path);
}
CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
return pvc->verifyTime;
}
static int32_t detailKeyToCssmErr(CFStringRef key) {
int32_t result = 0;
if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
result = -2147408896; }
else if (CFEqual(key, kSecPolicyCheckEmail)) {
result = -2147408872; }
else if (CFEqual(key, kSecPolicyCheckValidLeaf) ||
CFEqual(key, kSecPolicyCheckValidIntermediates) ||
CFEqual(key, kSecPolicyCheckValidRoot)) {
result = -2147409654; }
return result;
}
static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint);
static bool SecPVCIsAllowedError(SecPVCRef pvc, CFIndex ix, CFStringRef key) {
bool result = false;
CFArrayRef constraints = SecCertificatePathGetUsageConstraintsAtIndex(pvc->path, ix);
SecCertificateRef cert = SecCertificatePathGetCertificateAtIndex(pvc->path, ix);
CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints);
for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) {
CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX);
CFNumberRef allowedErrorNumber = NULL;
if (!isDictionary(constraint)) {
continue;
}
allowedErrorNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsAllowedError);
int32_t allowedErrorValue = 0;
if (!isNumber(allowedErrorNumber) || !CFNumberGetValue(allowedErrorNumber, kCFNumberSInt32Type, &allowedErrorValue)) {
continue;
}
if (SecPVCMeetsConstraint(pvc, cert, constraint)) {
if (allowedErrorValue == detailKeyToCssmErr(key)) {
result = true;
break;
}
}
}
return result;
}
bool SecPVCSetResultForced(SecPVCRef pvc,
CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
(pvc->callbacks == gSecPolicyLeafCallbacks ? "leaf"
: (pvc->callbacks == gSecPolicyPathCallbacks ? "path"
: "custom")),
(force ? "force" : ""), result);
if (!force) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
if (policy && !CFDictionaryContainsKey(policy->_options, key))
return true;
}
if (SecPVCIsAllowedError(pvc, ix, key)) {
secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
return true;
}
pvc->result = false;
if (!pvc->details)
return false;
CFMutableDictionaryRef detail =
(CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
CFDictionarySetValue(detail, key, result);
return true;
}
bool SecPVCSetResult(SecPVCRef pvc,
CFStringRef key, CFIndex ix, CFTypeRef result) {
return SecPVCSetResultForced(pvc, key, ix, result, false);
}
static void SecPVCValidateKey(const void *key, const void *value,
void *context) {
SecPVCRef pvc = (SecPVCRef)context;
if (!pvc->result && !pvc->details)
return;
SecPolicyCheckFunction fcn = (SecPolicyCheckFunction)
CFDictionaryGetValue(pvc->callbacks, key);
if (!fcn) {
#if 0
pvc->result = false;
#endif
if (pvc->callbacks == gSecPolicyLeafCallbacks) {
if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks, key)) {
pvc->result = false;
}
} else if (pvc->callbacks == gSecPolicyPathCallbacks) {
if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks, key)) {
pvc->result = false;
}
} else {
pvc->result = false;
}
return;
}
fcn(pvc, (CFStringRef)key);
}
bool SecPVCLeafChecks(SecPVCRef pvc) {
pvc->result = true;
CFArrayRef policies = pvc->policies;
CFIndex ix, count = CFArrayGetCount(policies);
for (ix = 0; ix < count; ++ix) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
pvc->policyIX = ix;
pvc->callbacks = gSecPolicyLeafCallbacks;
CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
if (!pvc->result && !pvc->details)
return pvc->result;
}
return pvc->result;
}
bool SecPVCParentCertificateChecks(SecPVCRef pvc, CFIndex ix) {
CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
&& SecPVCIsAnchored(pvc));
if (!SecCertificateIsValid(cert, verifyTime)) {
if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckValidRoot
: kSecPolicyCheckValidIntermediates, ix, kCFBooleanFalse))
goto errOut;
}
if (SecCertificateIsWeak(cert)) {
if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckWeakRoot
: kSecPolicyCheckWeakIntermediates, ix, kCFBooleanFalse))
goto errOut;
}
if (is_anchor) {
} else {
if (SecCertificateVersion(cert) >= 3) {
const SecCEBasicConstraints *bc =
SecCertificateGetBasicConstraints(cert);
if (!bc || !bc->isCA) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicConstraints,
ix, kCFBooleanFalse, true))
goto errOut;
}
}
SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage,
ix, kCFBooleanFalse, true))
goto errOut;
}
}
errOut:
return pvc->result;
}
bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) {
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != otapkiRef)
{
CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef);
CFRelease(otapkiRef);
if (NULL != blackListedKeys)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
&& SecPVCIsAnchored(pvc));
if (!is_anchor) {
CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
if (dgst) {
if (CFSetContainsValue(blackListedKeys, dgst)) {
SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey,
ix, kCFBooleanFalse, true);
}
CFRelease(dgst);
}
}
CFRelease(blackListedKeys);
return pvc->result;
}
}
return true;
}
bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix)
{
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != otapkiRef)
{
CFSetRef grayListKeys = SecOTAPKICopyGrayList(otapkiRef);
CFRelease(otapkiRef);
if (NULL != grayListKeys)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
&& SecPVCIsAnchored(pvc));
if (!is_anchor) {
CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
if (dgst) {
if (CFSetContainsValue(grayListKeys, dgst)) {
SecPVCSetResultForced(pvc, kSecPolicyCheckGrayListedKey,
ix, kCFBooleanFalse, true);
}
CFRelease(dgst);
}
}
CFRelease(grayListKeys);
return pvc->result;
}
}
return true;
}
static bool SecPVCContainsPolicy(SecPVCRef pvc, CFStringRef searchOid, CFStringRef searchName, CFIndex *policyIX) {
if (!isString(searchName) && !isString(searchOid)) {
return false;
}
CFArrayRef policies = pvc->policies;
CFIndex ix, count = CFArrayGetCount(policies);
for (ix = 0; ix < count; ++ix) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
CFStringRef policyName = SecPolicyGetName(policy);
CFStringRef policyOid = SecPolicyGetOidString(policy);
if (searchOid && searchName && policyOid && policyName) {
if (CFEqual(searchOid, policyOid) &&
CFEqual(searchName, policyName)) {
if (policyIX) { *policyIX = ix; }
return true;
}
}
if (!searchName && searchOid && policyOid) {
if (CFEqual(searchOid, policyOid)) {
if (policyIX) { *policyIX = ix; }
return true;
}
}
if (!searchOid && searchName && policyName) {
if (CFEqual(searchName, policyName)) {
if (policyIX) { *policyIX = ix; }
return true;
}
}
}
return false;
}
static bool SecPVCContainsString(SecPVCRef pvc, CFIndex policyIX, CFStringRef stringValue) {
if (!isString(stringValue)) {
return false;
}
bool result = false;
CFStringRef tmpStringValue = NULL;
if (CFStringGetCharacterAtIndex(stringValue, CFStringGetLength(stringValue) -1) == (UniChar)0x0000) {
tmpStringValue = CFStringCreateTruncatedCopy(stringValue, CFStringGetLength(stringValue) - 1);
} else {
tmpStringValue = CFStringCreateCopy(NULL, stringValue);
}
if (policyIX >= 0 && policyIX < CFArrayGetCount(pvc->policies)) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, policyIX);
CFStringRef policyString = NULL;
policyString = CFDictionaryGetValue(policy->_options, kSecPolicyCheckSSLHostname);
if (!policyString) {
policyString = CFDictionaryGetValue(policy->_options, kSecPolicyCheckEmail);
}
if (policyString && (CFStringCompare(tmpStringValue, policyString, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
result = true;
goto out;
}
CFArrayRef policyStrings = NULL;
policyStrings = CFDictionaryGetValue(policy->_options, kSecPolicyCheckEAPTrustedServerNames);
if (policyStrings && CFArrayContainsValue(policyStrings,
CFRangeMake(0, CFArrayGetCount(policyStrings)),
tmpStringValue)) {
result = true;
goto out;
}
}
out:
CFReleaseNull(tmpStringValue);
return result;
}
static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber) {
uint32_t ourTSKeyUsage = 0;
uint32_t keyUsage = 0;
if (keyUsageNumber &&
CFNumberGetValue(keyUsageNumber, kCFNumberSInt32Type, &keyUsage)) {
if (keyUsage & kSecKeyUsageDigitalSignature) {
ourTSKeyUsage |= kSecTrustSettingsKeyUseSignature;
}
if (keyUsage & kSecKeyUsageDataEncipherment) {
ourTSKeyUsage |= kSecTrustSettingsKeyUseEnDecryptData;
}
if (keyUsage & kSecKeyUsageKeyEncipherment) {
ourTSKeyUsage |= kSecTrustSettingsKeyUseEnDecryptKey;
}
if (keyUsage & kSecKeyUsageKeyAgreement) {
ourTSKeyUsage |= kSecTrustSettingsKeyUseKeyExchange;
}
if (keyUsage == kSecKeyUsageAll) {
ourTSKeyUsage = kSecTrustSettingsKeyUseAny;
}
}
return ourTSKeyUsage;
}
static uint32_t ts_key_usage_for_policy(SecPolicyRef policy) {
uint32_t ourTSKeyUsage = 0;
CFTypeRef policyKeyUsageType = NULL;
policyKeyUsageType = (CFTypeRef)CFDictionaryGetValue(policy->_options, kSecPolicyCheckKeyUsage);
if (isArray(policyKeyUsageType)) {
CFIndex ix, count = CFArrayGetCount(policyKeyUsageType);
for (ix = 0; ix < count; ix++) {
CFNumberRef policyKeyUsageNumber = NULL;
policyKeyUsageNumber = (CFNumberRef)CFArrayGetValueAtIndex(policyKeyUsageType, ix);
ourTSKeyUsage |= ts_key_usage_for_kuNumber(policyKeyUsageNumber);
}
} else if (isNumber(policyKeyUsageType)) {
ourTSKeyUsage |= ts_key_usage_for_kuNumber(policyKeyUsageType);
}
return ourTSKeyUsage;
}
static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc,
SecCertificateRef certificate, CFIndex policyIX, CFNumberRef keyUsageNumber) {
int64_t keyUsageValue = 0;
uint32_t ourKeyUsage = 0;
if (!isNumber(keyUsageNumber) || !CFNumberGetValue(keyUsageNumber, kCFNumberSInt64Type, &keyUsageValue)) {
return false;
}
if (keyUsageValue == kSecTrustSettingsKeyUseAny) {
return true;
}
if (SecPVCContainsPolicy(pvc, kSecPolicyAppleOCSPSigner, NULL, NULL)) {
ourKeyUsage |= kSecTrustSettingsKeyUseSignRevocation;
}
CFIndex pathIndex = -1;
if (pvc->path) {
pathIndex = SecCertificatePathGetIndexOfCertificate(pvc->path, certificate);
} else {
pathIndex = 0;
}
if (pathIndex != 0) {
ourKeyUsage |= kSecTrustSettingsKeyUseSignCert;
}
if (policyIX >= 0 && policyIX < CFArrayGetCount(pvc->policies)) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, policyIX);
ourKeyUsage |= ts_key_usage_for_policy(policy);
} else {
CFIndex ix, count = CFArrayGetCount(pvc->policies);
for (ix = 0; ix < count; ix++) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, ix);
ourKeyUsage |= ts_key_usage_for_policy(policy);
}
}
if (ourKeyUsage == (uint32_t)(keyUsageValue & 0x00ffffffff)) {
return true;
}
return false;
}
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
typedef struct CF_BRIDGED_TYPE(id) OpaqueSecTrustedApplicationRef *SecTrustedApplicationRef;
#include <Security/SecTrustedApplicationPriv.h>
#include <bsm/libbsm.h>
#include <libproc.h>
static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken, CFTypeRef appRef) {
bool result = false;
audit_token_t auditToken = {};
char path[MAXPATHLEN];
require(appRef && clientAuditToken, out);
require(CFGetTypeID(appRef) == SecTrustedApplicationGetTypeID(), out);
require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
require(proc_pidpath(audit_token_to_pid(auditToken), path, sizeof(path)) > 0, out);
if(errSecSuccess == SecTrustedApplicationValidateWithPath((SecTrustedApplicationRef)appRef, path)) {
result = true;
}
out:
return result;
}
#endif
static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint) {
CFStringRef policyOid = NULL, policyString = NULL, policyName = NULL;
CFNumberRef keyUsageNumber = NULL;
CFTypeRef trustedApplicationData = NULL;
bool policyMatch = false, policyStringMatch = false, applicationMatch = false , keyUsageMatch = false;
bool result = false;
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
SecPolicyRef policy = NULL;
policy = (SecPolicyRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicy);
policyOid = (policy) ? policy->_oid : NULL;
#else
policyOid = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicy);
#endif
policyName = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyName);
policyString = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyString);
keyUsageNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsKeyUsage);
CFIndex policyIX = -1;
policyMatch = SecPVCContainsPolicy(pvc, policyOid, policyName, &policyIX);
policyStringMatch = SecPVCContainsString(pvc, policyIX, policyString);
keyUsageMatch = SecPVCContainsTrustSettingsKeyUsage(pvc, certificate, policyIX, keyUsageNumber);
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
trustedApplicationData = CFDictionaryGetValue(constraint, kSecTrustSettingsApplication);
CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(pvc->builder);
applicationMatch = SecPVCCallerIsApplication(clientAuditToken, trustedApplicationData);
CFReleaseNull(clientAuditToken);
#else
if(CFDictionaryContainsKey(constraint, kSecTrustSettingsApplication)) {
secerror("kSecTrustSettingsApplication is not yet supported on this platform");
}
#endif
if (((!policyOid && !policyName) || policyMatch) &&
(!policyString || policyStringMatch) &&
(!trustedApplicationData || applicationMatch) &&
(!keyUsageNumber || keyUsageMatch)) {
result = true;
}
return result;
}
SecTrustSettingsResult SecPVCGetTrustSettingsResult(SecPVCRef pvc, SecCertificateRef certificate, CFArrayRef constraints) {
SecTrustSettingsResult result = kSecTrustSettingsResultInvalid;
CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints);
for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) {
CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX);
if (!isDictionary(constraint)) {
continue;
}
CFNumberRef resultNumber = NULL;
resultNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsResult);
uint32_t resultValue = kSecTrustSettingsResultInvalid;
if (!isNumber(resultNumber) || !CFNumberGetValue(resultNumber, kCFNumberSInt32Type, &resultValue)) {
resultValue = kSecTrustSettingsResultTrustRoot;
}
if (SecPVCMeetsConstraint(pvc, certificate, constraint)) {
result = resultValue;
break;
}
}
return result;
}
bool SecPVCCheckUsageConstraints(SecPVCRef pvc) {
bool shouldDeny = false;
CFIndex certIX, certCount = SecCertificatePathGetCount(pvc->path);
for (certIX = 0; certIX < certCount; certIX++) {
CFArrayRef constraints = SecCertificatePathGetUsageConstraintsAtIndex(pvc->path, certIX);
SecCertificateRef cert = SecCertificatePathGetCertificateAtIndex(pvc->path, certIX);
SecTrustSettingsResult result = SecPVCGetTrustSettingsResult(pvc, cert, constraints);
if (result == kSecTrustSettingsResultDeny) {
SecPVCSetResultForced(pvc, kSecPolicyCheckUsageConstraints, certIX, kCFBooleanFalse, true);
shouldDeny = true;
}
}
return shouldDeny;
}
bool SecPVCPathChecks(SecPVCRef pvc) {
secdebug("policy", "begin path: %@", pvc->path);
bool completed = true;
pvc->policyIX = 0;
SecPolicyCheckIdLinkage(pvc, kSecPolicyCheckIdLinkage);
if (pvc->result || pvc->details) {
SecPolicyCheckBasicCertificateProcessing(pvc,
kSecPolicyCheckBasicCertificateProcessing);
}
CFArrayRef policies = pvc->policies;
CFIndex count = CFArrayGetCount(policies);
for (; pvc->policyIX < count; ++pvc->policyIX) {
pvc->callbacks = gSecPolicyPathCallbacks;
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
if (!pvc->result && !pvc->details)
return completed;
}
(void)SecPVCCheckUsageConstraints(pvc);
pvc->is_ev = false;
if ((pvc->result || pvc->details) && pvc->optionally_ev) {
bool pre_ev_check_result = pvc->result;
SecPolicyCheckEV(pvc, kSecPolicyCheckExtendedValidation);
pvc->is_ev = pvc->result;
pvc->result = pre_ev_check_result;
}
completed = SecPVCCheckRevocation(pvc);
if (pvc->result || pvc->details) {
SecPolicyCheckCT(pvc, kSecPolicyCheckCertificateTransparency);
}
if (pvc->is_ev && !pvc->is_ct) {
pvc->is_ct_whitelisted = SecPVCCheckCTWhiteListedLeaf(pvc);
} else {
pvc->is_ct_whitelisted = false;
}
secdebug("policy", "end %strusted completed: %d path: %@",
(pvc->result ? "" : "not "), completed, pvc->path);
return completed;
}
CFAbsoluteTime SecPVCGetEarliestNextUpdate(SecPVCRef pvc) {
CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
CFAbsoluteTime enu = NULL_TIME;
if (certCount <= 1 || !pvc->rvcs) {
return enu;
}
certCount--;
for (certIX = 0; certIX < certCount; ++certIX) {
SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX];
CFAbsoluteTime thisCertNextUpdate = SecRVCGetEarliestNextUpdate(rvc);
if (thisCertNextUpdate == 0) {
if (certIX > 0) {
SecCertificateRef cert =
SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef ocspResponders = NULL;
ocspResponders = SecCertificateGetOCSPResponders(cert);
#if ENABLE_CRLS
CFArrayRef crlDPs = NULL;
crlDPs = SecCertificateGetCRLDistributionPoints(cert);
#endif
if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0)
#if ENABLE_CRLS
&& (!crlDPs || CFArrayGetCount(crlDPs) == 0)
#endif
) {
continue;
}
}
secdebug("rvc", "revocation checking soft failure for cert: %ld",
certIX);
enu = thisCertNextUpdate;
break;
}
if (enu == 0 || thisCertNextUpdate < enu) {
enu = thisCertNextUpdate;
}
}
secdebug("rvc", "revocation valid until: %lg", enu);
return enu;
}