/*
- * Copyright (c) 2008-2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2008-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
/*
- * SecPolicyServer.c - Trust policies dealing with certificate revocation.
+ * SecPolicyServer.c - Engine for evaluating certificate paths against trust policies.
*/
#include <securityd/SecPolicyServer.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecPolicyPriv.h>
-#include <utilities/SecIOFormat.h>
+#include <Security/SecTask.h>
#include <securityd/asynchttp.h>
#include <securityd/policytree.h>
#include <securityd/nameconstraints.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/SecFramework.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecTrustPriv.h>
+#include <Security/SecTrustInternal.h>
+#include <Security/SecTrustSettingsPriv.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 <securityd/SecTrustLoggingServer.h>
+#include <securityd/SecRevocationServer.h>
+#include <securityd/SecCertificateServer.h>
+#include <securityd/SecCertificateSource.h>
+#include <securityd/SecOCSPResponse.h>
#include <utilities/array_size.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecAppleAnchorPriv.h>
#include "OTATrustUtilities.h"
+#include "personalization.h"
+#include <sys/codesign.h>
-#define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
+#if !TARGET_OS_IPHONE
+#include <Security/SecTaskPriv.h>
+#endif
/* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
#ifndef DUMP_OCSPRESPONSES
****************** SecPolicy object ********************
********************************************************/
+static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix);
+static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc);
+static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc);
+
static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL;
static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL;
result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid);
if (roots && CFGetTypeID(result) != CFArrayGetTypeID())
{
- ocspdErrorLog("EVRoot.plist has non array value");
+ secerror("EVRoot.plist has non array value");
result = NULL;
}
CFRelease(oid);
}
-static bool SecPolicyIsEVPolicy(const DERItem *policyOID) {
+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;
/* Ensure that this certificate is a valid anchor for one of the
certificate policy oids specified in the leaf. */
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
break;
}
}
- require_quiet(good_ev_anchor, notEV);
+ 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_quiet(bc && bc->isCA == true, notEV);
+ require_action_quiet(bc && bc->isCA == true, notEV,
+ secnotice("ev", "Anchor has invalid basic constraints"));
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
- == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
+ require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
+ == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV,
+ secnotice("ev", "Anchor has invalid key usage %u", ku));
}
- CFAbsoluteTime jan2011 = 315532800;
- if (SecCertificateNotValidBefore(certificate) < jan2011) {
- /* At least MD5, SHA-1 with RSA 2048 or ECC NIST P-256. */
- } else {
- /* At least SHA-1, SHA-256, SHA-384 or SHA-512 with RSA 2048 or
- ECC NIST P-256. */
- }
+ /* At least RSA 2048 or ECC NIST P-256. */
+ 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;
- return true;
notEV:
- return false;
+ 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_quiet(cp && cp->numPolicies > 0, notEV);
- /* SecCertificateGetCRLDistributionPoints() is a noop right now */
-#if 0
+ require_action_quiet(cp && cp->numPolicies > 0, notEV,
+ secnotice("ev", "SubCA missing certificate policies"));
CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
- require_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV);
-#endif
+ require_action_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV,
+ secnotice("ev", "SubCA missing CRLDP"));
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
- require_quiet(bc && bc->isCA == true, notEV);
+ require_action_quiet(bc && bc->isCA == true, notEV,
+ secnotice("ev", "SubCA has invalid basic constraints"));
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
- == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
- CFAbsoluteTime jan2011 = 315532800;
- if (SecCertificateNotValidBefore(certificate) < jan2011) {
- /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
- } else {
- /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
- ECC NIST P-256. */
- }
-
- return true;
-notEV:
- return false;
-}
-
-bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate) {
- /* 3. Subscriber Certificate. */
+ require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
+ == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV,
+ secnotice("ev", "SubCA has invalid key usage %u", ku));
- /* (a) certificate Policies */
- const SecCECertificatePolicies *cp;
- cp = SecCertificateGetCertificatePolicies(certificate);
- require_quiet(cp && cp->numPolicies > 0, notEV);
- /* Now find at least one policy in here that has a qualifierID of id-qt 2
- and a policyQualifier that is a URI to the CPS and an EV policy OID. */
- 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);
-
- /* SecCertificateGetCRLDistributionPoints() is a noop right now */
-#if 0
- /* (b) cRLDistributionPoint
- (c) authorityInformationAccess */
- CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
- if (cdp) {
- require_quiet(CFArrayGetCount(cdp) > 0, notEV);
+ /* 6.1.5 Key Sizes */
+ 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) {
+ /* At least RSA 1024 or ECC NIST P-256. */
+ 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 {
- CFArrayRef or = SecCertificateGetOCSPResponders(certificate);
- require_quiet(or && CFArrayGetCount(or) > 0, notEV);
- //CFArrayRef ci = SecCertificateGetCAIssuers(certificate);
- }
-#endif
-
- /* (d) basicConstraints
- If present, the cA field MUST be set false. */
- const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
- if (bc) {
- require_quiet(bc->isCA == false, notEV);
+ /* At least RSA 2028 or ECC NIST P-256. */
+ 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"));
}
- /* (e) keyUsage. */
- SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- if (ku) {
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV);
+ /* 7.1.3 Algorithm Object Identifiers */
+ CFAbsoluteTime jan2016 = 473299200;
+ if (SecCertificateNotValidBefore(certificate) > jan2016) {
+ /* SHA-2 only */
+ require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1,
+ notEV, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
}
-#if 0
- /* The EV Cert Spec errata specifies this, though this is a check for SSL
- not specifically EV. */
-
- /* (e) extKeyUsage
-
-Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
- SecCertificateCopyExtendedKeyUsage(certificate);
-#endif
-
- CFAbsoluteTime jan2011 = 315532800;
- if (SecCertificateNotValidAfter(certificate) < jan2011) {
- /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
- } else {
- /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
- ECC NIST P-256. */
- }
+ isEV = true;
- return true;
notEV:
- return false;
+ CFReleaseNull(rsaSize);
+ CFReleaseNull(ecSize);
+ CFReleaseNull(keySizes);
+ return isEV;
}
/********************************************************
}
}
-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) {
+ if (!SecPolicyCheckCertKeyUsage(leaf, xku)) {
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 {
- /* Certificate has no extended key usage, only a match if the policy
- contains a 0 length CFDataRef. */
- return CFDataGetLength((CFDataRef)xeku) == 0;
- }
-}
-
/* AUDIT[securityd](done):
policy->_options is a caller provided dictionary, only its cf type has
been checked.
*/
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) {
+ if (!SecPolicyCheckCertExtendedKeyUsage(leaf, xeku)){
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
}
}
#endif
-static void SecPolicyCheckBasicContraints(SecPVCRef pvc,
+static void SecPolicyCheckBasicConstraints(SecPVCRef pvc,
CFStringRef key) {
//SecPolicyCheckBasicContraintsCommon(pvc, key, false);
}
static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc,
CFStringRef key) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFTypeRef pvcValue = CFDictionaryGetValue(policy->_options, key);
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- /* If the certificate has a subject, or
- if it doesn't, and it's the leaf and not self signed,
- and also has a critical subjectAltName extension it's valid. */
- if (!SecCertificateHasSubject(cert)) {
- if (ix == 0 && count > 1) {
- if (!SecCertificateHasCriticalSubjectAltName(cert)) {
- /* Leaf certificate with empty subject does not have
- a critical subject alt name extension. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- } else {
- /* CA certificate has empty subject. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- }
+ if (!SecPolicyCheckCertNonEmptySubject(cert, pvcValue)) {
+ if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
+ return;
+ }
}
}
CFStringRef key) {
}
-/* Compare hostname suffix to domain name.
- This function does not process wildcards, and allows hostname to match
- any subdomain level of the provided domain.
-
- To match, the last domain length chars of hostname must equal domain,
- and the character immediately preceding domain in hostname (if any)
- must be a dot. This means that domain 'bar.com' will match hostname
- values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
-
- Characters in each string are converted to lowercase for the comparison.
- Trailing '.' characters in both names will be ignored.
-
- Returns true on match, else false.
- */
-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)) {
- /* trivial case with at least one empty name */
- return (hlength == dlength) ? true : false;
- }
-
- /* trim off trailing dots */
- hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hlength-1);
- dch = CFStringGetCharacterFromInlineBuffer(&dbuf, dlength-1);
- if(hch == '.') {
- hrange.length = --hlength;
- }
- if(dch == '.') {
- drange.length = --dlength;
- }
-
- /* trim off leading dot in suffix, if present */
- dch = CFStringGetCharacterFromInlineBuffer(&dbuf, 0);
- if((dlength > 0) && (dch == '.')) {
- drange.location++;
- drange.length = --dlength;
- }
-
- if(hlength < dlength) {
- return false;
- }
-
- /* perform case-insensitive comparison of domain suffix */
- 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;
- }
- }
-
- /* require a dot prior to domain suffix, unless hostname == domain */
- if(hlength > dlength) {
- hch = CFStringGetCharacterFromInlineBuffer(&hbuf, (hlength-(dlength+1)));
- if(hch != '.') {
- return false;
- }
- }
-
- return true;
-}
-
-/* Compare hostname, to a server name obtained from the server's cert
- Obtained from the SubjectAltName or the CommonName entry in the Subject.
- Limited wildcard checking is performed here as outlined in
-
- RFC 2818 Section 3.1. Server Identity
-
- [...] Names may contain the wildcard
- character * which is considered to match any single domain name
- component or component fragment. E.g., *.a.com matches foo.a.com but
- not bar.foo.a.com. f*.com matches foo.com but not bar.com.
- [...]
-
- Trailing '.' characters in the hostname will be ignored.
-
- Returns true on match, else false.
-
- RFC6125:
- */
-bool SecDNSMatch(CFStringRef hostname, CFStringRef servername) {
- CFStringInlineBuffer hbuf, sbuf;
- CFIndex hix, six, tix,
- hlength = CFStringGetLength(hostname),
- slength = CFStringGetLength(servername);
- CFRange hrange = { 0, hlength }, srange = { 0, slength };
- CFStringInitInlineBuffer(hostname, &hbuf, hrange);
- CFStringInitInlineBuffer(servername, &sbuf, srange);
- bool prevLabel=false;
-
- for (hix = six = 0; six < slength; ++six) {
- UniChar tch, hch, sch = CFStringGetCharacterFromInlineBuffer(&sbuf, six);
- if (sch == '*') {
- if (prevLabel) {
- /* RFC6125: No wildcard after a Previous Label */
- /* INVALID: Means we have something like foo.*.<public_suffix> */
- return false;
- }
-
- if (six + 1 >= slength) {
- /* Trailing '*' in servername, match until end of hostname or
- trailing '.'. */
- do {
- if (hix >= hlength) {
- /* If we reach the end of the hostname we have a
- match. */
- return true;
- }
- hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
- } while (hch != '.');
- /* We reached the end of servername and found a '.' in
- hostname. Return true if hostname has a single
- trailing '.' return false if there is anything after it. */
- return hix == hlength;
- }
-
- /* Grab the character after the '*'. */
- sch = CFStringGetCharacterFromInlineBuffer(&sbuf, ++six);
- if (sch != '.') {
- /* We have something of the form '*foo.com'. Or '**.com'
- We don't deal with that yet, since it might require
- backtracking. Also RFC 2818 doesn't seem to require it. */
- return false;
- }
-
- /* We're looking at the '.' after the '*' in something of the
- form 'foo*.com' or '*.com'. Match until next '.' in hostname. */
- if (prevLabel==false) { /* RFC6125: Check if *.<tld> */
- tix=six+1;
- do { /* Loop to end of servername */
- if (tix > slength)
- return false; /* Means we have something like *.com */
- tch = CFStringGetCharacterFromInlineBuffer(&sbuf, tix++);
- } while (tch != '.');
- if (tix > slength)
- return false; /* In case we have *.com. */
- }
-
- do {
- /* Since we're not at the end of servername yet (that case
- was handled above), running out of chars in hostname
- means we don't have a match. */
- if (hix >= hlength)
- return false;
- hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
- } while (hch != '.');
- } else {
- /* We're looking at a non wildcard character in the servername.
- If we reached the end of hostname, it's not a match. */
- if (hix >= hlength)
- return false;
-
- /* Otherwise make sure the hostname matches the character in the
- servername, case insensitively. */
- hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++);
- if (towlower(hch) != towlower(sch))
- return false;
- if (sch == '.')
- prevLabel=true; /* Set if a confirmed previous component */
- }
- }
-
- if (hix < hlength) {
- /* We reached the end of servername but we have one or more characters
- left to compare against in the hostname. */
- if (hix + 1 == hlength &&
- CFStringGetCharacterFromInlineBuffer(&hbuf, hix) == '.') {
- /* Hostname has a single trailing '.', we're ok with that. */
- return true;
- }
- /* Anything else is not a match. */
- 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
-};
-
-/* Check whether hostname is in a particular set of allowed domains.
- Returns true if OK, false if not allowed.
- */
-static bool SecPolicyCheckDomain(SecPVCRef pvc, CFStringRef hostname)
-{
- CFIndex count = SecPVCGetCertificateCount(pvc);
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
- CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
-
- /* is this chain anchored by kAppleCorpCASHA1? */
- CFDataRef corpSHA1 = CFDataCreateWithBytesNoCopy(NULL,
- kAppleCorpCASHA1, kSecPolicySHA1Size, kCFAllocatorNull);
- bool isCorpSHA1 = (corpSHA1 && CFEqual(anchorSHA1, corpSHA1));
- CFReleaseSafe(corpSHA1);
- if (isCorpSHA1) {
- /* limit hostname to specified domains */
- 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;
- }
- /* %%% other CA pinning checks TBA */
-
- return true;
-}
-
/* AUDIT[securityd](done):
policy->_options is a caller provided dictionary, only its cf type has
been checked.
}
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) {
- /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
- the values returned by SecCertificateCopyIPAddresses() instead. */
- 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);
- }
- }
+ bool dnsMatch = SecPolicyCheckCertSSLHostname(leaf, hostName);
if (!dnsMatch) {
/* Hostname mismatch or no hostnames found in certificate. */
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
- else if (!SecPolicyCheckDomain(pvc, hostName)) {
- /* Hostname match, but domain not allowed for this CA */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- if ((dnsMatch || pvc->details)
- && SecPolicySubscriberCertificateCouldBeEV(leaf)) {
- secdebug("policy", "enabling optionally_ev");
- pvc->optionally_ev = true;
- /* optionally_ev => check_revocation, so we don't enable revocation
- checking here, since we don't want it on for non EV ssl certs. */
-#if 0
- /* Check revocation status if the certificate asks for it (and we
- support it) currently we only support ocsp. */
- CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(leaf);
- if (ocspResponders) {
- SecPVCSetCheckRevocation(pvc);
- }
-#endif
- }
}
/* AUDIT[securityd](done):
static void SecPolicyCheckEmail(SecPVCRef pvc, CFStringRef key) {
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFStringRef email = (CFStringRef)CFDictionaryGetValue(policy->_options, key);
- bool match = false;
if (!isString(email)) {
/* We can't return an error here and making the evaluation fail
won't help much either. */
}
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) {
+ if (!SecPolicyCheckCertEmail(leaf, email)) {
/* Hostname mismatch or no hostnames found in certificate. */
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
won't help much either. */
return;
}
- CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
- if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
- !CFEqual(commonName, CFArrayGetValueAtIndex(commonNames, 0))) {
- /* Common Name mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(commonNames);
+ if (!SecPolicyCheckCertSubjectCommonName(cert, commonName)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
/* AUDIT[securityd](done):
won't help much either. */
return;
}
- CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
- if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
- !CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) {
- /* Common Name mismatch. */
+ if (!SecPolicyCheckCertSubjectCommonName(cert, common_name)) {
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(commonNames);
+ }
}
/* AUDIT[securityd](done):
won't help much either. */
return;
}
- CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
- if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
- !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames, 0), prefix)) {
- /* Common Name prefix mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(commonNames);
+ if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert, prefix)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
/* AUDIT[securityd](done):
won't help much either. */
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)))
- /* Common Name mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- CFReleaseSafe(test_common_name);
- }
- CFReleaseSafe(commonNames);
+ if (!SecPolicyCheckCertSubjectCommonNameTEST(cert, common_name)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
/* AUDIT[securityd](done):
won't help much either. */
return;
}
- CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
- if (SecCertificateNotValidBefore(cert) <= at) {
- /* Leaf certificate has not valid before that is too old. */
+ if (!SecPolicyCheckCertNotValidBefore(cert, date)) {
if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
return;
}
}
}
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex count = SecPVCGetCertificateCount(pvc);
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
+static bool isDigestInPolicy(SecPVCRef pvc, CFStringRef key, CFDataRef digest) {
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
- CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
bool foundMatch = false;
-
if (isData(value))
- foundMatch = CFEqual(anchorSHA1, value);
+ foundMatch = CFEqual(digest, value);
else if (isArray(value))
- foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), anchorSHA1);
+ foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), digest);
else {
/* @@@ We only support Data and Array but we can't return an error here so.
- we let the evaluation fail (not much help) and assert in debug. */
+ we let the evaluation fail (not much help) and assert in debug. */
assert(false);
}
- if (!foundMatch)
- if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, 0, kCFBooleanFalse))
+ 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;
+}
+
+
+/* AUDIT[securityd](done):
+ policy->_options is a caller provided dictionary, only its cf type has
+ been checked.
+ */
+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) {
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
SecCertificateRef cert = NULL;
CFDataRef digest = NULL;
- bool foundMatch = false;
if (SecPVCGetCertificateCount(pvc) < 2) {
SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 0, kCFBooleanFalse);
cert = SecPVCGetCertificateAtIndex(pvc, 1);
digest = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert);
- if (isData(value))
- foundMatch = CFEqual(digest, value);
- else if (isArray(value))
- foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), digest);
- else {
- /* @@@ We only support Data and Array but we can't return an error here so.
- we let the evaluation fail (not much help) and assert in debug. */
- assert(false);
+ if (!isDigestInPolicy(pvc, key, digest)) {
+ SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 1, kCFBooleanFalse);
}
-
CFReleaseNull(digest);
-
- if (!foundMatch) {
- SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 0, kCFBooleanFalse);
- }
}
/*
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);
won't help much either. */
return;
}
- CFArrayRef organization = SecCertificateCopyOrganization(cert);
- if (!organization || CFArrayGetCount(organization) != 1 ||
- !CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) {
+ if (!SecPolicyCheckCertSubjectOrganization(cert, org)) {
/* Leaf Subject Organization mismatch. */
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
- CFReleaseSafe(organization);
}
static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc,
won't help much either. */
return;
}
- CFArrayRef organizationalUnit = SecCertificateCopyOrganizationalUnit(cert);
- if (!organizationalUnit || CFArrayGetCount(organizationalUnit) != 1 ||
- !CFEqual(orgUnit, CFArrayGetValueAtIndex(organizationalUnit, 0))) {
- /* Leaf Subject Organizational Unit mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(organizationalUnit);
+ if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert, orgUnit)) {
+ /* Leaf Subject Organization mismatch. */
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
/* AUDIT[securityd](done):
return;
}
- CFIndex tsnCount = CFArrayGetCount(trustedServerNames);
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
- bool dnsMatch = false;
- CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf);
- if (dnsNames) {
- CFIndex ix, count = CFArrayGetCount(dnsNames);
- // @@@ This is O(N^2) unfortunately we can't do better easily unless
- // we don't do wildcard matching. */
- 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)) {
- /* @@@ We can't return an error here and making the
- evaluation fail won't help much either. */
- CFReleaseSafe(dnsNames);
- return;
- }
- /* we purposefully reverse the arguments here such that dns names
- from the cert are matched against a server name list, where
- the server names list can contain wildcards and the dns name
- cannot. References: http://support.microsoft.com/kb/941123
- It's easy to find occurrences where people tried to use
- wildcard certificates and were told that those don't work
- in this context. */
- if (SecDNSMatch(dns, serverName)) {
- dnsMatch = true;
- break;
- }
- }
- }
- CFRelease(dnsNames);
- }
-
- if (!dnsMatch) {
+ if (!SecPolicyCheckCertEAPTrustedServerNames(leaf, trustedServerNames)) {
/* Hostname mismatch or no hostnames found in certificate. */
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
serial_ptr, sizeof(*UTN_USERFirst_Hardware_Serial)))
{
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ pvc->result = kSecTrustResultFatalTrustFailure;
CFReleaseSafe(serial);
return;
}
if (CFSetContainsValue(blackListedKeys, dgst))
{
SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ pvc->result = kSecTrustResultFatalTrustFailure;
}
CFRelease(dgst);
}
CFRelease(grayListedKeys);
}
}
- }
+}
static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc, CFStringRef key)
{
SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
- if (value && SecCertificateHasMarkerExtension(cert, value))
- return;
-
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ if (!SecPolicyCheckCertLeafMarkerOid(cert, value)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
-static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key)
+static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc, CFStringRef key)
{
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
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;
+ if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert, value)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
}
-/* Returns true if path is on the allow list, false otherwise */
-static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc)
+/*
+ * The value is a dictionary. The dictionary contains keys indicating
+ * whether the value is for Prod or QA. The values are the same as
+ * in the options dictionary for SecPolicyCheckLeafMarkerOid.
+ */
+static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc, CFStringRef key)
{
- bool result = false;
- CFIndex ix = 0, count = SecPVCGetCertificateCount(pvc);
- CFStringRef authKey = NULL;
- SecOTAPKIRef otapkiRef = NULL;
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFDictionaryRef value = CFDictionaryGetValue(policy->_options, key);
+ CFTypeRef prodValue = CFDictionaryGetValue(value, kSecPolicyLeafMarkerProd);
- //get authKeyID from the last chain in the cert
- if (count < 1) {
- return result;
- }
- SecCertificateRef lastCert = SecPVCGetCertificateAtIndex(pvc, count - 1);
- CFDataRef authKeyID = SecCertificateGetAuthorityKeyID(lastCert);
- if (NULL == authKeyID) {
- return result;
- }
- authKey = CFDataCopyHexString(authKeyID);
-
- //if allowList && key is in allowList, this would have chained up to a now-removed anchor
- otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL == otapkiRef) {
- goto errout;
- }
- CFDictionaryRef allowList = SecOTAPKICopyAllowList(otapkiRef);
- if (NULL == allowList) {
- goto errout;
+ if (!SecPolicyCheckCertLeafMarkerOid(cert, prodValue)) {
+ bool result = false;
+ if (!result) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
}
+}
- CFArrayRef allowedCerts = CFDictionaryGetValue(allowList, authKey);
- if (!allowedCerts || !CFArrayGetCount(allowedCerts)) {
- goto errout;
- }
+static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key)
+{
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
- //search sorted array for the SHA256 hash of a cert in the chain
- CFRange range = CFRangeMake(0, CFArrayGetCount(allowedCerts));
- for (ix = 0; ix < count; ix++) {
+ for (ix = 1; ix < count - 1; 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)) {
- //this cert is in the allowlist
- result = true;
- }
- }
-
- CFRelease(certHash);
+ if (SecCertificateHasMarkerExtension(cert, value))
+ return;
}
-
-errout:
- CFRelease(authKey);
- CFReleaseNull(otapkiRef);
- CFReleaseNull(allowList);
- return result;
+ 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);
-/****************************************************************************
- *********************** New rfc5280 Chain Validation ***********************
- ****************************************************************************/
-
-#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; /* includes optional params */
- 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;
-};
-
-/* For each node of depth i-1 in the valid_policy_tree where P-OID is in the expected_policy_set, create a child node as follows: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
-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;
+ for (ix = 1; ix < count - 1; ix++) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (!SecPolicyCheckCertExtendedKeyUsage(cert, peku)) {
+ SecPVCSetResult(pvc, key, ix, kCFBooleanFalse);
+ }
+ }
}
-/* If the valid_policy_tree includes a node of depth i-1 with the valid_policy anyPolicy, generate a child node with the following values: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
-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 void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc, CFStringRef key)
+{
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFTypeRef organization = CFDictionaryGetValue(policy->_options, key);
-/* Return true iff node has a child with a valid_policy equal to oid. */
-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;
+ for (ix = 1; ix < count - 1; ix++) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (!SecPolicyCheckCertSubjectOrganization(cert, organization)) {
+ SecPVCSetResult(pvc, key, ix, kCFBooleanFalse);
}
}
- return false;
}
-/* For each node in the valid_policy_tree of depth i-1, for each value in the expected_policy_set (including anyPolicy) that does not appear in a child node, create a child node with the following values: set the valid_policy to the value from the expected_policy_set in the parent node, set the qualifier_set to AP-Q, and set the expected_policy_set to the value in the valid_policy from this node. */
-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;
+static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc, CFStringRef key)
+{
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFTypeRef country = CFDictionaryGetValue(policy->_options, key);
+
+ for (ix = 1; ix < count - 1; ix++) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (!SecPolicyCheckCertSubjectCountry(cert, country)) {
+ SecPVCSetResult(pvc, key, ix, kCFBooleanFalse);
}
}
- return added_node;
}
-#if 0
-/* For each node where ID-P is the valid_policy, set expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
-static bool policy_tree_map(policy_tree_t node, void *ctx) {
- /* Can't map oidAnyPolicy. */
- 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;
- /* First count how many mappings match this nodes valid_policy. */
- 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
+/****************************************************************************
+ *********************** New rfc5280 Chain Validation ***********************
+ ****************************************************************************/
-#define POLICY_MAPPING 0
+#define POLICY_MAPPING 1
#define POLICY_SUBTREES 1
/* rfc5280 basic cert processing. */
/* Inputs */
//cert_path_t path;
CFIndex count = SecPVCGetCertificateCount(pvc);
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
/* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
assert((unsigned long)count<=UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */
uint32_t n = (uint32_t)count;
- bool is_anchored = SecPVCIsAnchored(pvc);
+
+ bool is_anchored = SecPathBuilderIsAnchored(pvc->builder);
+ bool is_anchor_trusted = false;
if (is_anchored) {
- /* If the anchor is trusted we don't procces the last cert in the
+ CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, n - 1);
+ if (CFArrayGetCount(constraints) == 0) {
+ /* Given that the path builder has already indicated the last cert in this chain has
+ * trust set on it, empty constraints means trusted. */
+ is_anchor_trusted = true;
+ } else {
+ /* Determine whether constraints say to trust this cert for this PVC. */
+ SecTrustSettingsResult tsResult = SecPVCGetTrustSettingsResult(pvc, SecCertificatePathVCGetCertificateAtIndex(path, n - 1),
+ constraints);
+ if (tsResult == kSecTrustSettingsResultTrustRoot || tsResult == kSecTrustSettingsResultTrustAsRoot) {
+ is_anchor_trusted = true;
+ }
+ }
+ }
+
+ if (is_anchor_trusted) {
+ /* If the anchor is trusted we don't process the last cert in the
chain (root). */
n--;
} else {
- /* trust may be restored for a path with an untrusted root that matches the allow list */
- if (!SecPVCCheckCertificateAllowList(pvc)) {
+ /* trust may be restored for a path with an untrusted root that matches the allow list.
+ (isAllowlisted is set by revocation check, which is performed prior to path checks) */
+ if (!SecCertificatePathVCIsAllowlisted(path)) {
/* Add a detail for the root not being trusted. */
- if (SecPVCSetResultForced(pvc, kSecPolicyCheckAnchorTrusted,
- n - 1, kCFBooleanFalse, true))
+ if (!SecPVCSetResultForced(pvc, kSecPolicyCheckAnchorTrusted,
+ n - 1, kCFBooleanFalse, true)) {
return;
+ }
}
}
CFAbsoluteTime verify_time = SecPVCGetVerifyTime(pvc);
//policy_set_t user_initial_policy_set = NULL;
//trust_anchor_t anchor;
- bool initial_policy_mapping_inhibit = false;
- bool initial_explicit_policy = false;
- bool initial_any_policy_inhibit = false;
/* Initialization */
- 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);
- assert(permitted_subtrees != NULL);
- assert(excluded_subtrees != NULL);
+ 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 (!SecCertificatePathVCVerifyPolicyTree(path, is_anchor_trusted)) {
+ if (!SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true)) {
+ goto errOut;
+ }
+ }
#if 0
/* Path builder ensures we only get cert chains with proper issuer
for (i = 1; i <= n; ++i) {
/* Process Cert */
cert = SecPVCGetCertificateAtIndex(pvc, n - i);
- bool is_self_issued = SecPVCIsCertificateAtIndexSelfSigned(pvc, n - i);
+ bool is_self_issued = SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc->builder), n - i);
/* (a) Verify the basic certificate information. */
/* @@@ Ensure that cert was signed with working_public_key_algorithm
/* Already done by chain builder. */
if (!SecCertificateIsValid(cert, verify_time)) {
CFStringRef fail_key = i == n ? kSecPolicyCheckValidLeaf : kSecPolicyCheckValidIntermediates;
- if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse))
- return;
+ if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) {
+ goto errOut;
+ }
}
-#endif
-#if 0
- /* Check revocation status if the certificate asks for it. */
- CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
- if (ocspResponders) {
- SecPVCSetCheckRevocation(pvc);
+ if (SecCertificateIsWeakKey(cert)) {
+ CFStringRef fail_key = i == n ? kSecPolicyCheckWeakLeaf : kSecPolicyCheckWeakIntermediates;
+ if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) {
+ goto errOut;
+ }
+ pvc->result = kSecTrustResultFatalTrustFailure;
}
#endif
/* @@@ cert.issuer == working_issuer_name. */
/* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
if(excluded_subtrees && CFArrayGetCount(excluded_subtrees)) {
if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, excluded_subtrees, &found, false)) || found) {
- if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) return;
+ secnotice("policy", "name in excluded subtrees");
+ if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; }
}
}
/* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
if(permitted_subtrees && CFArrayGetCount(permitted_subtrees)) {
if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, permitted_subtrees, &found, true)) || !found) {
- if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) return;
+ secnotice("policy", "name not in permitted subtrees");
+ if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; }
}
}
}
#endif
- /* (d) */
- 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);
- }
- }
- }
- /* The certificate policies extension includes the policy
- anyPolicy with the qualifier set AP-Q and either
- (a) inhibit_anyPolicy is greater than 0 or
- (b) i < n and the certificate is self-issued. */
- 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);
- /* (e) */
- if (!cp) {
- if (pvc->valid_policy_tree)
- policy_tree_prune(&pvc->valid_policy_tree);
- }
- }
- /* (f) Verify that either explicit_policy is greater than 0 or the
- valid_policy_tree is not equal to NULL. */
- if (!pvc->valid_policy_tree && explicit_policy == 0) {
- /* valid_policy_tree is empty and explicit policy is 0, illegal. */
- if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, n - i, kCFBooleanFalse, true))
- return;
- }
+ /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
+
/* If Last Cert in Path */
if (i == n)
break;
/* Prepare for Next Cert */
-#if POLICY_MAPPING
- /* (a) verify that anyPolicy does not appear as an
- issuerDomainPolicy or a subjectDomainPolicy */
- 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)) {
- /* Policy mapping uses anyPolicy, illegal. */
- if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, n - i, kCFBooleanFalse))
- return;
- }
- }
- /* (b) */
- /* (1) If the policy_mapping variable is greater than 0 */
- if (policy_mapping > 0) {
- if (!policy_tree_walk_depth(pvc->valid_policy_tree, i,
- policy_tree_map, (void *)pm)) {
- /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1 that has a valid_policy of anyPolicy as follows:
-
- (i) set the valid_policy to ID-P;
-
- (ii) set the qualifier_set to the qualifier set of the
- policy anyPolicy in the certificate policies
- extension of certificate i; and
- (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
- }
- } else {
- #if 0
- /* (i) delete each node of depth i in the valid_policy_tree
- where ID-P is the valid_policy. */
- 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
- /* (ii) If there is a node in the valid_policy_tree of depth
- i-1 or less without any child nodes, delete that
- node. Repeat this step until there are no nodes of
- depth i-1 or less without children. */
- policy_tree_prune_childless(&pvc->valid_policy_tree, i - 1);
- }
- }
-#endif /* POLICY_MAPPING */
- /* (c)(d)(e)(f) */
+ /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
+ /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
//working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
//working_public_key = SecCertificateCopyPublicKey(cert);
//working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
CFArrayAppendArray(excluded_subtrees, excluded_subtrees_in_cert, range);
}
#endif
- /* (h) */
- if (!is_self_issued) {
- if (explicit_policy)
- explicit_policy--;
- if (policy_mapping)
- policy_mapping--;
- if (inhibit_any_policy)
- inhibit_any_policy--;
- }
- /* (i) */
- 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;
- }
- }
- /* (j) */
- uint32_t iap = SecCertificateGetInhibitAnyPolicySkipCerts(cert);
- if (iap < inhibit_any_policy) {
- inhibit_any_policy = iap;
- }
+ /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
+
/* (k) */
const SecCEBasicConstraints *bc =
SecCertificateGetBasicConstraints(cert);
-#if 0 /* Checked in chain builder pre signature verify already. */
+#if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
if (!bc || !bc->isCA) {
/* Basic constraints not present or not marked as isCA, illegal. */
- if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints,
- n - i, kCFBooleanFalse))
- return;
+ if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints,
+ n - i, kCFBooleanFalse)) {
+ goto errOut;
+ }
}
#endif
/* (l) */
max_path_length--;
} else {
/* max_path_len exceeded, illegal. */
- if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints,
- n - i, kCFBooleanFalse))
- return;
+ if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints,
+ n - i, kCFBooleanFalse)) {
+ goto errOut;
+ }
}
}
/* (m) */
&& bc->pathLenConstraint < max_path_length) {
max_path_length = bc->pathLenConstraint;
}
-#if 0 /* Checked in chain builder pre signature verify already. */
+#if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
/* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage,
- n - i, kCFBooleanFalse, true))
- return;
+ n - i, kCFBooleanFalse, true)) {
+ goto errOut;
+ }
}
#endif
/* (o) Recognize and process any other critical extension present in the certificate. Process any other recognized non-critical extension present in the certificate that is relevant to path processing. */
if (SecCertificateHasUnknownCriticalExtension(cert)) {
/* Certificate contains one or more unknown critical extensions. */
if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions,
- n - i, kCFBooleanFalse))
- return;
+ n - i, kCFBooleanFalse)) {
+ goto errOut;
+ }
}
} /* end loop over certs in path */
/* Wrap up */
- cert = SecPVCGetCertificateAtIndex(pvc, 0);
- /* (a) */
- if (explicit_policy)
- explicit_policy--;
- /* (b) */
- const SecCEPolicyConstraints *pc = SecCertificateGetPolicyConstraints(cert);
- if (pc) {
- if (pc->requireExplicitPolicyPresent
- && pc->requireExplicitPolicy == 0) {
- explicit_policy = 0;
- }
- }
+ /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
/* (c) */
//working_public_key = SecCertificateCopyPublicKey(cert);
/* (d) */
if (SecCertificateHasUnknownCriticalExtension(cert)) {
/* Certificate contains one or more unknown critical extensions. */
if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions,
- 0, kCFBooleanFalse))
- return;
- }
- /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
-
- if (pvc->valid_policy_tree) {
-#if !defined(NDEBUG)
- policy_tree_dump(pvc->valid_policy_tree);
-#endif
- /* (g3c4) */
- //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
- }
-
- /* If either (1) the value of explicit_policy variable is greater than
- zero or (2) the valid_policy_tree is not NULL, then path processing
- has succeeded. */
- if (!pvc->valid_policy_tree && explicit_policy == 0) {
- /* valid_policy_tree is empty and explicit policy is 0, illegal. */
- if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, 0, kCFBooleanFalse, true))
- return;
+ 0, kCFBooleanFalse)) {
+ goto errOut;
+ }
}
+ /* (g) done by SecCertificatePathVCVerifyPolicyTree */
+errOut:
CFReleaseNull(permitted_subtrees);
CFReleaseNull(excluded_subtrees);
}
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
policy_set_t valid_policies = NULL;
+ /* 6.1.7. Key Usage Purposes */
+ if (count) {
+ CFAbsoluteTime jul2016 = 489024000;
+ SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
+ if (SecCertificateNotValidBefore(leaf) > jul2016 && count < 3) {
+ /* Root CAs may not sign subscriber certificates after 30 June 2016. */
+ 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);
} else if (ix < count - 1) {
/* Subordinate CA */
if (!SecPolicySubordinateCACertificateCouldBeEV(cert)) {
- secdebug("ev", "subordinate certificate is not ev");
+ secnotice("ev", "subordinate certificate is not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
policy_set_free(valid_policies);
} else {
/* Root CA */
if (!SecPolicyRootCACertificateIsEV(cert, valid_policies)) {
- secdebug("ev", "anchor certificate is not ev");
+ secnotice("ev", "anchor certificate is not ev");
if (SecPVCSetResultForced(pvc, key,
ix, kCFBooleanFalse, true)) {
policy_set_free(valid_policies);
}
policy_set_free(policies);
if (!valid_policies) {
- secdebug("ev", "valid_policies set is empty: chain not ev");
+ secnotice("ev", "valid_policies set is empty: chain not ev");
/* If we ever get into a state where no policies are valid anymore
this can't be an ev chain. */
if (SecPVCSetResultForced(pvc, key,
return data;
}
-/* If the 'sct' is valid, return the operator ID of the log that signed this sct.
+static
+CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
+{
+ return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
+}
+
+static
+uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
+{
+ return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
+}
+
+
+
+
+/*
+ If the 'sct' is valid, add it to the validatingLogs dictionary.
+
+ Inputs:
+ - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
+ - sct: the SCT date
+ - entry_type: 0 for x509 cert, 1 for precert.
+ - entry: the cert or precert data.
+ - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
+ - trustedLog: Dictionary contain the Trusted Logs.
The SCT is valid if:
- It decodes properly.
- Its timestamp is less than 'verifyTime'.
- It is signed by a log in 'trustedLogs'.
- - The signing log expiryTime (if any) is less than 'verifyTime' (entry_type==0) or 'issuanceTime' (entry_type==1).
+ - If entry_type = 0, the log must be currently qualified.
+ - If entry_type = 1, the log may be expired.
- If the SCT is valid, the returned CFStringRef is the identifier for the log operator. That value is not retained.
- If the SCT is valid, '*validLogAtVerifyTime' is set to true if the log is not expired at 'verifyTime'
+ If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
+ If an entry for the same log already existing in the dictionary, the entry is replaced only if the timestamp of this SCT is earlier.
- If the SCT is not valid this function return NULL.
*/
-static CFStringRef get_valid_sct_operator(CFDataRef sct, int entry_type, CFDataRef entry, CFAbsoluteTime verifyTime, CFAbsoluteTime issuanceTime, CFArrayRef trustedLogs, bool *validLogAtVerifyTime)
+
+
+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;
uint8_t sigAlg;
size_t signatureLen;
const uint8_t *signatureData;
- CFStringRef result = NULL;
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);
- uint64_t vt =(uint64_t)( verifyTime + kCFAbsoluteTimeIntervalSince1970) * 1000;
require(len>=43, out);
q = SSLEncodeUint16(q, extensionsLen);
memcpy(q, extensionsData, extensionsLen);
- CFDataRef logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, NULL);
+ logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
CFDictionaryRef logData = CFArrayGetValueMatching(trustedLogs, ^bool(const void *dict) {
const void *key_data;
});
require(logData, out);
- /* If an expiry date is specified, and is a valid CFDate, then we check it against issuanceTime or verifyTime */
- const void *expiry_date;
- if(CFDictionaryGetValueIfPresent(logData, CFSTR("expiry"), &expiry_date) && isDate(expiry_date)) {
- CFAbsoluteTime expiryTime = CFDateGetAbsoluteTime(expiry_date);
- if(entry_type == 1) {/* pre-cert: check the validity of the log at issuanceTime */
- require(issuanceTime<=expiryTime, out);
- } else {
- require(verifyTime<=expiryTime, out);
- }
- *validLogAtVerifyTime = (verifyTime<=expiryTime);
- } else {
- *validLogAtVerifyTime = true;
+ if(entry_type==0) {
+ // For external SCTs, only keep SCTs from currently valid logs.
+ require(!CFDictionaryContainsKey(logData, CFSTR("expiry")), out);
}
CFDataRef logKeyData = CFDictionaryGetValue(logData, CFSTR("key"));
algId.parameters.Length = 0;
if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
- result = CFDictionaryGetValue(logData, CFSTR("operator"));
+ *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;
}
SecOCSPSingleResponseDestroy(ocspSingleResponse);
}
- SecOCSPResponseFinalize(ocspResponse);
}
+ if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
});
if(CFArrayGetCount(SCTs)==0) {
CFDataRef precertEntry = copy_precert_entry_from_chain(pvc);
CFDataRef x509Entry = copy_x509_entry_from_chain(pvc);
- // This eventually contain the list of operators who validated the SCT.
- CFMutableSetRef operatorsValidatingEmbeddedScts = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
- CFMutableSetRef operatorsValidatingExternalScts = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
+ // This eventually contain list of logs who validated the SCT.
+ CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc));
- __block bool atLeastOneValidAtVerifyTime = false;
- __block int lifetime; // in Months
+ __block bool at_least_one_currently_valid_external = 0;
+ __block bool at_least_one_currently_valid_embedded = 0;
- require(operatorsValidatingEmbeddedScts, out);
- require(operatorsValidatingExternalScts, out);
+ require(logsValidatingEmbeddedScts, out);
+ require(currentLogsValidatingScts, out);
if(trustedLogs) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
- if(embeddedScts) {
+ if(embeddedScts && precertEntry) { // Don't bother if we could not get the precert.
CFArrayForEach(embeddedScts, ^(const void *value){
- bool validLogAtVerifyTime = false;
- CFStringRef operator = get_valid_sct_operator(value, 1, precertEntry, pvc->verifyTime, SecCertificateNotValidBefore(leafCert), trustedLogs, &validLogAtVerifyTime);
- if(operator) CFSetAddValue(operatorsValidatingEmbeddedScts, operator);
- if(validLogAtVerifyTime) atLeastOneValidAtVerifyTime = true;
+ 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) {
+ if(builderScts && x509Entry) { // Don't bother if we could not get the cert.
CFArrayForEach(builderScts, ^(const void *value){
- bool validLogAtVerifyTime = false;
- CFStringRef operator = get_valid_sct_operator(value, 0, x509Entry, pvc->verifyTime, SecCertificateNotValidBefore(leafCert), trustedLogs, &validLogAtVerifyTime);
- if(operator) CFSetAddValue(operatorsValidatingExternalScts, operator);
- if(validLogAtVerifyTime) atLeastOneValidAtVerifyTime = true;
+ 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) {
+ if(ocspScts && x509Entry) {
CFArrayForEach(ocspScts, ^(const void *value){
- bool validLogAtVerifyTime = false;
- CFStringRef operator = get_valid_sct_operator(value, 0, x509Entry, pvc->verifyTime, SecCertificateNotValidBefore(leafCert), trustedLogs, &validLogAtVerifyTime);
- if(operator) CFSetAddValue(operatorsValidatingExternalScts, operator);
- if(validLogAtVerifyTime) atLeastOneValidAtVerifyTime = true;
+ 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;
+ }
});
}
}
- /* We now have 2 sets of operators that validated those SCTS, count them and make a final decision.
- Current Policy:
- is_ct = (A1 OR A2) AND B.
- A1: 2+ to 5+ SCTs from the cert from independent logs valid at issuance time
- (operatorsValidatingEmbeddedScts)
- A2: 2+ SCTs from external sources (OCSP stapled response and TLS extension)
- from independent logs valid at verify time. (operatorsValidatingExternalScts)
- B: All least one SCTs from a log valid at verify time.
+ /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
- Policy is based on: https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxjZXJ0aWZpY2F0ZXRyYW5zcGFyZW5jeXxneDo0ODhjNGRlOTIyMzYwNTcz
- with one difference: we consider SCTs from OCSP and TLS extensions as a whole.
- It sounds like this is what Google will eventually do, per:
- https://groups.google.com/forum/?fromgroups#!topic/certificate-transparency/VdXuzA3TLWY
+ Current Policy:
+ is_ct = (A1 AND A2) OR (B1 AND B2).
- */
+ A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
+ A2: At least one embedded SCT from a currently valid log.
- SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
- int _lifetime;
- CFCalendarGetComponentDifference(zuluCalendar,
- SecCertificateNotValidBefore(leafCert),
- SecCertificateNotValidAfter(leafCert),
- 0, "M", &_lifetime);
- lifetime = _lifetime;
- });
+ B1: SCTs from 2 currently valid logs (from any source)
+ B2: At least 1 external SCT from a currently valid log.
- CFIndex requiredEmbeddedSctsCount;
+ */
- if (lifetime < 15) {
- requiredEmbeddedSctsCount = 2;
- } else if (lifetime <= 27) {
- requiredEmbeddedSctsCount = 3;
- } else if (lifetime <= 39) {
- requiredEmbeddedSctsCount = 4;
- } else {
- requiredEmbeddedSctsCount = 5;
- }
+ SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), false);
+
+ if(at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2) {
+ SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), true);
+ } else if(at_least_one_currently_valid_embedded) {
+ __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc);
+ __block int lifetime; // in Months
+ __block unsigned once_or_current_qualified_embedded = 0;
+
+ /* Calculate issuance time base on timestamp of SCTs from current logs */
+ CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) {
+ CFDictionaryRef log = key;
+ if(!CFDictionaryContainsKey(log, CFSTR("expiry"))) {
+ // Log is still qualified
+ CFDateRef ts = (CFDateRef) value;
+ CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts);
+ if(timestamp < issuanceTime) {
+ issuanceTime = timestamp;
+ }
+ }
+ });
+
+ /* Count Logs */
+ 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;
+ }
- pvc->is_ct = ((CFSetGetCount(operatorsValidatingEmbeddedScts) >= requiredEmbeddedSctsCount) ||
- (CFSetGetCount(operatorsValidatingExternalScts) >= 2)
- ) && atLeastOneValidAtVerifyTime;
+ if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
+ SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), true);
+ }
+ }
out:
-
- CFReleaseSafe(operatorsValidatingEmbeddedScts);
- CFReleaseSafe(operatorsValidatingExternalScts);
+ CFReleaseSafe(logsValidatingEmbeddedScts);
+ CFReleaseSafe(currentLogsValidatingScts);
CFReleaseSafe(builderScts);
CFReleaseSafe(embeddedScts);
CFReleaseSafe(ocspScts);
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)) {
+ policy_set_free(policies);
+ return true;
+ }
+ policy_set_free(policies);
+ }
+ return false;
+}
+
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;
+ bool result = false;
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;
- }
- }
+ 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) {
- SecPVCSetCheckRevocation(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
+ if (isString(value)) {
+ SecPathBuilderSetRevocationMethod(pvc->builder, value);
+ }
}
static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc,
CFStringRef key) {
- SecPVCSetCheckRevocationResponseRequired(pvc);
+ pvc->require_revocation_response = true;
+ secdebug("policy", "revocation response required");
}
-static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc,
- CFStringRef key) {
- SecPathBuilderSetCanAccessNetwork(pvc->builder, false);
+static void SecPolicyCheckRevocationOnline(SecPVCRef pvc, CFStringRef key) {
+ SecPathBuilderSetCheckRevocationOnline(pvc->builder);
}
-// MARK: -
-// MARK: SecRVCRef
-/********************************************************
- ****************** SecRVCRef Functions *****************
- ********************************************************/
-
-const CFAbsoluteTime kSecDefaultOCSPResponseTTL = 24.0 * 60.0 * 60.0;
-
-/* Revocation verification context. */
-struct OpaqueSecRVC {
- /* Will contain the response data. */
- asynchttp_t http;
-
- /* Pointer to the pvc for this revocation check. */
- SecPVCRef pvc;
-
- /* The ocsp request we send to each responder. */
- SecOCSPRequestRef ocspRequest;
-
- /* The freshest response we received so far, from stapling or cache or responder. */
- SecOCSPResponseRef ocspResponse;
-
- /* The best validated candidate single response we received so far, from stapling or cache or responder. */
- SecOCSPSingleResponseRef ocspSingleResponse;
-
- /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
- CFIndex certIX;
-
- /* Index in array returned by SecCertificateGetOCSPResponders() for current
- responder. */
- CFIndex responderIX;
-
- /* URL of current responder. */
- CFURLRef responder;
-
- /* Date until which this revocation status is valid. */
- CFAbsoluteTime nextUpdate;
+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);
+ }
+}
- bool done;
-};
-typedef struct OpaqueSecRVC *SecRVCRef;
-
-static void SecRVCDelete(SecRVCRef rvc) {
- secdebug("alloc", "%p", rvc);
- asynchttp_free(&rvc->http);
- SecOCSPRequestFinalize(rvc->ocspRequest);
- if (rvc->ocspResponse) {
- SecOCSPResponseFinalize(rvc->ocspResponse);
- rvc->ocspResponse = NULL;
- if (rvc->ocspSingleResponse) {
- SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
- rvc->ocspSingleResponse = NULL;
- }
- }
-}
-
-/* Return the next responder we should contact for this rvc or NULL if we
- exhausted them all. */
-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) {
- /* We only support http and https responders currently. */
- bool valid_responder = (CFEqual(CFSTR("http"), scheme) ||
- CFEqual(CFSTR("https"), scheme));
- CFRelease(scheme);
- if (valid_responder)
- return responder;
- }
+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 && SecCertificateIsWeakKey(cert)) {
+ /* Intermediate certificate has a weak key. */
+ if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
+ return;
+ pvc->result = kSecTrustResultFatalTrustFailure;
}
}
- return NULL;
}
-/* Fire off an async http request for this certs revocation status, return
- false if request was queued, true if we're done. */
-static bool SecRVCFetchNext(SecRVCRef rvc) {
- while ((rvc->responder = SecRVCGetNextResponder(rvc))) {
- CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest);
- if (!request)
- goto errOut;
-
- secdebug("ocsp", "Sending http ocsp request for cert %ld", rvc->certIX);
- if (!asyncHttpPost(rvc->responder, request, &rvc->http)) {
- /* Async request was posted, wait for reply. */
- return false;
- }
+static void SecPolicyCheckWeakLeaf(SecPVCRef pvc,
+ CFStringRef key) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
+ if (cert && SecCertificateIsWeakKey(cert)) {
+ /* Leaf certificate has a weak key. */
+ if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
+ return;
+ pvc->result = kSecTrustResultFatalTrustFailure;
}
+}
-errOut:
- rvc->done = true;
- return true;
+static void SecPolicyCheckWeakRoot(SecPVCRef pvc,
+ CFStringRef key) {
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
+ ix = count - 1;
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (cert && SecCertificateIsWeakKey(cert)) {
+ /* Root certificate has a weak key. */
+ if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
+ return;
+ pvc->result = kSecTrustResultFatalTrustFailure;
+ }
}
-/* Process a verified ocsp response for a given cert. Return true if the
- certificate status was obtained. */
-static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef this,
- SecRVCRef rvc) {
- bool processed;
- switch (this->certStatus) {
- case CS_Good:
- secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
- /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
- in the info dictionary. */
- //cert.revokeCheckGood(true);
- 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);
- /* @@@ Mark cert as revoked (with reason) at revocation date in
- the info dictionary, or perhaps we should use a different key per
- reason? That way a client using exceptions can ignore some but
- not all reasons. */
- SInt32 reason = this->crlReason;
- CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
- SecPVCSetResultForced(rvc->pvc, kSecPolicyCheckRevocation, rvc->certIX,
- cfreason, true);
- if (rvc->pvc && rvc->pvc->info) {
- /* make the revocation reason available in the trust result */
- CFDictionarySetValue(rvc->pvc->info, kSecTrustRevocationReason, cfreason);
- }
- CFRelease(cfreason);
- processed = true;
- break;
- case CS_Unknown:
- /* not an error, no per-cert status, nothing here */
- secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
- processed = false;
- break;
- default:
- secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
- (int)this->certStatus, rvc->certIX);
- processed = false;
- break;
- }
-
- return processed;
-}
-
-static void SecRVCUpdatePVC(SecRVCRef rvc) {
- if (rvc->ocspSingleResponse) {
- SecOCSPSingleResponseProcess(rvc->ocspSingleResponse, rvc);
- }
- if (rvc->ocspResponse) {
- rvc->nextUpdate = SecOCSPResponseGetExpirationTime(rvc->ocspResponse);
- }
-}
-
-static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecRVCRef rvc, CFAbsoluteTime verifyTime) {
- bool trusted;
- SecCertificatePathRef issuer = SecCertificatePathCopyFromParent(rvc->pvc->path, rvc->certIX + 1);
- SecCertificatePathRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
- CFRelease(issuer);
-
- if (signer) {
- if (signer == issuer) {
- /* We already know we trust issuer since it's the path we are
- trying to verify minus the leaf. */
- 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);
- /* @@@ Now check that we trust signer. */
- const void *ocspSigner = SecPolicyCreateOCSPSigner();
- CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
- &ocspSigner, 1, &kCFTypeArrayCallBacks);
- CFRelease(ocspSigner);
- 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 is false we are waiting for a callback, this
- shouldn't happen since we aren't asking for details, no
- revocation checking is done. */
- if (!completed) {
- ocspdErrorLog("SecPVCPathChecks unexpectedly started "
- "background job!");
- /* @@@ assert() or abort here perhaps? */
- }
- }
- if (ospvc.result) {
- secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
- rvc->responder);
- trusted = true;
- } else {
- /* @@@ We don't trust the cert so don't use this response. */
- ocspdErrorLog("ocsp response signed by certificate which "
- "does not satisfy ocspSigner policy");
- trusted = false;
- }
- SecPVCDelete(&ospvc);
+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;
}
-
- CFRelease(signer);
- } else {
- /* @@@ No signer found for this ocsp response, discard it. */
- 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 SecRVCConsumeOCSPResponse(SecRVCRef rvc, SecOCSPResponseRef ocspResponse /*CF_CONSUMED*/, CFTimeInterval maxAge, bool updateCache) {
- SecOCSPSingleResponseRef sr = NULL;
- require_quiet(ocspResponse, errOut);
- SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
- require_action_quiet(orStatus == kSecOCSPSuccess, errOut,
- secdebug("ocsp", "responder: %@ returned status: %d", rvc->responder, orStatus));
- require_action_quiet(sr = SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest), errOut,
- secdebug("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc->responder));
- // Check if this response is fresher than any (cached) response we might still have in the rvc.
- require_quiet(!rvc->ocspSingleResponse || rvc->ocspSingleResponse->thisUpdate < sr->thisUpdate, errOut);
+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;
+ }
+ }
+}
- CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
- /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
- check whether the leaf was revoked (we are already checking the rest of
- the chain). */
- /* Check the OCSP response signature and verify the response. */
- require_quiet(SecOCSPResponseVerify(ocspResponse, rvc,
- sr->certStatus == CS_Revoked ? SecOCSPResponseProducedAt(ocspResponse) : verifyTime), errOut);
+static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc) {
+ SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
+ require_quiet(leaf, out);
- // If we get here, we have a properly signed ocsp response
- // but we haven't checked dates yet.
+ /* And now a special snowflake from our tests */
- bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
- if (sr->certStatus == CS_Good) {
- // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
- require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
- } else if (sr->certStatus == CS_Revoked) {
- // Expire revoked responses when the subject certificate itself expires.
- ocspResponse->expireTime = SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX));
- }
+ /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
+ /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
+ /* Not After : May 26 09:37:50 2017 GMT */
+ static const uint8_t vodafone[] = {
+ 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
+ 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
+ };
- // Ok we like the new response, let's toss the old one.
- if (updateCache)
- SecOCSPCacheReplaceResponse(rvc->ocspResponse, ocspResponse, rvc->responder, verifyTime);
+ /* subject:/C=US/ST=Kansas/L=Overland Park/O=Sprint/CN=oma.ssprov.sprint.com */
+ /* issuer :/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C */
+ /* Not After : Aug 16 05:04:29 2017 GMT */
+ static const uint8_t sprint[] = {
+ 0xa3, 0x18, 0x70, 0x4f, 0xf7, 0xbf, 0xfb, 0x2b, 0xe2, 0x64,
+ 0x3a, 0x2d, 0x2b, 0xb8, 0x10, 0x5f, 0x77, 0xd5, 0x01, 0xab
+ };
- if (rvc->ocspResponse) SecOCSPResponseFinalize(rvc->ocspResponse);
- rvc->ocspResponse = ocspResponse;
- ocspResponse = NULL;
+ CFDataRef leafFingerprint = SecCertificateGetSHA1Digest(leaf);
+ require_quiet(leafFingerprint, out);
+ const unsigned int len = 20;
+ const uint8_t *dp = CFDataGetBytePtr(leafFingerprint);
+ if (dp && (!memcmp(vodafone, dp, len) || !memcmp(sprint,dp,len))) {
+ return true;
+ }
- if (rvc->ocspSingleResponse) SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
- rvc->ocspSingleResponse = sr;
- sr = NULL;
+out:
+ return false;
+}
- rvc->done = sr_valid;
+static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc, CFStringRef key);
-errOut:
- if (sr) SecOCSPSingleResponseDestroy(sr);
- if (ocspResponse) SecOCSPResponseFinalize(ocspResponse);
-}
+static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc,
+ CFStringRef key) {
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
-/* Callback from async http code after an ocsp response has been received. */
-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) {
- /* Parse the returned data as if it's an ocspResponse. */
- ocspResponse = SecOCSPResponseCreate(data);
- CFRelease(data);
+ Boolean keyInPolicy = false;
+ CFArrayRef policies = pvc->policies;
+ CFIndex policyIX, policyCount = CFArrayGetCount(policies);
+ for (policyIX = 0; policyIX < policyCount; ++policyIX) {
+ SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
+ if (policy && CFDictionaryContainsKey(policy->_options, key)) {
+ keyInPolicy = true;
}
}
- SecRVCConsumeOCSPResponse(rvc, ocspResponse, maxAge, true);
- // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
+ /* We only enforce this check when *both* of the following are true:
+ * 1. One of the certs in the path has this usage constraint, and
+ * 2. One of the policies in the PVC has this key
+ * (As compared to normal policy options which require only one to be true..) */
+ require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc, key) &&
+ keyInPolicy, out);
- if (!rvc->done) {
- /* Clear the data for the next response. */
- asynchttp_free(http);
- SecRVCFetchNext(rvc);
+ /* Ignore the anchor if it's trusted */
+ if (SecPathBuilderIsAnchored(pvc->builder)) {
+ count--;
}
-
- if (rvc->done) {
- SecRVCUpdatePVC(rvc);
- SecRVCDelete(rvc);
- if (!--pvc->asyncJobCount) {
- SecPathBuilderStep(pvc->builder);
+ for (ix = 0; ix < count; ++ix) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (SecCertificateIsWeakHash(cert)) {
+ if (!leaf_is_on_weak_hash_whitelist(pvc)) {
+ if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) {
+ return;
+ }
+ }
}
}
+out:
+ return;
}
-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.token = SecPathBuilderCopyClientAuditToken(pvc->builder);
- rvc->http.completed = SecOCSPFetchCompleted;
- rvc->http.info = rvc;
- rvc->ocspRequest = NULL;
- rvc->responderIX = 0;
- rvc->responder = NULL;
- rvc->nextUpdate = NULL_TIME;
- rvc->ocspResponse = NULL;
- rvc->ocspSingleResponse = NULL;
- rvc->done = false;
-}
-
+static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc,
+ CFStringRef key) {
+ CFIndex ix, count = SecPVCGetCertificateCount(pvc);
-static bool SecPVCCheckRevocation(SecPVCRef pvc) {
- secdebug("ocsp", "checking revocation");
- CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
- bool completed = true;
- if (certCount <= 1) {
- /* Can't verify without an issuer; we're done */
- return completed;
- }
- if (!SecPVCIsAnchored(pvc)) {
- /* We can't check revocation for chains without a trusted anchor. */
- return completed;
+ Boolean keyInPolicy = false;
+ CFArrayRef policies = pvc->policies;
+ CFIndex policyIX, policyCount = CFArrayGetCount(policies);
+ for (policyIX = 0; policyIX < policyCount; ++policyIX) {
+ SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
+ if (policy && CFDictionaryContainsKey(policy->_options, key)) {
+ keyInPolicy = true;
+ }
}
- certCount--;
-#if 0
- /* TODO: Implement getting this value from the client.
- Optional responder passed in though policy. */
- CFURLRef localResponder = NULL;
- /* Generate a nonce in outgoing request if true. */
- bool genNonce = false;
- /* Require a nonce in response if true. */
- bool requireRespNonce = false;
- bool cacheReadDisable = false;
- bool cacheWriteDisable = false;
-#endif
+ /* We only enforce this check when *both* of the following are true:
+ * 1. One of the certs in the path has this usage constraint, and
+ * 2. One of the policies in the PVC has this key
+ * (As compared to normal policy options which require only one to be true..) */
+ require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc, key) &&
+ keyInPolicy, out);
- if (pvc->rvcs) {
- /* We have done revocation checking already, we're done. */
- secdebug("ocsp", "Not rechecking revocation");
- return completed;
+ /* Ignore the anchor if it's trusted */
+ if (SecPathBuilderIsAnchored(pvc->builder)) {
+ count--;
}
-
- /* Setup things so we check revocation status of all certs except the
- anchor. */
- pvc->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount);
-
-#if 0
- /* Lookup cached revocation data for each certificate. */
- for (certIX = 0; certIX < certCount; ++certIX) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
- CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
- if (ocspResponders) {
- /* First look though passed in ocsp responses. */
- //SecPVCGetOCSPResponseForCertificateAtIndex(pvc, ix, singleResponse);
-
- /* Then look though shared cache (we don't care which responder
- something came from here). */
- CFDataRef ocspResponse = SecOCSPCacheCopyMatching(SecCertIDRef certID, NULL);
-
- /* Now let's parse the response. */
- if (decodeOCSPResponse(ocspResp)) {
- secdebug("ocsp", "response ok: %@", ocspResp);
- } else {
- secdebug("ocsp", "response bad: %@", ocspResp);
- /* ocsp response not ok. */
- if (!SecPVCSetResultForced(pvc, key, ix, kCFBooleanFalse, true))
- return completed;
- }
- CFReleaseSafe(ocspResp);
- } else {
- /* Check if certificate has any crl distributionPoints. */
- CFArrayRef distributionPoints = SecCertificateGetCRLDistributionPoints(cert);
- if (distributionPoints) {
- /* Look for a cached CRL and potentially delta CRL for this certificate. */
+ for (ix = 0; ix < count; ++ix) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ if (!SecCertificateIsStrongKey(cert)) {
+ if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) {
+ return;
}
}
- }
-#endif
- /* Note that if we are multi threaded and a job completes after it
- is started but before we return from this function, we don't want
- a callback to decrement asyncJobCount to zero before we finish issuing
- all the jobs. To avoid this we pretend we issued certCount async jobs,
- and decrement pvc->asyncJobCount for each cert that we don't start a
- background fetch for. */
- pvc->asyncJobCount = (unsigned int) certCount;
-
- /* Loop though certificates again and issue an ocsp fetch if the
- revocation status checking isn't done yet. */
- 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;
+ } /* Cert loop */
+out:
+ return;
+}
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc,
- rvc->certIX);
- /* The certIX + 1 is ok here since certCount is always at least 1
- less than the actual number of certs. */
- SecCertificateRef issuer = SecPVCGetCertificateAtIndex(rvc->pvc,
- rvc->certIX + 1);
-
- rvc->ocspRequest = SecOCSPRequestCreate(cert, issuer);
-
- /* Get stapled OCSP responses */
- CFArrayRef ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
-
- /* If we have any OCSP stapled responses, check those first */
- if(ocspResponsesData) {
- secdebug("ocsp", "Checking stapled responses for cert %ld", certIX);
- CFArrayForEach(ocspResponsesData, ^(const void *value) {
- /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
- SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
- SecRVCConsumeOCSPResponse(rvc, ocspResponse, NULL_TIME, false);
- });
- CFRelease(ocspResponsesData);
+static void SecPolicyCheckPinningRequired(SecPVCRef pvc, CFStringRef key) {
+ /* Pinning is disabled on the system, skip. */
+ if (SecIsInternalRelease()) {
+ if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
+ CFSTR("com.apple.security"), NULL)) {
+ return;
}
+ }
- /* Then check the cached response */
- secdebug("ocsp", "Checking cached responses for cert %ld", certIX);
- SecRVCConsumeOCSPResponse(rvc, SecOCSPCacheCopyMatching(rvc->ocspRequest, NULL), NULL_TIME, false);
-
- /* Unless we successfully checked the revocation status of this cert
- based on the cache or stapled responses, attempt to fire off an async http request
- for this cert's revocation status. */
- bool fetch_done = true;
- if (rvc->done || !SecPathBuilderCanAccessNetwork(pvc->builder) ||
- (fetch_done = SecRVCFetchNext(rvc))) {
- /* We got a cache hit or we aren't allowed to access the network,
- or the async http post failed. */
- SecRVCUpdatePVC(rvc);
- SecRVCDelete(rvc);
- /* We didn't really start a background job for this cert. */
- pvc->asyncJobCount--;
- } else if (!fetch_done) {
- /* We started at least one background fetch. */
- completed = false;
+ CFArrayRef policies = pvc->policies;
+ CFIndex policyIX, policyCount = CFArrayGetCount(policies);
+ for (policyIX = 0; policyIX < policyCount; ++policyIX) {
+ SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
+ CFStringRef policyName = SecPolicyGetName(policy);
+ if (CFEqualSafe(policyName, CFSTR("sslServer"))) {
+ /* policy required pinning, but we didn't use a pinning policy */
+ if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) {
+ return;
+ }
}
}
-
- /* Return false if we started any background jobs. */
- /* We can't just return !pvc->asyncJobCount here, since if we started any
- jobs the completion callback will be called eventually and it will call
- SecPathBuilderStep(). If for some reason everything completed before we
- get here we still want the outer SecPathBuilderStep() to terminate so we
- keep track of whether we started any jobs and return false if so. */
- return completed;
}
-
-void SecPolicyServerInitalize(void) {
+void SecPolicyServerInitialize(void) {
gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, NULL);
gSecPolicyPathCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
kSecPolicyCheckExtendedKeyUsage,
SecPolicyCheckExtendedKeyUsage);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckBasicContraints,
- SecPolicyCheckBasicContraints);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckBasicConstraints,
+ SecPolicyCheckBasicConstraints);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckNonEmptySubject,
SecPolicyCheckNonEmptySubject);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckAnchorSHA1,
SecPolicyCheckAnchorSHA1);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckIntermediateSPKISHA256,
- SecPolicyCheckIntermediateSPKISHA256);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckAnchorSHA256,
+ SecPolicyCheckAnchorSHA256);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckAnchorApple,
SecPolicyCheckAnchorApple);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckRevocationResponseRequired,
SecPolicyCheckRevocationResponseRequired);
+ CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckRevocationOnline,
+ SecPolicyCheckRevocationOnline);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckNoNetworkAccess,
SecPolicyCheckNoNetworkAccess);
CFDictionaryAddValue(gSecPolicyLeafCallbacks,
kSecPolicyCheckLeafMarkerOid,
SecPolicyCheckLeafMarkerOid);
+ CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckLeafMarkerOidWithoutValueCheck,
+ SecPolicyCheckLeafMarkerOidWithoutValueCheck);
+ CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckLeafMarkersProdAndQA,
+ SecPolicyCheckLeafMarkersProdAndQA);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckIntermediateSPKISHA256,
+ SecPolicyCheckIntermediateSPKISHA256);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckIntermediateEKU,
+ SecPolicyCheckIntermediateEKU);
CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckIntermediateMarkerOid,
SecPolicyCheckIntermediateMarkerOid);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
kSecPolicyCheckCertificatePolicy,
SecPolicyCheckCertificatePolicyOid);
-}
-
-/* AUDIT[securityd](done):
- array (ok) is a caller provided array, only its cf type has
- been checked.
- The options (ok) field ends up in policy->_options unchecked, so every access
- of policy->_options needs to be validated.
- */
-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;
-}
-
-/* AUDIT[securityd](done):
- value (ok) is an element in a caller provided array.
- */
-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);
- }
- }
-}
-
-/* AUDIT[securityd](done):
- serializedPolicies (ok) is a caller provided array, only its cf type has
- been checked.
- */
-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;
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckWeakIntermediates,
+ SecPolicyCheckWeakIntermediates);
+ CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckWeakLeaf,
+ SecPolicyCheckWeakLeaf);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckWeakRoot,
+ SecPolicyCheckWeakRoot);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckKeySize,
+ SecPolicyCheckKeySize);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckSignatureHashAlgorithms,
+ SecPolicyCheckSignatureHashAlgorithms);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckSystemTrustedWeakHash,
+ SecPolicyCheckSystemTrustedWeakHash);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckSystemTrustedWeakKey,
+ SecPolicyCheckSystemTrustedWeakKey);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckIntermediateOrganization,
+ SecPolicyCheckIntermediateOrganization);
+ CFDictionaryAddValue(gSecPolicyPathCallbacks,
+ kSecPolicyCheckIntermediateCountry,
+ SecPolicyCheckIntermediateCountry);
+ CFDictionaryAddValue(gSecPolicyLeafCallbacks,
+ kSecPolicyCheckPinningRequired,
+ SecPolicyCheckPinningRequired);
}
// MARK: -
****************** SecPVCRef Functions *****************
********************************************************/
-void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies,
- CFAbsoluteTime verifyTime) {
+void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies) {
secdebug("alloc", "%p", pvc);
// Weird logging policies crashes.
//secdebug("policy", "%@", policies);
+
+ // Zero the pvc struct so only non-zero fields need to be explicitly set
+ memset(pvc, 0, sizeof(struct OpaqueSecPVC));
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->response_required = false;
- pvc->optionally_ev = false;
- pvc->is_ev = false;
- pvc->result = true;
-}
+ pvc->result = kSecTrustResultUnspecified;
-static void SecPVCDeleteRVCs(SecPVCRef pvc) {
- secdebug("alloc", "%p", pvc);
- if (pvc->rvcs) {
- free(pvc->rvcs);
- pvc->rvcs = NULL;
- }
+ CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ pvc->leafDetails = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail,
+ 1, &kCFTypeArrayCallBacks);
+ CFRelease(certDetail);
}
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->leafDetails);
}
-void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathRef path,
- CF_CONSUMED CFArrayRef details) {
+void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathVCRef path) {
secdebug("policy", "%@", path);
- if (pvc->path != path) {
- /* Changing path makes us clear the Revocation Verification Contexts */
- 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;
+ pvc->result = kSecTrustResultUnspecified;
+ CFReleaseNull(pvc->details);
+}
+
+void SecPVCComputeDetails(SecPVCRef pvc, SecCertificatePathVCRef path) {
+ pvc->policyIX = 0;
+
+ /* Since we don't run the LeafChecks again, we need to preserve the
+ * result the leaf had. */
+ CFIndex ix, pathLength = SecCertificatePathVCGetCount(path);
+ CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
+ pathLength, pvc->leafDetails);
+ for (ix = 1; ix < pathLength; ++ix) {
+ CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFArrayAppendValue(details, certDetail);
+ CFRelease(certDetail);
+ }
+ CFRetainAssign(pvc->details, details);
+ pvc->result = pvc->leafResult;
+ CFReleaseSafe(details);
}
SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) {
return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
}
-CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
- return SecCertificatePathGetCount(pvc->path);
+static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
+ return SecPathBuilderGetCertificateCount(pvc->builder);
}
-SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
- return SecCertificatePathGetCertificateAtIndex(pvc->path, ix);
+static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
+ return SecPathBuilderGetCertificateAtIndex(pvc->builder, ix);
}
-bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc, CFIndex ix) {
- return SecCertificatePathSelfSignedIndex(pvc->path) == ix;
+static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
+ return SecPathBuilderGetVerifyTime(pvc->builder);
}
-void SecPVCSetCheckRevocation(SecPVCRef pvc) {
- pvc->check_revocation = true;
- secdebug("ocsp", "deferred revocation checking enabled");
+static bool SecPVCIsExceptedError(SecPVCRef pvc, CFIndex ix, CFStringRef key, CFTypeRef value) {
+ CFArrayRef exceptions = SecPathBuilderGetExceptions(pvc->builder);
+ if (!exceptions) { return false; }
+ CFIndex exceptionsCount = CFArrayGetCount(exceptions);
+
+ /* There are two types of exceptions:
+ * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
+ * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
+ * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
+ */
+#if TARGET_OS_OSX
+ CFDictionaryRef options = CFArrayGetValueAtIndex(exceptions, 0);
+ /* Type 2 */
+ if (exceptionsCount == 1 && (ix > 0 || !CFDictionaryContainsKey(options, kSecCertificateDetailSHA1Digest))) {
+ /* SHA1Digest not allowed */
+ if (CFDictionaryContainsKey(options, kSecCertificateDetailSHA1Digest)) { return false; }
+ /* Key excepted */
+ if (CFDictionaryContainsKey(options, key)) {
+ /* Special case -- AnchorTrusted only for self-signed certs */
+ if (CFEqual(kSecPolicyCheckAnchorTrusted, key)) {
+ Boolean isSelfSigned = false;
+ SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(pvc->builder, ix);
+ if (!cert || (errSecSuccess != SecCertificateIsSelfSigned(cert, &isSelfSigned)) || !isSelfSigned) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+#endif
+
+ /* Type 1 */
+ if (ix >= exceptionsCount) { return false; }
+ CFDictionaryRef exception = CFArrayGetValueAtIndex(exceptions, ix);
+
+ /* Compare the cert hash */
+ if (!CFDictionaryContainsKey(exception, kSecCertificateDetailSHA1Digest)) { return false; }
+ SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(pvc->builder, ix);
+ if (!CFEqual(SecCertificateGetSHA1Digest(cert), CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest))) {
+ return false;
+ }
+
+ /* Key Excepted */
+ CFTypeRef exceptionValue = CFDictionaryGetValue(exception, key);
+ if (exceptionValue && CFEqual(value, exceptionValue)) {
+ /* Only change result if PVC is already ok */
+ if (SecPVCIsOkResult(pvc)) {
+ // Chains that pass due to exceptions get Proceed result.
+ pvc->result = kSecTrustResultProceed;
+ }
+ return true;
+ }
+
+ return false;
}
-void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc) {
- pvc->response_required = true;
- secdebug("ocsp", "revocation response required");
+static int32_t detailKeyToCssmErr(CFStringRef key) {
+ int32_t result = 0;
+
+ if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
+ result = -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
+ }
+ else if (CFEqual(key, kSecPolicyCheckEmail)) {
+ result = -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
+ }
+ else if (CFEqual(key, kSecPolicyCheckValidLeaf) ||
+ CFEqual(key, kSecPolicyCheckValidIntermediates) ||
+ CFEqual(key, kSecPolicyCheckValidRoot)) {
+ result = -2147409654; // CSSMERR_TP_CERT_EXPIRED
+ }
+
+ return result;
}
-bool SecPVCIsAnchored(SecPVCRef pvc) {
- return SecCertificatePathIsAnchored(pvc->path);
+static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint);
+
+static bool SecPVCIsAllowedError(SecPVCRef pvc, CFIndex ix, CFStringRef key) {
+ bool result = false;
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, ix);
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 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;
}
-CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
- return pvc->verifyTime;
+static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc, CFStringRef key) {
+ CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
+ for (certIX = 0; certIX < certCount; certIX++) {
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, certIX);
+ CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints);
+ for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) {
+ CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX);
+ if (!isDictionary(constraint)) {
+ continue;
+ }
+
+ CFDictionaryRef policyOptions = NULL;
+ policyOptions = (CFDictionaryRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyOptions);
+ if (policyOptions && isDictionary(policyOptions) &&
+ CFDictionaryContainsKey(policyOptions, key)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/* AUDIT[securityd](done):
bool SecPVCSetResultForced(SecPVCRef pvc,
CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
- secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
+ secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
(pvc->callbacks == gSecPolicyLeafCallbacks ? "leaf"
: (pvc->callbacks == gSecPolicyPathCallbacks ? "path"
: "custom")),
/* If this is not something the current policy cares about ignore
this error and return true so our caller continues evaluation. */
if (!force) {
- /* @@@ The right long term fix might be to check if none of the passed
- in policies contain this key, since not all checks are run for all
- policies. */
+ /* Either the policy or the usage constraints have to have this key */
SecPolicyRef policy = SecPVCGetPolicy(pvc);
- if (policy && !CFDictionaryContainsKey(policy->_options, key))
+ if (!(SecPVCKeyIsConstraintPolicyOption(pvc, key) ||
+ (policy && CFDictionaryContainsKey(policy->_options, key)))) {
return true;
+ }
}
- /* @@@ Check to see if the SecTrustSettings for the certificate in question
+ /* Check to see if the SecTrustSettings for the certificate in question
tell us to ignore this error. */
- pvc->result = false;
+ if (SecPVCIsAllowedError(pvc, ix, key)) {
+ secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
+ return true;
+ }
+
+ /* Check to see if exceptions tells us to ignore this error. */
+ if (SecPVCIsExceptedError(pvc, ix, key, result)) {
+ secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key);
+ return true;
+ }
+
+ /* Check SecPVCIsOkResult to avoid resetting deny or fatal to recoverable */
+ if (SecPVCIsOkResult(pvc)) {
+ pvc->result = kSecTrustResultRecoverableTrustFailure;
+ }
if (!pvc->details)
return false;
/* If our caller doesn't want full details and we failed earlier there is
no point in doing additional checks. */
- if (!pvc->result && !pvc->details)
+ if (!SecPVCIsOkResult(pvc) && !pvc->details)
return;
SecPolicyCheckFunction fcn = (SecPolicyCheckFunction)
CFDictionaryGetValue(pvc->callbacks, key);
if (!fcn) {
-#if 0
- /* Why not to have optional policy checks rant:
- Not all keys are in all dictionaries anymore, so why not make checks
- optional? This way a client can ask for something and the server will
- do a best effort based on the supported flags. It works since they are
- synchronized now, but we need some debug checking here for now. */
- pvc->result = false;
-#endif
if (pvc->callbacks == gSecPolicyLeafCallbacks) {
if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks, key)) {
- pvc->result = false;
+ pvc->result = kSecTrustResultOtherError;
}
} else if (pvc->callbacks == gSecPolicyPathCallbacks) {
if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks, key)) {
- pvc->result = false;
+ pvc->result = kSecTrustResultOtherError;
}
} else {
- /* Non standard validation phase, nothing is optional. */
- pvc->result = false;
+ /* Non standard validation phase. This may be a new key from the
+ * pinning DB which is not implemented in this OS version. Log
+ * a warning. */
+ secwarning("policy: unknown policy key %@, skipping", key);
}
return;
}
policy->_options is a caller provided dictionary, only its cf type has
been checked.
*/
-bool SecPVCLeafChecks(SecPVCRef pvc) {
- pvc->result = true;
+SecTrustResultType SecPVCLeafChecks(SecPVCRef pvc) {
+ /* We need to compute details for the leaf. */
+ CFRetainAssign(pvc->details, pvc->leafDetails);
+
CFArrayRef policies = pvc->policies;
CFIndex ix, count = CFArrayGetCount(policies);
for (ix = 0; ix < count; ++ix) {
/* Validate all keys for all policies. */
pvc->callbacks = gSecPolicyLeafCallbacks;
CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
- if (!pvc->result && !pvc->details)
- return pvc->result;
}
+ pvc->leafResult = pvc->result;
+ CFRetainAssign(pvc->leafDetails, pvc->details);
+
return pvc->result;
}
+bool SecPVCIsOkResult(SecPVCRef pvc) {
+ if (pvc->result == kSecTrustResultRecoverableTrustFailure ||
+ pvc->result == kSecTrustResultDeny ||
+ pvc->result == kSecTrustResultFatalTrustFailure ||
+ pvc->result == kSecTrustResultOtherError) {
+ return false;
+ }
+ return true;
+}
+
bool SecPVCParentCertificateChecks(SecPVCRef pvc, CFIndex ix) {
/* Check stuff common to intermediate and anchors. */
- CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
- && SecPVCIsAnchored(pvc));
- if (!SecCertificateIsValid(cert, verifyTime)) {
- /* Certificate has expired. */
- if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckValidRoot
- : kSecPolicyCheckValidIntermediates, ix, kCFBooleanFalse))
+ CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
+ CFIndex anchor_ix = SecPVCGetCertificateCount(pvc) - 1;
+ bool is_anchor = (ix == anchor_ix && SecPathBuilderIsAnchored(pvc->builder));
+
+ if (!SecCertificateIsValid(cert, verifyTime)) {
+ /* Certificate has expired. */
+ if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckValidRoot
+ : kSecPolicyCheckValidIntermediates, ix, kCFBooleanFalse)) {
goto errOut;
- }
+ }
+ }
+
+ if (SecCertificateIsWeakKey(cert)) {
+ /* Certificate uses weak key. */
+ if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckWeakRoot
+ : kSecPolicyCheckWeakIntermediates, ix, kCFBooleanFalse)) {
+ goto errOut;
+ }
+ }
if (is_anchor) {
/* Perform anchor specific checks. */
} else {
/* Perform intermediate specific checks. */
- /* (k) */
- const SecCEBasicConstraints *bc =
- SecCertificateGetBasicConstraints(cert);
- if (!bc || !bc->isCA) {
- /* Basic constraints not present or not marked as isCA, illegal. */
- if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicContraints,
- ix, kCFBooleanFalse, true))
- goto errOut;
+ /* (k) Basic constraints only relevant for v3 and later. */
+ if (SecCertificateVersion(cert) >= 3) {
+ const SecCEBasicConstraints *bc =
+ SecCertificateGetBasicConstraints(cert);
+ if (!bc || !bc->isCA) {
+ /* Basic constraints not present or not marked as isCA, illegal. */
+ if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicConstraints,
+ ix, kCFBooleanFalse, true)) {
+ goto errOut;
+ }
+ }
+ }
+ /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
+ not an anchor), we additionally require that the certificate chain
+ does not end in a v3 or later anchor. [rdar://32204517] */
+ else if (ix > 0 && ix < anchor_ix) {
+ SecCertificateRef anchor = SecPVCGetCertificateAtIndex(pvc, anchor_ix);
+ if (SecCertificateVersion(anchor) >= 3) {
+ if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicConstraints,
+ ix, kCFBooleanFalse, true)) {
+ goto errOut;
+ }
+ }
}
- /* Consider adding (l) max_path_length checking here. */
+ /* (l) max_path_length is checked elsewhere. */
/* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) {
if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage,
- ix, kCFBooleanFalse, true))
+ ix, kCFBooleanFalse, true)) {
goto errOut;
+ }
}
}
errOut:
- return pvc->result;
+ return SecPVCIsOkResult(pvc);
}
-bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) {
+static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) {
/* Check stuff common to intermediate and anchors. */
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != blackListedKeys)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
- && SecPVCIsAnchored(pvc));
- if (!is_anchor) {
- /* Check for blacklisted intermediates keys. */
- CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
- if (dgst) {
- /* Check dgst against blacklist. */
- if (CFSetContainsValue(blackListedKeys, dgst)) {
- SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey,
- ix, kCFBooleanFalse, true);
- }
- CFRelease(dgst);
- }
- }
+ CFIndex count = SecPVCGetCertificateCount(pvc);
+ bool is_last = (ix == count - 1);
+ bool is_anchor = (is_last && SecPathBuilderIsAnchored(pvc->builder));
+ if (!is_anchor) {
+ /* Check for blacklisted intermediate issuer keys. */
+ CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
+ if (dgst) {
+ /* Check dgst against blacklist. */
+ if (CFSetContainsValue(blackListedKeys, dgst)) {
+ /* Check allow list for this blacklisted issuer key,
+ which is the authority key of the issued cert at ix-1.
+ */
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ bool allowed = path && SecCertificatePathVCIsAllowlisted(path);
+ if (!allowed) {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey,
+ ix, kCFBooleanFalse, true);
+ pvc->result = kSecTrustResultFatalTrustFailure;
+ }
+ }
+ CFRelease(dgst);
+ }
+ }
CFRelease(blackListedKeys);
- return pvc->result;
+ return SecPVCIsOkResult(pvc);
}
}
// Assume OK
return true;
}
-bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix)
+static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix)
{
- /* Check stuff common to intermediate and anchors. */
+ /* Check stuff common to intermediate and anchors. */
SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL != otapkiRef)
{
if (NULL != grayListKeys)
{
SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
- && SecPVCIsAnchored(pvc));
- if (!is_anchor) {
- /* Check for gray listed intermediates keys. */
- CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
- if (dgst) {
- /* Check dgst against gray list. */
- if (CFSetContainsValue(grayListKeys, dgst)) {
- SecPVCSetResultForced(pvc, kSecPolicyCheckGrayListedKey,
- ix, kCFBooleanFalse, true);
- }
- CFRelease(dgst);
- }
- }
+ CFIndex count = SecPVCGetCertificateCount(pvc);
+ bool is_last = (ix == count - 1);
+ bool is_anchor = (is_last && SecPathBuilderIsAnchored(pvc->builder));
+ if (!is_anchor) {
+ /* Check for gray listed intermediate issuer keys. */
+ CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
+ if (dgst) {
+ /* Check dgst against gray list. */
+ if (CFSetContainsValue(grayListKeys, dgst)) {
+ /* Check allow list for this graylisted issuer key,
+ which is the authority key of the issued cert at ix-1.
+ */
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ bool allowed = path && SecCertificatePathVCIsAllowlisted(path);
+ if (!allowed) {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckGrayListedKey,
+ ix, kCFBooleanFalse, true);
+ }
+ }
+ CFRelease(dgst);
+ }
+ }
CFRelease(grayListKeys);
- return pvc->result;
+ return SecPVCIsOkResult(pvc);
}
}
// Assume ok
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);
+ /* Prefer a match of both name and OID */
+ if (searchOid && searchName && policyOid && policyName) {
+ if (CFEqual(searchOid, policyOid) &&
+ CFEqual(searchName, policyName)) {
+ if (policyIX) { *policyIX = ix; }
+ return true;
+ }
+ }
+ /* Next best is just OID. */
+ 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);
+ /* Have to look for all the possible locations of name string */
+ 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;
+ }
+
+ /* We're using the key for revocation if we have the OCSPSigner policy.
+ * @@@ If we support CRLs, we'd need to check for that policy here too.
+ */
+ if (SecPVCContainsPolicy(pvc, kSecPolicyAppleOCSPSigner, NULL, NULL)) {
+ ourKeyUsage |= kSecTrustSettingsKeyUseSignRevocation;
+ }
+
+ /* We're using the key for verifying a cert if it's a root/intermediate
+ * in the chain. If the cert isn't in the path yet, we're about to add it,
+ * so it's a root/intermediate. If there is no path, this is the leaf.
+ */
+ CFIndex pathIndex = -1;
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ if (path) {
+ pathIndex = SecCertificatePathVCGetIndexOfCertificate(path, certificate);
+ } else {
+ pathIndex = 0;
+ }
+ if (pathIndex != 0) {
+ ourKeyUsage |= kSecTrustSettingsKeyUseSignCert;
+ }
+
+ /* The rest of the key usages may be specified by the policy(ies). */
+ if (policyIX >= 0 && policyIX < CFArrayGetCount(pvc->policies)) {
+ SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, policyIX);
+ ourKeyUsage |= ts_key_usage_for_policy(policy);
+ } else {
+ /* Get key usage from ALL policies */
+ 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_OSX
+#include <Security/SecTrustedApplicationPriv.h>
+#include <Security/SecTask.h>
+#include <Security/SecTaskPriv.h>
+#include <bsm/libbsm.h>
+#include <libproc.h>
+
+extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
+
+static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken, CFTypeRef appRef) {
+ bool result = false;
+ audit_token_t auditToken = {};
+ SecTaskRef task = NULL;
+ SecRequirementRef requirement = NULL;
+ CFStringRef stringRequirement = NULL;
+
+ require(appRef && clientAuditToken, out);
+ require(CFGetTypeID(appRef) == SecTrustedApplicationGetTypeID(), out);
+ require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef)appRef, &requirement), out);
+ require(requirement, out);
+ require_noerr(SecRequirementsCopyString(requirement, kSecCSDefaultFlags, &stringRequirement), out);
+ require(stringRequirement, out);
+
+ require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
+ CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
+ require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out);
+
+ if(errSecSuccess == SecTaskValidateForRequirement(task, stringRequirement)) {
+ result = true;
+ }
+
+out:
+ CFReleaseNull(task);
+ CFReleaseNull(requirement);
+ CFReleaseNull(stringRequirement);
+ return result;
+}
+#endif
+
+static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc, CFDictionaryRef options) {
+ if (!isDictionary(options)) {
+ return false;
+ }
+
+ /* Push */
+ CFDictionaryRef currentCallbacks = pvc->callbacks;
+
+ /* We need to run the leaf and path checks using these options. */
+ pvc->callbacks = gSecPolicyLeafCallbacks;
+ CFDictionaryApplyFunction(options, SecPVCValidateKey, pvc);
+
+ pvc->callbacks = gSecPolicyPathCallbacks;
+ CFDictionaryApplyFunction(options, SecPVCValidateKey, pvc);
+
+ /* Pop */
+ pvc->callbacks = currentCallbacks;
+
+ /* Our work here is done; no need to claim a match */
+ return false;
+}
+
+static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint) {
+ CFStringRef policyOid = NULL, policyString = NULL, policyName = NULL;
+ CFNumberRef keyUsageNumber = NULL;
+ CFTypeRef trustedApplicationData = NULL;
+ CFDictionaryRef policyOptions = NULL;
+
+ bool policyMatch = false, policyStringMatch = false, applicationMatch = false ,
+ keyUsageMatch = false, policyOptionMatch = false;
+ bool result = false;
+
+#if TARGET_OS_MAC && !TARGET_OS_IPHONE
+ /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
+ 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);
+ policyOptions = (CFDictionaryRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyOptions);
+
+ CFIndex policyIX = -1;
+ policyMatch = SecPVCContainsPolicy(pvc, policyOid, policyName, &policyIX);
+ policyStringMatch = SecPVCContainsString(pvc, policyIX, policyString);
+ keyUsageMatch = SecPVCContainsTrustSettingsKeyUsage(pvc, certificate, policyIX, keyUsageNumber);
+ policyOptionMatch = SecPVCContainsTrustSettingsPolicyOption(pvc, policyOptions);
+
+#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 we either didn't find the parameter in the dictionary or we got a match
+ * against that parameter, for all possible parameters in the dictionary, then
+ * this trust setting result applies to the output. */
+ if (((!policyOid && !policyName) || policyMatch) &&
+ (!policyString || policyStringMatch) &&
+ (!trustedApplicationData || applicationMatch) &&
+ (!keyUsageNumber || keyUsageMatch) &&
+ (!policyOptions || policyOptionMatch)) {
+ 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)) {
+ /* no SecTrustSettingsResult entry defaults to TrustRoot*/
+ resultValue = kSecTrustSettingsResultTrustRoot;
+ }
+
+ if (SecPVCMeetsConstraint(pvc, certificate, constraint)) {
+ result = resultValue;
+ break;
+ }
+ }
+ return result;
+}
+
+static void SecPVCCheckUsageConstraints(SecPVCRef pvc) {
+ CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
+ for (certIX = 0; certIX < certCount; certIX++) {
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, certIX);
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, certIX);
+ SecTrustSettingsResult result = SecPVCGetTrustSettingsResult(pvc, cert, constraints);
+
+ /* Set the pvc trust result based on the usage constraints and anchor source. */
+ if (result == kSecTrustSettingsResultDeny) {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckUsageConstraints, certIX, kCFBooleanFalse, true);
+ pvc->result = kSecTrustResultDeny;
+ } else if ((result == kSecTrustSettingsResultTrustRoot || result == kSecTrustSettingsResultTrustAsRoot ||
+ result == kSecTrustSettingsResultInvalid) && SecPVCIsOkResult(pvc)) {
+ /* If we already think the PVC is ok and this cert is from one of the user/
+ * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
+ * all mean we should use the special "Proceed" trust result. */
+#if TARGET_OS_IPHONE
+ if (SecPathBuilderIsAnchorSource(pvc->builder, kSecUserAnchorSource) &&
+ SecCertificateSourceContains(kSecUserAnchorSource, cert)) {
+#else
+ if (SecPathBuilderIsAnchorSource(pvc->builder, kSecLegacyAnchorSource) &&
+ SecCertificateSourceContains(kSecLegacyAnchorSource, cert)) {
+#endif
+ pvc->result = kSecTrustResultProceed;
+ }
+ }
+ }
+}
+
+#define kSecPolicySHA256Size 32
+static const UInt8 kTestDateConstraintsRoot[kSecPolicySHA256Size] = {
+ 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
+ 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
+};
+static const UInt8 kWS_CA1_G2[kSecPolicySHA256Size] = {
+ 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
+ 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
+};
+static const UInt8 kWS_CA1_NEW[kSecPolicySHA256Size] = {
+ 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
+ 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
+};
+static const UInt8 kWS_CA2_NEW[kSecPolicySHA256Size] = {
+ 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
+ 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
+};
+static const UInt8 kWS_ECC[kSecPolicySHA256Size] = {
+ 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
+ 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
+};
+static const UInt8 kSC_SFSCA[kSecPolicySHA256Size] = {
+ 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
+ 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
+};
+static const UInt8 kSC_SHA2[kSecPolicySHA256Size] = {
+ 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
+ 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
+};
+static const UInt8 kSC_G2[kSecPolicySHA256Size] = {
+ 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
+ 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
+};
+
+static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc) {
+ static CFSetRef sConstrainedRoots = NULL;
+ static dispatch_once_t _t;
+ dispatch_once(&_t, ^{
+ const UInt8 *v_hashes[] = {
+ kWS_CA1_G2, kWS_CA1_NEW, kWS_CA2_NEW, kWS_ECC,
+ kSC_SFSCA, kSC_SHA2, kSC_G2, kTestDateConstraintsRoot
+ };
+ CFMutableSetRef set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+ CFIndex ix, count = sizeof(v_hashes)/sizeof(*v_hashes);
+ for (ix=0; ix<count; ix++) {
+ CFDataRef hash = CFDataCreateWithBytesNoCopy(NULL, v_hashes[ix],
+ kSecPolicySHA256Size, kCFAllocatorNull);
+ if (hash) {
+ CFSetAddValue(set, hash);
+ CFRelease(hash);
+ }
+ }
+ sConstrainedRoots = set;
+ });
+
+ bool shouldDeny = false;
+ CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
+ for (certIX = certCount - 1; certIX >= 0 && !shouldDeny; certIX--) {
+ SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, certIX);
+ CFDataRef sha256 = SecCertificateCopySHA256Digest(cert);
+ if (sha256 && CFSetContainsValue(sConstrainedRoots, sha256)) {
+ /* matched a constrained root; check notBefore dates on all its children. */
+ CFIndex childIX = certIX;
+ while (--childIX >= 0) {
+ SecCertificateRef child = SecPVCGetCertificateAtIndex(pvc, childIX);
+ /* 1 Dec 2016 00:00:00 GMT */
+ if (child && (CFAbsoluteTime)502243200.0 <= SecCertificateNotValidBefore(child)) {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey, certIX, kCFBooleanFalse, true);
+ pvc->result = kSecTrustResultFatalTrustFailure;
+ shouldDeny = true;
+ break;
+ }
+ }
+ }
+ CFReleaseNull(sha256);
+ }
+}
+
/* AUDIT[securityd](done):
policy->_options is a caller provided dictionary, only its cf type has
been checked.
*/
-bool SecPVCPathChecks(SecPVCRef pvc) {
- secdebug("policy", "begin path: %@", pvc->path);
- bool completed = true;
+void SecPVCPathChecks(SecPVCRef pvc) {
+ secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc->builder));
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
/* This needs to be initialized before we call any function that might call
SecPVCSetResultForced(). */
pvc->policyIX = 0;
SecPolicyCheckIdLinkage(pvc, kSecPolicyCheckIdLinkage);
- if (pvc->result || pvc->details) {
+ if (SecPVCIsOkResult(pvc) || pvc->details) {
+ /* @@@ This theoretically only needs to be done once per path, but since
+ this function affects the pvc result, we'll run it every time. */
SecPolicyCheckBasicCertificateProcessing(pvc,
kSecPolicyCheckBasicCertificateProcessing);
}
CFArrayRef policies = pvc->policies;
- CFIndex count = CFArrayGetCount(policies);
- for (; pvc->policyIX < count; ++pvc->policyIX) {
+ CFIndex count = CFArrayGetCount(policies);
+ for (; pvc->policyIX < count; ++pvc->policyIX) {
/* Validate all keys for all policies. */
pvc->callbacks = gSecPolicyPathCallbacks;
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
+ SecPolicyRef policy = SecPVCGetPolicy(pvc);
CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
- if (!pvc->result && !pvc->details)
- return completed;
- }
+ if (!SecPVCIsOkResult(pvc) && !pvc->details)
+ return;
+ }
- /* Check the things we can't check statically for the certificate path. */
- /* Critical Extensions, chainLength. */
+ // Reset
+ pvc->policyIX = 0;
- /* Policy tests. */
- 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;
- /* If ev checking failed, we still want to accept this chain
- as a non EV one, if it was valid as such. */
- pvc->result = pre_ev_check_result;
- }
- /* Check revocation only if the chain is valid so far. Then only check
- revocation if the client asked for it explicitly or is_ev is
- true. */
- if (pvc->result && (pvc->is_ev || pvc->check_revocation)) {
- completed = SecPVCCheckRevocation(pvc);
- }
+ /* Check whether the TrustSettings say to deny a cert in the path. */
+ SecPVCCheckUsageConstraints(pvc);
+
+ /* Check for Blocklisted certs */
+ SecPVCCheckIssuerDateConstraints(pvc);
+ CFIndex ix;
+ count = SecCertificatePathVCGetCount(path);
+ for (ix = 1; ix < count; ix++) {
+ SecPVCGrayListedKeyChecks(pvc, ix);
+ SecPVCBlackListedKeyChecks(pvc, ix);
+ }
+
+ /* Path-based check tests. */
+ if (!SecCertificatePathVCIsPathValidated(path)) {
+ bool ev_check_ok = false;
+ if (SecCertificatePathVCIsOptionallyEV(path)) {
+ SecTrustResultType pre_ev_check_result = pvc->result;
+ SecPolicyCheckEV(pvc, kSecPolicyCheckExtendedValidation);
+ ev_check_ok = SecPVCIsOkResult(pvc);
+ /* If ev checking failed, we still want to accept this chain
+ as a non EV one, if it was valid as such. */
+ pvc->result = pre_ev_check_result;
+ }
- /* Check for CT */
- if (pvc->result || pvc->details) {
+ /* Check for CT */
/* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
SecPolicyCheckCT(pvc, kSecPolicyCheckCertificateTransparency);
- }
+ /* Certs are only EV if they are also CT verified */
+ if (ev_check_ok && SecCertificatePathVCIsCT(path)) {
+ SecCertificatePathVCSetIsEV(path, true);
+ }
+ }
//errOut:
- secdebug("policy", "end %strusted completed: %d path: %@",
- (pvc->result ? "" : "not "), completed, pvc->path);
- return completed;
-}
-
-/* This function returns 0 to indicate revocation checking was not completed
- for this certificate chain, otherwise return to date at which the first
- piece of revocation checking info we used expires. */
-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) {
- /* We allow for CA certs to not be revocation checked if they
- have no ocspResponders to check against, but the leaf
- must be checked in order for us to claim we did revocation
- checking. */
- SecCertificateRef cert =
- SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX);
- CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
- if (!ocspResponders || CFArrayGetCount(ocspResponders) == 0) {
- /* We can't check this cert so we don't consider it a soft
- failure that we didn't. Ideally we should support crl
- checking and remove this workaround, since that more
- strict. */
- continue;
- }
+ secdebug("policy", "end %strusted path: %@",
+ (SecPVCIsOkResult(pvc) ? "" : "not "), SecPathBuilderGetPath(pvc->builder));
+
+ SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc->builder));
+ return;
+}
+
+void SecPVCPathCheckRevocationRequired(SecPVCRef pvc) {
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ CFIndex ix, certCount = SecCertificatePathVCGetCount(path);
+ for (ix = 0; ix < certCount; ix++) {
+ /* If we require revocation (for that cert per the SecCertificateVCRef or
+ * per the pvc) */
+ if (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path, ix) ||
+ ((ix == 0) && pvc->require_revocation_response)) {
+ /* Do we have a valid revocation response? */
+ SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, ix);
+ if (SecRVCGetEarliestNextUpdate(rvc) == NULL_TIME) {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired,
+ ix, kCFBooleanFalse, true);
}
- 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
- /* Perhaps we don't want to do this since some policies might
- ignore the certificate expiration but still use revocation
- checking. */
-
- /* Earliest certificate expiration date. */
- 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;
}