#include <securityd/SecPolicyServer.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecPolicyPriv.h>
#include <utilities/SecIOFormat.h>
#include <securityd/asynchttp.h>
#include <securityd/policytree.h>
#include <CoreFoundation/CFTimeZone.h>
#include <wctype.h>
#include <libDER/oids.h>
#include <CoreFoundation/CFNumber.h>
#include <Security/SecCertificateInternal.h>
#include <AssertMacros.h>
#include <utilities/debugging.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/SecInternal.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 "OTATrustUtilities.h"
#define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
#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())
{
ocspdErrorLog("EVRoot.plist has non array value");
result = NULL;
}
CFRelease(oid);
}
return result;
}
static bool SecPolicyIsEVPolicy(const DERItem *policyOID) {
return SecPolicyAnchorDigestsForEVPolicy(policyOID);
}
static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate,
policy_set_t valid_policies) {
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_quiet(good_ev_anchor, notEV);
CFAbsoluteTime october2006 = 178761600;
if (SecCertificateVersion(certificate) >= 3
&& SecCertificateNotValidBefore(certificate) >= october2006) {
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
require_quiet(bc && bc->isCA == true, notEV);
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
== (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
}
CFAbsoluteTime jan2011 = 315532800;
if (SecCertificateNotValidBefore(certificate) < jan2011) {
} else {
}
return true;
notEV:
return false;
}
static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate) {
const SecCECertificatePolicies *cp;
cp = SecCertificateGetCertificatePolicies(certificate);
require_quiet(cp && cp->numPolicies > 0, notEV);
#if 0
CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
require_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV);
#endif
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
require_quiet(bc && bc->isCA == true, notEV);
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
== (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
CFAbsoluteTime jan2011 = 315532800;
if (SecCertificateNotValidBefore(certificate) < jan2011) {
} else {
}
return true;
notEV:
return false;
}
bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate) {
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);
#if 0
CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
if (cdp) {
require_quiet(CFArrayGetCount(cdp) > 0, notEV);
} else {
CFArrayRef or = SecCertificateGetOCSPResponders(certificate);
require_quiet(or && CFArrayGetCount(or) > 0, notEV);
}
#endif
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
if (bc) {
require_quiet(bc->isCA == false, notEV);
}
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
if (ku) {
require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV);
}
#if 0
SecCertificateCopyExtendedKeyUsage(certificate);
#endif
CFAbsoluteTime jan2011 = 315532800;
if (SecCertificateNotValidAfter(certificate) < jan2011) {
} else {
}
return true;
notEV:
return false;
}
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 bool keyusage_allows(SecKeyUsage keyUsage, CFTypeRef xku) {
if (!xku || CFGetTypeID(xku) != CFNumberGetTypeID())
return false;
SInt32 dku;
CFNumberGetValue((CFNumberRef)xku, kCFNumberSInt32Type, &dku);
SecKeyUsage ku = (SecKeyUsage)dku;
return (keyUsage & ku) == ku;
}
static void SecPolicyCheckKeyUsage(SecPVCRef pvc,
CFStringRef key) {
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
SecKeyUsage keyUsage = SecCertificateGetKeyUsage(leaf);
bool match = false;
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef xku = CFDictionaryGetValue(policy->_options, key);
if (isArray(xku)) {
CFIndex ix, count = CFArrayGetCount(xku);
for (ix = 0; ix < count; ++ix) {
CFTypeRef ku = CFArrayGetValueAtIndex(xku, ix);
if (keyusage_allows(keyUsage, ku)) {
match = true;
break;
}
}
} else {
match = keyusage_allows(keyUsage, xku);
}
if (!match) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage,
CFTypeRef xeku) {
if (!xeku || CFGetTypeID(xeku) != CFDataGetTypeID())
return false;
if (extendedKeyUsage) {
CFRange all = { 0, CFArrayGetCount(extendedKeyUsage) };
return CFArrayContainsValue(extendedKeyUsage, all, xeku);
} else {
return CFDataGetLength((CFDataRef)xeku) == 0;
}
}
static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc, CFStringRef key) {
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
CFArrayRef leafExtendedKeyUsage = SecCertificateCopyExtendedKeyUsage(leaf);
bool match = false;
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef xeku = CFDictionaryGetValue(policy->_options, key);
if (isArray(xeku)) {
CFIndex ix, count = CFArrayGetCount(xeku);
for (ix = 0; ix < count; ix++) {
CFTypeRef eku = CFArrayGetValueAtIndex(xeku, ix);
if (extendedkeyusage_allows(leafExtendedKeyUsage, eku)) {
match = true;
break;
}
}
} else {
match = extendedkeyusage_allows(leafExtendedKeyUsage, xeku);
}
CFReleaseSafe(leafExtendedKeyUsage);
if (!match) {
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 SecPolicyCheckBasicContraints(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 SecDNSMatch(CFStringRef hostname, CFStringRef servername) {
CFStringInlineBuffer hbuf, sbuf;
CFIndex hix, six,
hlength = CFStringGetLength(hostname),
slength = CFStringGetLength(servername);
CFRange hrange = { 0, hlength }, srange = { 0, slength };
CFStringInitInlineBuffer(hostname, &hbuf, hrange);
CFStringInitInlineBuffer(servername, &sbuf, srange);
for (hix = six = 0; six < slength; ++six) {
UniChar hch, sch = CFStringGetCharacterFromInlineBuffer(&sbuf, six);
if (sch == '*') {
if (six + 1 >= slength) {
do {
if (hix >= hlength) {
return true;
}
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
} while (hch != '.');
return hix == hlength;
}
sch = CFStringGetCharacterFromInlineBuffer(&sbuf, ++six);
if (sch != '.') {
return false;
}
do {
if (hix >= hlength)
return false;
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
} while (hch != '.');
} else {
if (hix >= hlength)
return false;
hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
if (towlower(hch) != towlower(sch))
return false;
}
}
if (hix < hlength) {
if (hix + 1 == hlength &&
CFStringGetCharacterFromInlineBuffer(&hbuf, hix) == '.') {
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 = false;
CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf);
if (dnsNames) {
CFIndex ix, count = CFArrayGetCount(dnsNames);
for (ix = 0; ix < count; ++ix) {
CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
if (SecDNSMatch(hostName, dns)) {
dnsMatch = true;
break;
}
}
CFRelease(dnsNames);
}
if (!dnsMatch) {
CFArrayRef ipAddresses = SecCertificateCopyIPAddresses(leaf);
if (ipAddresses) {
CFIndex ix, count = CFArrayGetCount(ipAddresses);
for (ix = 0; ix < count; ++ix) {
CFStringRef ipAddress = (CFStringRef)CFArrayGetValueAtIndex(ipAddresses, ix);
if (!CFStringCompare(hostName, ipAddress, kCFCompareCaseInsensitive)) {
dnsMatch = true;
break;
}
}
CFRelease(ipAddresses);
}
}
if (!dnsMatch) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
if ((dnsMatch || pvc->details)
&& SecPolicySubscriberCertificateCouldBeEV(leaf)) {
secdebug("policy", "enabling optionally_ev");
pvc->optionally_ev = true;
#if 0
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(leaf);
if (ocspResponders) {
SecPVCSetCheckRevocation(pvc);
}
#endif
}
}
static void SecPolicyCheckEmail(SecPVCRef pvc, CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef email = (CFStringRef)CFDictionaryGetValue(policy->_options, key);
bool match = false;
if (!isString(email)) {
return;
}
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
CFArrayRef addrs = SecCertificateCopyRFC822Names(leaf);
if (addrs) {
CFIndex ix, count = CFArrayGetCount(addrs);
for (ix = 0; ix < count; ++ix) {
CFStringRef addr = (CFStringRef)CFArrayGetValueAtIndex(addrs, ix);
if (!CFStringCompare(email, addr, kCFCompareCaseInsensitive)) {
match = true;
break;
}
}
CFRelease(addrs);
}
if (!match) {
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;
}
CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
!CFEqual(commonName, CFArrayGetValueAtIndex(commonNames, 0))) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFReleaseSafe(commonNames);
}
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;
}
CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
!CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFReleaseSafe(commonNames);
}
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;
}
CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
!CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames, 0), prefix)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFReleaseSafe(commonNames);
}
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;
}
CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
if (!commonNames || CFArrayGetCount(commonNames) != 1) {
CFStringRef cert_common_name = CFArrayGetValueAtIndex(commonNames, 0);
CFStringRef test_common_name = common_name ?
CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("TEST %@ TEST"), common_name) :
NULL;
if (!CFEqual(common_name, cert_common_name) &&
(!test_common_name || !CFEqual(test_common_name, cert_common_name)))
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
CFReleaseSafe(test_common_name);
}
CFReleaseSafe(commonNames);
}
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;
}
CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
if (SecCertificateNotValidBefore(cert) <= at) {
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 void SecPolicyCheckAnchorSHA1(SecPVCRef pvc,
CFStringRef key) {
CFIndex count = SecPVCGetCertificateCount(pvc);
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
bool foundMatch = false;
if (isData(value))
foundMatch = CFEqual(anchorSHA1, value);
else if (isArray(value))
foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), anchorSHA1);
else {
assert(false);
}
if (!foundMatch)
if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, 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;
}
CFArrayRef organization = SecCertificateCopyOrganization(cert);
if (!organization || CFArrayGetCount(organization) != 1 ||
!CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFReleaseSafe(organization);
}
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;
}
CFArrayRef organizationalUnit = SecCertificateCopyOrganizationalUnit(cert);
if (!organizationalUnit || CFArrayGetCount(organizationalUnit) != 1 ||
!CFEqual(orgUnit, CFArrayGetValueAtIndex(organizationalUnit, 0))) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
CFReleaseSafe(organizationalUnit);
}
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;
}
CFIndex tsnCount = CFArrayGetCount(trustedServerNames);
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
bool dnsMatch = false;
CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf);
if (dnsNames) {
CFIndex ix, count = CFArrayGetCount(dnsNames);
for (ix = 0; !dnsMatch && ix < count; ++ix) {
CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
CFIndex tix;
for (tix = 0; tix < tsnCount; ++tix) {
CFStringRef serverName =
(CFStringRef)CFArrayGetValueAtIndex(trustedServerNames, tix);
if (!isString(serverName)) {
CFReleaseSafe(dnsNames);
return;
}
if (SecDNSMatch(dns, serverName)) {
dnsMatch = true;
break;
}
}
}
CFRelease(dnsNames);
}
if (!dnsMatch) {
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)))
{
CFDataRef serial = SecCertificateCopySerialNumber(cert);
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 (value && SecCertificateHasMarkerExtension(cert, value))
return;
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);
}
#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 0
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 (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;
#if POLICY_SUBTREES
root_name_t initial_permitted_subtrees = NULL;
root_name_t initial_excluded_subtrees = NULL;
#endif
pvc->valid_policy_tree = policy_tree_create(&oidAnyPolicy, NULL);
#if POLICY_SUBTREES
root_name_t permitted_subtrees = initial_permitted_subtrees;
root_name_t excluded_subtrees = initial_excluded_subtrees;
#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))
return;
}
#endif
#if 0
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if (ocspResponders) {
SecPVCSetCheckRevocation(pvc);
}
#endif
#if POLICY_SUBTREES
if (!is_self_issued || i == n) {
}
#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) {
if (!SecPVCSetResultForced(pvc, key , n - i, kCFBooleanFalse, true))
return;
}
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))
return;
}
}
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
#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, kSecPolicyCheckBasicContraints,
n - i, kCFBooleanFalse))
return;
}
#endif
if (!is_self_issued) {
if (max_path_length > 0) {
max_path_length--;
} else {
if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints,
n - i, kCFBooleanFalse))
return;
}
}
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))
return;
}
#endif
if (SecCertificateHasUnknownCriticalExtension(cert)) {
if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions,
n - i, kCFBooleanFalse))
return;
}
}
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))
return;
}
if (pvc->valid_policy_tree) {
#if !defined(NDEBUG)
policy_tree_dump(pvc->valid_policy_tree);
#endif
}
if (!pvc->valid_policy_tree && explicit_policy == 0) {
if (!SecPVCSetResultForced(pvc, key , 0, kCFBooleanFalse, true))
return;
}
}
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;
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)) {
secdebug("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)) {
secdebug("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) {
secdebug("ev", "valid_policies set is empty: chain not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
return;
}
}
}
policy_set_free(valid_policies);
}
static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc, CFStringRef key)
{
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
DERItem key_value;
key_value.data = NULL;
key_value.length = 0;
if (CFGetTypeID(value) == CFDataGetTypeID())
{
CFDataRef key_data = (CFDataRef)value;
key_value.data = (DERByte *)CFDataGetBytePtr(key_data);
key_value.length = (DERSize)CFDataGetLength(key_data);
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;
}
}
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
static void SecPolicyCheckRevocation(SecPVCRef pvc,
CFStringRef key) {
SecPVCSetCheckRevocation(pvc);
}
static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc,
CFStringRef key) {
SecPathBuilderSetCanAccessNetwork(pvc->builder, false);
}
struct OpaqueSecRVC {
asynchttp_t http;
SecPVCRef pvc;
SecOCSPRequestRef ocspRequest;
CFIndex certIX;
CFIndex responderIX;
CFURLRef responder;
CFAbsoluteTime nextUpdate;
bool done;
};
typedef struct OpaqueSecRVC *SecRVCRef;
static void SecRVCDelete(SecRVCRef rvc) {
secdebug("alloc", "%p", rvc);
asynchttp_free(&rvc->http);
SecOCSPRequestFinalize(rvc->ocspRequest);
}
static CFURLRef SecRVCGetNextResponder(SecRVCRef 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 SecRVCFetchNext(SecRVCRef rvc) {
while ((rvc->responder = SecRVCGetNextResponder(rvc))) {
CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest);
if (!request)
goto errOut;
if (!asyncHttpPost(rvc->responder, request, &rvc->http)) {
return false;
}
}
errOut:
rvc->done = true;
return true;
}
static bool SecOCSPSingleResponseProccess(SecOCSPSingleResponseRef this,
SecRVCRef rvc) {
bool proccessed;
switch (this->certStatus) {
case CS_Good:
secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
rvc->nextUpdate = this->nextUpdate;
proccessed = 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);
CFRelease(cfreason);
proccessed = true;
break;
case CS_Unknown:
secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
proccessed = false;
break;
default:
secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
(int)this->certStatus, rvc->certIX);
proccessed = false;
break;
}
return proccessed;
}
static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecRVCRef rvc) {
bool trusted;
SecCertificatePathRef issuer = SecCertificatePathCopyFromParent(rvc->pvc->path, rvc->certIX + 1);
SecCertificatePathRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
CFRelease(issuer);
if (signer) {
if (signer == issuer) {
secdebug("ocsp", "ocsp responder: %@ response signed by issuer",
rvc->responder);
trusted = true;
} else {
secdebug("ocsp",
"ocsp responder: %@ response signed by cert issued by issuer",
rvc->responder);
const void *ocspSigner = SecPolicyCreateOCSPSigner();
CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
&ocspSigner, 1, &kCFTypeArrayCallBacks);
CFRelease(ocspSigner);
CFAbsoluteTime verifyTime = SecOCSPResponseVerifyTime(ocspResponse);
struct OpaqueSecPVC ospvc;
SecPVCInit(&ospvc, rvc->pvc->builder, policies, verifyTime);
CFRelease(policies);
SecPVCSetPath(&ospvc, signer, NULL);
SecPVCLeafChecks(&ospvc);
if (ospvc.result) {
bool completed = SecPVCPathChecks(&ospvc);
if (!completed) {
ocspdErrorLog("SecPVCPathChecks unexpectedly started "
"background job!");
}
}
if (ospvc.result) {
secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
rvc->responder);
trusted = true;
} else {
ocspdErrorLog("ocsp response signed by certificate which "
"does not satisfy ocspSigner policy");
trusted = false;
}
SecPVCDelete(&ospvc);
}
CFRelease(signer);
} else {
secdebug("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
return trusted;
}
static void SecOCSPFetchCompleted(asynchttp_t *http, CFTimeInterval maxAge) {
SecRVCRef rvc = (SecRVCRef)http->info;
SecPVCRef pvc = rvc->pvc;
SecOCSPResponseRef ocspResponse = NULL;
if (http->response) {
CFDataRef data = CFHTTPMessageCopyBody(http->response);
if (data) {
ocspResponse = SecOCSPResponseCreate(data, maxAge);
CFRelease(data);
}
}
if (ocspResponse) {
SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
if (orStatus == kSecOCSPSuccess) {
SecOCSPSingleResponseRef sr =
SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest);
if (!sr) {
secdebug("ocsp",
"ocsp responder: %@ did not include status of requested cert",
rvc->responder);
} else {
if (SecOCSPResponseVerify(ocspResponse, rvc)) {
secdebug("ocsp","responder: %@ sent proper response",
rvc->responder);
if (SecOCSPSingleResponseProccess(sr, rvc)) {
if (rvc->nextUpdate == 0) {
rvc->nextUpdate =
SecOCSPResponseGetExpirationTime(ocspResponse);
}
SecOCSPCacheAddResponse(ocspResponse, rvc->responder);
rvc->done = true;
}
}
SecOCSPSingleResponseDestroy(sr);
}
} else {
secdebug("ocsp", "responder: %@ returned status: %d",
rvc->responder, orStatus);
#if 0
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckRevocation,
rvc->certIX, kCFBooleanFalse, true))
return;
#endif
}
SecOCSPResponseFinalize(ocspResponse);
}
if (!rvc->done) {
asynchttp_free(http);
SecRVCFetchNext(rvc);
}
if (rvc->done) {
SecRVCDelete(rvc);
if (!--pvc->asyncJobCount) {
SecPathBuilderStep(pvc->builder);
}
}
}
static void SecRVCInit(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) {
secdebug("alloc", "%p", rvc);
rvc->pvc = pvc;
rvc->certIX = certIX;
rvc->http.queue = SecPathBuilderGetQueue(pvc->builder);
rvc->http.completed = SecOCSPFetchCompleted;
rvc->http.info = rvc;
rvc->ocspRequest = NULL;
rvc->responderIX = 0;
rvc->responder = NULL;
rvc->nextUpdate = 0;
rvc->done = false;
}
static bool SecPVCCheckRevocation(SecPVCRef pvc) {
secdebug("ocsp", "checking revocation");
CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
bool completed = true;
if (certCount <= 1) {
return completed;
}
if (!SecPVCIsAnchored(pvc)) {
return completed;
}
certCount--;
#if 0
CFURLRef localResponder = NULL;
bool genNonce = false;
bool requireRespNonce = false;
bool cacheReadDisable = false;
bool cacheWriteDisable = false;
#endif
if (pvc->rvcs) {
secdebug("ocsp", "Not rechecking revocation");
return completed;
}
pvc->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount);
#if 0
for (certIX = 0; certIX < certCount; ++certIX) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if (ocspResponders) {
CFDataRef ocspResponse = SecOCSPCacheCopyMatching(SecCertIDRef certID, NULL);
if (decodeOCSPResponse(ocspResp)) {
secdebug("ocsp", "response ok: %@", ocspResp);
} else {
secdebug("ocsp", "response bad: %@", ocspResp);
if (!SecPVCSetResultForced(pvc, key, ix, kCFBooleanFalse, true))
return completed;
}
CFReleaseSafe(ocspResp);
} else {
CFArrayRef distributionPoints = SecCertificateGetCRLDistributionPoints(cert);
if (distributionPoints) {
}
}
}
#endif
pvc->asyncJobCount = (unsigned int) certCount;
for (certIX = 0; certIX < certCount; ++certIX) {
secdebug("ocsp", "checking revocation for cert: %ld", certIX);
SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX];
SecRVCInit(rvc, pvc, certIX);
if (rvc->done)
continue;
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc,
rvc->certIX);
SecCertificateRef issuer = SecPVCGetCertificateAtIndex(rvc->pvc,
rvc->certIX + 1);
rvc->ocspRequest = SecOCSPRequestCreate(cert, issuer);
SecOCSPResponseRef ocspResponse;
ocspResponse = SecOCSPCacheCopyMatching(rvc->ocspRequest, NULL);
if (ocspResponse) {
SecOCSPSingleResponseRef sr =
SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest);
if (!sr) {
secdebug("ocsp", "cached ocsp response did not include status"
" of requested cert");
} else {
if (SecOCSPResponseVerify(ocspResponse, rvc)) {
secdebug("ocsp","cached response still has valid signature");
if (SecOCSPSingleResponseProccess(sr, rvc)) {
CFAbsoluteTime expTime =
SecOCSPResponseGetExpirationTime(ocspResponse);
if (rvc->nextUpdate == 0 || expTime < rvc->nextUpdate)
rvc->nextUpdate = expTime;
rvc->done = true;
}
}
SecOCSPSingleResponseDestroy(sr);
}
SecOCSPResponseFinalize(ocspResponse);
}
bool fetch_done = true;
if (rvc->done || !SecPathBuilderCanAccessNetwork(pvc->builder) ||
(fetch_done = SecRVCFetchNext(rvc))) {
SecRVCDelete(rvc);
pvc->asyncJobCount--;
} else if (!fetch_done) {
completed = false;
}
}
return completed;
}
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,
kSecPolicyCheckBasicContraints, SecPolicyCheckBasicContraints);
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(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectOrganization,
SecPolicyCheckSubjectOrganization);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectOrganizationalUnit,
SecPolicyCheckSubjectOrganizationalUnit);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckEAPTrustedServerNames,
SecPolicyCheckEAPTrustedServerNames);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckSubjectCommonNameTEST,
SecPolicyCheckSubjectCommonNameTEST);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckRevocation,
SecPolicyCheckRevocation);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckNoNetworkAccess,
SecPolicyCheckNoNetworkAccess);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckBlackListedLeaf,
SecPolicyCheckBlackListedLeaf);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckGrayListedLeaf,
SecPolicyCheckGrayListedLeaf);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckLeafMarkerOid,
SecPolicyCheckLeafMarkerOid);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIntermediateMarkerOid,
SecPolicyCheckIntermediateMarkerOid);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckCertificatePolicy,
SecPolicyCheckCertificatePolicyOid);
}
static SecPolicyRef SecPolicyCreateWithArray(CFArrayRef array) {
SecPolicyRef policy = NULL;
require_quiet(array && CFArrayGetCount(array) == 2, errOut);
CFStringRef oid = (CFStringRef)CFArrayGetValueAtIndex(array, 0);
require_quiet(isString(oid), errOut);
CFDictionaryRef options = (CFDictionaryRef)CFArrayGetValueAtIndex(array, 1);
require_quiet(isDictionary(options), errOut);
policy = SecPolicyCreate(oid, options);
errOut:
return policy;
}
static void deserializePolicy(const void *value, void *context) {
CFArrayRef policyArray = (CFArrayRef)value;
if (isArray(policyArray)) {
CFTypeRef deserializedPolicy = SecPolicyCreateWithArray(policyArray);
if (deserializedPolicy) {
CFArrayAppendValue((CFMutableArrayRef)context, deserializedPolicy);
CFRelease(deserializedPolicy);
}
}
}
CFArrayRef SecPolicyArrayDeserialize(CFArrayRef serializedPolicies) {
CFMutableArrayRef result = NULL;
require_quiet(isArray(serializedPolicies), errOut);
CFIndex count = CFArrayGetCount(serializedPolicies);
result = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
CFRange all_policies = { 0, count };
CFArrayApplyFunction(serializedPolicies, all_policies, deserializePolicy, result);
errOut:
return result;
}
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 = false;
pvc->optionally_ev = false;
pvc->is_ev = false;
pvc->result = true;
}
static void SecPVCDeleteRVCs(SecPVCRef pvc) {
secdebug("alloc", "%p", pvc);
if (pvc->rvcs) {
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);
}
void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathRef path,
CFArrayRef details) {
secdebug("policy", "%@", path);
if (pvc->path != path) {
SecPVCDeleteRVCs(pvc);
pvc->path = 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) {
pvc->check_revocation = true;
secdebug("ocsp", "deferred revocation checking enabled");
}
bool SecPVCIsAnchored(SecPVCRef pvc) {
return SecCertificatePathIsAnchored(pvc->path);
}
CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
return pvc->verifyTime;
}
bool SecPVCSetResultForced(SecPVCRef pvc,
CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
secdebug("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;
}
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 (is_anchor) {
} else {
const SecCEBasicConstraints *bc =
SecCertificateGetBasicConstraints(cert);
if (!bc || !bc->isCA) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicContraints,
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;
}
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;
}
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;
}
if (pvc->result && (pvc->is_ev || pvc->check_revocation)) {
completed = SecPVCCheckRevocation(pvc);
}
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 = 0;
if (certCount <= 1 || !pvc->rvcs) {
return enu;
}
certCount--;
for (certIX = 0; certIX < certCount; ++certIX) {
SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX];
if (rvc->nextUpdate == 0) {
if (certIX > 0) {
SecCertificateRef cert =
SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if (!ocspResponders || CFArrayGetCount(ocspResponders) == 0) {
continue;
}
}
secdebug("ocsp", "revocation checking soft failure for cert: %ld",
certIX);
enu = rvc->nextUpdate;
break;
}
if (enu == 0 || rvc->nextUpdate < enu) {
enu = rvc->nextUpdate;
}
#if 0
SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
CFAbsoluteTime nva = SecCertificateNotValidAfter(cert);
if (nva && (enu == 0 || nva < enu)
enu = nva;
#endif
}
secdebug("ocsp", "revocation valid until: %lg", enu);
return enu;
}