+++ /dev/null
-/*
- * Copyright (c) 2008-2012 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * SecPolicyServer.c - Trust policies dealing with certificate revocation.
- */
-
-#include <securityd/SecPolicyServer.h>
-#include <Security/SecPolicyInternal.h>
-#include <Security/SecPolicyPriv.h>
-#include <utilities/SecIOFormat.h>
-#include <securityd/asynchttp.h>
-#include <securityd/policytree.h>
-#include <CoreFoundation/CFTimeZone.h>
-#include <wctype.h>
-#include <libDER/oids.h>
-#include <CoreFoundation/CFNumber.h>
-#include <Security/SecCertificateInternal.h>
-#include <AssertMacros.h>
-#include <utilities/debugging.h>
-#include <security_asn1/SecAsn1Coder.h>
-#include <security_asn1/ocspTemplates.h>
-#include <security_asn1/oidsalg.h>
-#include <security_asn1/oidsocsp.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <Security/SecFramework.h>
-#include <Security/SecPolicyInternal.h>
-#include <Security/SecTrustPriv.h>
-#include <Security/SecInternal.h>
-#include <CFNetwork/CFHTTPMessage.h>
-#include <CFNetwork/CFHTTPStream.h>
-#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
-#include <asl.h>
-#include <securityd/SecOCSPRequest.h>
-#include <securityd/SecOCSPResponse.h>
-#include <securityd/asynchttp.h>
-#include <securityd/SecTrustServer.h>
-#include <securityd/SecOCSPCache.h>
-#include <utilities/array_size.h>
-#include <utilities/SecCFWrappers.h>
-#include "OTATrustUtilities.h"
-
-#define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
-
-/* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
-#ifndef DUMP_OCSPRESPONSES
-#define DUMP_OCSPRESPONSES 0
-#endif
-
-#if DUMP_OCSPRESPONSES
-
-#include <unistd.h>
-#include <fcntl.h>
-
-static void secdumpdata(CFDataRef data, const char *name) {
- int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- write(fd, CFDataGetBytePtr(data), CFDataGetLength(data));
- close(fd);
-}
-
-#endif
-
-
-/********************************************************
- ****************** SecPolicy object ********************
- ********************************************************/
-
-static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL;
-static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL;
-
-static CFArrayRef SecPolicyAnchorDigestsForEVPolicy(const DERItem *policyOID)
-{
- CFArrayRef result = NULL;
- SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL == otapkiRef)
- {
- return result;
- }
-
- CFDictionaryRef evToPolicyAnchorDigest = SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef);
- CFRelease(otapkiRef);
-
- if (NULL == evToPolicyAnchorDigest)
- {
- return result;
- }
-
- CFArrayRef roots = NULL;
- CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, policyOID);
- if (oid && evToPolicyAnchorDigest)
- {
- result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid);
- if (roots && CFGetTypeID(result) != CFArrayGetTypeID())
- {
- ocspdErrorLog("EVRoot.plist has non array value");
- result = NULL;
- }
- CFRelease(oid);
- }
- return result;
-}
-
-
-static bool SecPolicyIsEVPolicy(const DERItem *policyOID) {
- return SecPolicyAnchorDigestsForEVPolicy(policyOID);
-}
-
-static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate,
- policy_set_t valid_policies) {
- /* Ensure that this certificate is a valid anchor for one of the
- certificate policy oids specified in the leaf. */
- CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
- policy_set_t ix;
- bool good_ev_anchor = false;
- for (ix = valid_policies; ix; ix = ix->oid_next) {
- CFArrayRef digests = SecPolicyAnchorDigestsForEVPolicy(&ix->oid);
- if (digests && CFArrayContainsValue(digests,
- CFRangeMake(0, CFArrayGetCount(digests)), digest)) {
- secdebug("ev", "found anchor for policy oid");
- good_ev_anchor = true;
- break;
- }
- }
- require_quiet(good_ev_anchor, notEV);
-
- CFAbsoluteTime october2006 = 178761600;
- if (SecCertificateVersion(certificate) >= 3
- && SecCertificateNotValidBefore(certificate) >= october2006) {
- const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
- require_quiet(bc && bc->isCA == true, notEV);
- SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
- == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
- }
-
- CFAbsoluteTime jan2011 = 315532800;
- if (SecCertificateNotValidBefore(certificate) < jan2011) {
- /* 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. */
- }
-
- return true;
-notEV:
- return false;
-}
-
-static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate) {
- const SecCECertificatePolicies *cp;
- cp = SecCertificateGetCertificatePolicies(certificate);
- require_quiet(cp && cp->numPolicies > 0, notEV);
- /* SecCertificateGetCRLDistributionPoints() is a noop right now */
-#if 0
- CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate);
- require_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV);
-#endif
- const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
- require_quiet(bc && bc->isCA == true, notEV);
- SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign))
- == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV);
- CFAbsoluteTime jan2011 = 315532800;
- if (SecCertificateNotValidBefore(certificate) < jan2011) {
- /* 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. */
-
- /* (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);
- } 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);
- }
-
- /* (e) keyUsage. */
- SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
- if (ku) {
- require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV);
- }
-
-#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. */
- }
-
- return true;
-notEV:
- return false;
-}
-
-/********************************************************
- **************** SecPolicy Callbacks *******************
- ********************************************************/
-static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc,
- CFStringRef key) {
-}
-
-static void SecPolicyCheckIdLinkage(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- CFDataRef parentSubjectKeyID = NULL;
- for (ix = count - 1; ix >= 0; --ix) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- /* If the previous certificate in the chain had a SubjectKeyID,
- make sure it matches the current certificates AuthorityKeyID. */
- if (parentSubjectKeyID) {
- /* @@@ According to RFC 2459 neither AuthorityKeyID nor
- SubjectKeyID can be critical. Currenty we don't check
- for this. */
- CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyID(cert);
- if (authorityKeyID) {
- if (!CFEqual(parentSubjectKeyID, authorityKeyID)) {
- /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- }
- }
-
- parentSubjectKeyID = SecCertificateGetSubjectKeyID(cert);
- }
-}
-
-static bool keyusage_allows(SecKeyUsage keyUsage, CFTypeRef xku) {
- if (!xku || CFGetTypeID(xku) != CFNumberGetTypeID())
- return false;
-
- SInt32 dku;
- CFNumberGetValue((CFNumberRef)xku, kCFNumberSInt32Type, &dku);
- SecKeyUsage ku = (SecKeyUsage)dku;
- return (keyUsage & ku) == ku;
-}
-
-static void SecPolicyCheckKeyUsage(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
- SecKeyUsage keyUsage = SecCertificateGetKeyUsage(leaf);
- bool match = false;
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFTypeRef xku = CFDictionaryGetValue(policy->_options, key);
- if (isArray(xku)) {
- CFIndex ix, count = CFArrayGetCount(xku);
- for (ix = 0; ix < count; ++ix) {
- CFTypeRef ku = CFArrayGetValueAtIndex(xku, ix);
- if (keyusage_allows(keyUsage, ku)) {
- match = true;
- break;
- }
- }
- } else {
- match = keyusage_allows(keyUsage, xku);
- }
- if (!match) {
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
-}
-
-static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage,
- CFTypeRef xeku) {
- if (!xeku || CFGetTypeID(xeku) != CFDataGetTypeID())
- return false;
- if (extendedKeyUsage) {
- CFRange all = { 0, CFArrayGetCount(extendedKeyUsage) };
- return CFArrayContainsValue(extendedKeyUsage, all, xeku);
- } else {
- /* 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) {
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
-}
-
-#if 0
-static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc,
- CFStringRef key, bool strict) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- for (ix = 0; ix < count; ++ix) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- const SecCEBasicConstraints *bc =
- SecCertificateGetBasicConstraints(cert);
- if (bc) {
- if (strict) {
- if (ix == 0) {
- /* Leaf certificate has basic constraints extension. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- } else if (!bc->critical) {
- /* Basic constraints extension is not marked critical. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- }
-
- if (ix > 0 || count == 1) {
- if (!bc->isCA) {
- /* Non leaf certificate marked as isCA false. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
-
- if (bc->pathLenConstraintPresent) {
- if (bc->pathLenConstraint < (uint32_t)(ix - 1)) {
-#if 0
- /* @@@ If a self signed certificate is issued by
- another cert that is trusted, then we are supposed
- to treat the self signed cert itself as the anchor
- for path length purposes. */
- CFIndex ssix = SecCertificatePathSelfSignedIndex(path);
- if (ssix >= 0 && ix >= ssix) {
- /* It's ok if the pathLenConstraint isn't met for
- certificates signing a self signed cert in the
- chain. */
- } else
-#endif
- {
- /* Path Length Constraint Exceeded. */
- if (!SecPVCSetResult(pvc, key, ix,
- kCFBooleanFalse))
- return;
- }
- }
- }
- }
- } else if (strict && ix > 0) {
- /* In strict mode all CA certificates *MUST* have a critical
- basic constraints extension and the leaf certificate
- *MUST NOT* have a basic constraints extension. */
- /* CA certificate is missing basicConstraints extension. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- }
-}
-#endif
-
-static void SecPolicyCheckBasicContraints(SecPVCRef pvc,
- CFStringRef key) {
- //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
-}
-
-static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- 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;
- }
- }
- }
-}
-
-static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc,
- CFStringRef key) {
-}
-
-/* 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.
- */
-static bool SecDNSMatch(CFStringRef hostname, CFStringRef servername) {
- CFStringInlineBuffer hbuf, sbuf;
- CFIndex hix, six,
- hlength = CFStringGetLength(hostname),
- slength = CFStringGetLength(servername);
- CFRange hrange = { 0, hlength }, srange = { 0, slength };
- CFStringInitInlineBuffer(hostname, &hbuf, hrange);
- CFStringInitInlineBuffer(servername, &sbuf, srange);
-
- for (hix = six = 0; six < slength; ++six) {
- UniChar hch, sch = CFStringGetCharacterFromInlineBuffer(&sbuf, six);
- if (sch == '*') {
- if (six + 1 >= slength) {
- /* 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. */
- do {
- /* Since we're not at the end of servername yet (that case
- was handeled 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 (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;
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckSSLHostname(SecPVCRef pvc,
- CFStringRef key) {
- /* @@@ Consider what to do if the caller passes in no hostname. Should
- we then still fail if the leaf has no dnsNames or IPAddresses at all? */
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef hostName = (CFStringRef)
- CFDictionaryGetValue(policy->_options, key);
- if (!isString(hostName)) {
- /* @@@ We can't return an error here and making the evaluation fail
- won't help much either. */
- return;
- }
-
- SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
- bool dnsMatch = false;
- CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf);
- if (dnsNames) {
- CFIndex ix, count = CFArrayGetCount(dnsNames);
- for (ix = 0; ix < count; ++ix) {
- CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
- if (SecDNSMatch(hostName, dns)) {
- dnsMatch = true;
- break;
- }
- }
- CFRelease(dnsNames);
- }
-
- if (!dnsMatch) {
- /* 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);
- }
- }
-
- if (!dnsMatch) {
- /* Hostname mismatch or no hostnames found in certificate. */
- 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):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-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. */
- return;
- }
-
- SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
- CFArrayRef addrs = SecCertificateCopyRFC822Names(leaf);
- if (addrs) {
- CFIndex ix, count = CFArrayGetCount(addrs);
- for (ix = 0; ix < count; ++ix) {
- CFStringRef addr = (CFStringRef)CFArrayGetValueAtIndex(addrs, ix);
- if (!CFStringCompare(email, addr, kCFCompareCaseInsensitive)) {
- match = true;
- break;
- }
- }
- CFRelease(addrs);
- }
-
- if (!match) {
- /* Hostname mismatch or no hostnames found in certificate. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
-}
-
-static void SecPolicyCheckValidIntermediates(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
- for (ix = 1; ix < count - 1; ++ix) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- if (!SecCertificateIsValid(cert, verifyTime)) {
- /* Intermediate certificate has expired. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
- }
-}
-
-static void SecPolicyCheckValidLeaf(SecPVCRef pvc,
- CFStringRef key) {
- CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- if (!SecCertificateIsValid(cert, verifyTime)) {
- /* Leaf certificate has expired. */
- if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse))
- return;
- }
-}
-
-static void SecPolicyCheckValidRoot(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc);
- ix = count - 1;
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- if (!SecCertificateIsValid(cert, verifyTime)) {
- /* Root certificate has expired. */
- if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse))
- return;
- }
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex count = SecPVCGetCertificateCount(pvc);
- if (count < 2) {
- /* Can't check intermediates common name if there is no intermediate. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- return;
- }
-
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 1);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef commonName =
- (CFStringRef)CFDictionaryGetValue(policy->_options, key);
- if (!isString(commonName)) {
- /* @@@ We can't return an error here and making the evaluation fail
- 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);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options,
- key);
- if (!isString(common_name)) {
- /* @@@ We can't return an error here and making the evaluation fail
- won't help much either. */
- return;
- }
- CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
- if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
- !CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) {
- /* Common Name mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(commonNames);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef prefix = (CFStringRef)CFDictionaryGetValue(policy->_options,
- key);
- if (!isString(prefix)) {
- /* @@@ We can't return an error here and making the evaluation fail
- 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);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options,
- key);
- if (!isString(common_name)) {
- /* @@@ We can't return an error here and making the evaluation fail
- 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);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckNotValidBefore(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFDateRef date = (CFDateRef)CFDictionaryGetValue(policy->_options, key);
- if (!isDate(date)) {
- /* @@@ We can't return an error here and making the evaluation fail
- 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 (!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 SecPolicyCheckChainLength(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex count = SecPVCGetCertificateCount(pvc);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFNumberRef chainLength =
- (CFNumberRef)CFDictionaryGetValue(policy->_options, key);
- CFIndex value;
- if (!chainLength || CFGetTypeID(chainLength) != CFNumberGetTypeID() ||
- !CFNumberGetValue(chainLength, kCFNumberCFIndexType, &value)) {
- /* @@@ We can't return an error here and making the evaluation fail
- won't help much either. */
- return;
- }
- if (value != count) {
- /* Chain length doesn't match policy requirement. */
- 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);
- CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
- CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
-
- bool foundMatch = false;
-
- if (isData(value))
- foundMatch = CFEqual(anchorSHA1, value);
- else if (isArray(value))
- foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), anchorSHA1);
- else {
- /* @@@ 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 (!foundMatch)
- if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, 0, kCFBooleanFalse))
- return;
-
- return;
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef org = (CFStringRef)CFDictionaryGetValue(policy->_options,
- key);
- if (!isString(org)) {
- /* @@@ We can't return an error here and making the evaluation fail
- won't help much either. */
- return;
- }
- CFArrayRef organization = SecCertificateCopyOrganization(cert);
- if (!organization || CFArrayGetCount(organization) != 1 ||
- !CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) {
- /* Leaf Subject Organization mismatch. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFReleaseSafe(organization);
-}
-
-static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFStringRef orgUnit = (CFStringRef)CFDictionaryGetValue(policy->_options,
- key);
- if (!isString(orgUnit)) {
- /* @@@ We can't return an error here and making the evaluation fail
- 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);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc,
- CFStringRef key) {
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFArrayRef trustedServerNames = (CFArrayRef)
- CFDictionaryGetValue(policy->_options, key);
- /* No names specified means we accept any name. */
- if (!trustedServerNames)
- return;
- if (!isArray(trustedServerNames)) {
- /* @@@ We can't return an error here and making the evaluation fail
- won't help much either. */
- 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) {
- /* Hostname mismatch or no hostnames found in certificate. */
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
-}
-
-static const unsigned char UTN_USERFirst_Hardware_Serial[][16] = {
-{ 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
-{ 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
-{ 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
-{ 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
-{ 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
-{ 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
-{ 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
-{ 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
-{ 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
-
-static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer[] = {
- 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
- 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
- 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
- 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
- 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
- 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
- 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
- 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
- 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
- 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
- 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
- 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
- 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
-};
-static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len = 151;
-
-
-static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc,
- CFStringRef key) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- CFDataRef issuer = cert ? SecCertificateGetNormalizedIssuerContent(cert) : NULL;
-
- if (issuer && (CFDataGetLength(issuer) == (CFIndex)UTN_USERFirst_Hardware_Normalized_Issuer_len) &&
- (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer, CFDataGetBytePtr(issuer),
- UTN_USERFirst_Hardware_Normalized_Issuer_len)))
- {
- CFDataRef serial = SecCertificateCopySerialNumber(cert);
- if (serial) {
- CFIndex serial_length = CFDataGetLength(serial);
- const uint8_t *serial_ptr = CFDataGetBytePtr(serial);
-
- while ((serial_length > 0) && (*serial_ptr == 0)) {
- serial_ptr++;
- serial_length--;
- }
-
- if (serial_length == (CFIndex)sizeof(*UTN_USERFirst_Hardware_Serial)) {
- unsigned int i;
- for (i = 0; i < array_size(UTN_USERFirst_Hardware_Serial); i++)
- {
- if (0 == memcmp(UTN_USERFirst_Hardware_Serial[i],
- serial_ptr, sizeof(*UTN_USERFirst_Hardware_Serial)))
- {
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- CFReleaseSafe(serial);
- return;
- }
- }
- }
- CFRelease(serial);
- }
- }
-
- SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL != otapkiRef)
- {
- CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef);
- CFRelease(otapkiRef);
- if (NULL != blackListedKeys)
- {
- /* Check for blacklisted intermediates keys. */
- CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
- if (dgst)
- {
- /* Check dgst against blacklist. */
- if (CFSetContainsValue(blackListedKeys, dgst))
- {
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFRelease(dgst);
- }
- CFRelease(blackListedKeys);
- }
- }
-}
-
-static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc, CFStringRef key)
-{
- SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL != otapkiRef)
- {
- CFSetRef grayListedKeys = SecOTAPKICopyGrayList(otapkiRef);
- CFRelease(otapkiRef);
- if (NULL != grayListedKeys)
- {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
-
- CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert);
- if (dgst)
- {
- /* Check dgst against gray. */
- if (CFSetContainsValue(grayListedKeys, dgst))
- {
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
- CFRelease(dgst);
- }
- CFRelease(grayListedKeys);
- }
- }
- }
-
-static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc, CFStringRef key)
-{
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
-
- if (value && SecCertificateHasMarkerExtension(cert, value))
- return;
-
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
-}
-
-static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key)
-{
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
-
- for (ix = 1; ix < count - 1; ix++) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- if (SecCertificateHasMarkerExtension(cert, value))
- return;
- }
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
-}
-
-
-
-/****************************************************************************
- *********************** 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;
-}
-
-/* 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;
-}
-
-/* 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;
- }
- }
- 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;
- }
- }
- 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
-
-#define POLICY_MAPPING 0
-#define POLICY_SUBTREES 0
-
-/* rfc5280 basic cert processing. */
-static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc,
- CFStringRef key) {
- /* Inputs */
- //cert_path_t path;
- CFIndex count = SecPVCGetCertificateCount(pvc);
- /* 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);
- if (is_anchored) {
- /* If the anchor is trusted we don't procces the last cert in the
- chain (root). */
- n--;
- } else {
- /* Add a detail for the root not being trusted. */
- 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;
-#if POLICY_SUBTREES
- root_name_t initial_permitted_subtrees = NULL;
- root_name_t initial_excluded_subtrees = NULL;
-#endif
-
- /* Initialization */
- pvc->valid_policy_tree = policy_tree_create(&oidAnyPolicy, NULL);
-#if POLICY_SUBTREES
- root_name_t permitted_subtrees = initial_permitted_subtrees;
- root_name_t excluded_subtrees = initial_excluded_subtrees;
-#endif
- uint32_t explicit_policy = initial_explicit_policy ? 0 : n + 1;
- uint32_t inhibit_any_policy = initial_any_policy_inhibit ? 0 : n + 1;
- uint32_t policy_mapping = initial_policy_mapping_inhibit ? 0 : n + 1;
-
-#if 0
- /* Path builder ensures we only get cert chains with proper issuer
- chaining with valid signatures along the way. */
- algorithm_id_t working_public_key_algorithm = anchor->public_key_algorithm;
- SecKeyRef working_public_key = anchor->public_key;
- x500_name_t working_issuer_name = anchor->issuer_name;
-#endif
- uint32_t i, max_path_length = n;
- SecCertificateRef cert = NULL;
- for (i = 1; i <= n; ++i) {
- /* Process Cert */
- cert = SecPVCGetCertificateAtIndex(pvc, n - i);
- bool is_self_issued = SecPVCIsCertificateAtIndexSelfSigned(pvc, n - i);
-
- /* (a) Verify the basic certificate information. */
- /* @@@ Ensure that cert was signed with working_public_key_algorithm
- using the working_public_key and the working_public_key_parameters. */
-#if 1
- /* 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;
- }
-#endif
-#if 0
- /* Check revocation status if the certificate asks for it. */
- CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
- if (ocspResponders) {
- SecPVCSetCheckRevocation(pvc);
- }
-#endif
- /* @@@ cert.issuer == working_issuer_name. */
-
-#if POLICY_SUBTREES
- /* (b) (c) */
- if (!is_self_issued || i == n) {
- /* Verify that the subject name is within one of the permitted_subtrees for X.500 distinguished names, and verify that each of the alternative names in the subjectAltName extension (critical or non-critical) is within one of the permitted_subtrees for that name type. */
- /* Verify that the subject name is not within any of the excluded_subtrees for X.500 distinguished names, and verify that each of the alternative names in the subjectAltName extension (critical or non-critical) is not within any of the excluded_subtrees for that name type. */
- }
-#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;
- }
- /* 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) */
- //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
- //working_public_key = SecCertificateCopyPublicKey(cert);
- //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
- //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
-#if POLICY_SUBTREES
- /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables as follows:
- */
- /* @@@ handle name constraints. */
-#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;
- }
- /* (k) */
- const SecCEBasicConstraints *bc =
- SecCertificateGetBasicConstraints(cert);
-#if 0 /* Checked in chain builder pre signature verify already. */
- if (!bc || !bc->isCA) {
- /* Basic constraints not present or not marked as isCA, illegal. */
- if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints,
- n - i, kCFBooleanFalse))
- return;
- }
-#endif
- /* (l) */
- if (!is_self_issued) {
- if (max_path_length > 0) {
- max_path_length--;
- } else {
- /* max_path_len exceeded, illegal. */
- if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints,
- n - i, kCFBooleanFalse))
- return;
- }
- }
- /* (m) */
- if (bc && bc->pathLenConstraintPresent
- && bc->pathLenConstraint < max_path_length) {
- max_path_length = bc->pathLenConstraint;
- }
-#if 0 /* Checked in chain builder pre signature verify already. */
- /* (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;
- }
-#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;
- }
- }
- /* 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;
- }
- }
- /* (c) */
- //working_public_key = SecCertificateCopyPublicKey(cert);
- /* (d) */
- /* If the subjectPublicKeyInfo field of the certificate contains an algorithm field with null parameters or parameters are omitted, compare the certificate subjectPublicKey algorithm to the working_public_key_algorithm. If the certificate subjectPublicKey algorithm and the
-working_public_key_algorithm are different, set the working_public_key_parameters to null. */
- //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
- /* (e) */
- //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
- /* (f) Recognize and process any other critical extension present in the certificate n. Process any other recognized non-critical extension present in certificate n that is relevant to path processing. */
- 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;
- }
-}
-
-static policy_set_t policies_for_cert(SecCertificateRef cert) {
- policy_set_t policies = NULL;
- const SecCECertificatePolicies *cp =
- SecCertificateGetCertificatePolicies(cert);
- size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
- for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
- policy_set_add(&policies, &cp->policies[policy_ix].policyIdentifier);
- }
- return policies;
-}
-
-static void SecPolicyCheckEV(SecPVCRef pvc,
- CFStringRef key) {
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- policy_set_t valid_policies = NULL;
-
- for (ix = 0; ix < count; ++ix) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- policy_set_t policies = policies_for_cert(cert);
- if (ix == 0) {
- /* Subscriber */
- /* anyPolicy in the leaf isn't allowed for EV, so only init
- valid_policies if we have real policies. */
- if (!policy_set_contains(policies, &oidAnyPolicy)) {
- valid_policies = policies;
- policies = NULL;
- }
- } else if (ix < count - 1) {
- /* Subordinate CA */
- if (!SecPolicySubordinateCACertificateCouldBeEV(cert)) {
- secdebug("ev", "subordinate certificate is not ev");
- if (SecPVCSetResultForced(pvc, key,
- ix, kCFBooleanFalse, true)) {
- policy_set_free(valid_policies);
- policy_set_free(policies);
- return;
- }
- }
- policy_set_intersect(&valid_policies, policies);
- } else {
- /* Root CA */
- if (!SecPolicyRootCACertificateIsEV(cert, valid_policies)) {
- secdebug("ev", "anchor certificate is not ev");
- if (SecPVCSetResultForced(pvc, key,
- ix, kCFBooleanFalse, true)) {
- policy_set_free(valid_policies);
- policy_set_free(policies);
- return;
- }
- }
- }
- policy_set_free(policies);
- if (!valid_policies) {
- secdebug("ev", "valid_policies set is empty: chain not ev");
- /* If we ever get into a state where no policies are valid anymore
- this can't be an ev chain. */
- if (SecPVCSetResultForced(pvc, key,
- ix, kCFBooleanFalse, true)) {
- return;
- }
- }
- }
-
- policy_set_free(valid_policies);
-
- /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
-Subscriber MUST contain an OID defined by the CA in the certificate’s
-certificatePolicies extension that: (i) indicates which CA policy statement relates
-to that certificate, (ii) asserts the CA’s adherence to and compliance with these
-Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
-marks the certificate as being an EV Certificate.
-(b) EV Subordinate CA Certificates
-(1) Certificates issued to Subordinate CAs that are not controlled by the issuing
-CA MUST contain one or more OIDs defined by the issuing CA that
-explicitly identify the EV Policies that are implemented by the Subordinate
-CA;
-(2) Certificates issued to Subordinate CAs that are controlled by the Root CA
-MAY contain the special anyPolicy OID (2.5.29.32.0).
-(c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
-certificatePolicies or extendedKeyUsage extensions.
-*/
-}
-
-
-static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc, CFStringRef key)
-{
- CFIndex ix, count = SecPVCGetCertificateCount(pvc);
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
- DERItem key_value;
- key_value.data = NULL;
- key_value.length = 0;
-
- if (CFGetTypeID(value) == CFDataGetTypeID())
- {
- CFDataRef key_data = (CFDataRef)value;
- key_value.data = (DERByte *)CFDataGetBytePtr(key_data);
- key_value.length = (DERSize)CFDataGetLength(key_data);
-
- for (ix = 0; ix < count; ix++) {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- policy_set_t policies = policies_for_cert(cert);
-
- if (policy_set_contains(policies, &key_value)) {
- return;
- }
- }
- SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
- }
-}
-
-
-static void SecPolicyCheckRevocation(SecPVCRef pvc,
- CFStringRef key) {
- SecPVCSetCheckRevocation(pvc);
-}
-
-static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc,
- CFStringRef key) {
- SecPathBuilderSetCanAccessNetwork(pvc->builder, false);
-}
-
-// MARK: -
-// MARK: SecRVCRef
-/********************************************************
- ****************** SecRVCRef Functions *****************
- ********************************************************/
-
-/* 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;
-
- /* 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;
-
- bool done;
-};
-typedef struct OpaqueSecRVC *SecRVCRef;
-
-static void SecRVCDelete(SecRVCRef rvc) {
- secdebug("alloc", "%p", rvc);
- asynchttp_free(&rvc->http);
- SecOCSPRequestFinalize(rvc->ocspRequest);
-}
-
-/* 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;
- }
- }
- }
- 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;
-
- if (!asyncHttpPost(rvc->responder, request, &rvc->http)) {
- /* Async request was posted, wait for reply. */
- return false;
- }
- }
-
-errOut:
- rvc->done = true;
- return true;
-}
-
-/* Proccess a verified ocsp response for a given cert. Return true if the
- certificate status was obtained. */
-static bool SecOCSPSingleResponseProccess(SecOCSPSingleResponseRef this,
- SecRVCRef rvc) {
- bool proccessed;
- 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;
- proccessed = 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);
- CFRelease(cfreason);
- proccessed = true;
- break;
- case CS_Unknown:
- /* not an error, no per-cert status, nothing here */
- secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
- proccessed = false;
- break;
- default:
- secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
- (int)this->certStatus, rvc->certIX);
- proccessed = false;
- break;
- }
-
- return proccessed;
-}
-
-static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecRVCRef rvc) {
- bool trusted;
- SecCertificatePathRef issuer = SecCertificatePathCopyFromParent(rvc->pvc->path, rvc->certIX + 1);
- SecCertificatePathRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
- CFRelease(issuer);
-
- if (signer) {
- if (signer == issuer) {
- /* 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);
- CFAbsoluteTime verifyTime = SecOCSPResponseVerifyTime(ocspResponse);
- struct OpaqueSecPVC ospvc;
- SecPVCInit(&ospvc, rvc->pvc->builder, policies, verifyTime);
- CFRelease(policies);
- SecPVCSetPath(&ospvc, signer, NULL);
- SecPVCLeafChecks(&ospvc);
- if (ospvc.result) {
- bool completed = SecPVCPathChecks(&ospvc);
- /* If completed 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);
- }
-
- 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;
-}
-
-/* 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, maxAge);
- CFRelease(data);
- }
- }
-
- if (ocspResponse) {
- SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
- if (orStatus == kSecOCSPSuccess) {
- SecOCSPSingleResponseRef sr =
- SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest);
- if (!sr) {
- /* The ocsp response didn't have a singleResponse for the cert
- we are looking for, let's try the next responder. */
- secdebug("ocsp",
- "ocsp responder: %@ did not include status of requested cert",
- rvc->responder);
- } else {
- /* We got a singleResponse for the cert we are interested in,
- let's proccess it. */
- /* @@@ 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. */
- if (SecOCSPResponseVerify(ocspResponse, rvc)) {
- secdebug("ocsp","responder: %@ sent proper response",
- rvc->responder);
-
- if (SecOCSPSingleResponseProccess(sr, rvc)) {
- if (rvc->nextUpdate == 0) {
- rvc->nextUpdate =
- SecOCSPResponseGetExpirationTime(ocspResponse);
- }
- /* If the singleResponse had meaningful information, we
- cache the response. */
- SecOCSPCacheAddResponse(ocspResponse, rvc->responder);
- rvc->done = true;
- }
- }
- SecOCSPSingleResponseDestroy(sr);
- }
- } else {
- /* ocsp response not ok. Let's try next responder. */
- secdebug("ocsp", "responder: %@ returned status: %d",
- rvc->responder, orStatus);
-#if 0
- if (!SecPVCSetResultForced(pvc, kSecPolicyCheckRevocation,
- rvc->certIX, kCFBooleanFalse, true))
- return;
-#endif
- }
- SecOCSPResponseFinalize(ocspResponse);
- }
-
- if (!rvc->done) {
- /* Clear the data for the next response. */
- asynchttp_free(http);
- SecRVCFetchNext(rvc);
- }
-
- if (rvc->done) {
- SecRVCDelete(rvc);
- if (!--pvc->asyncJobCount) {
- SecPathBuilderStep(pvc->builder);
- }
- }
-}
-
-static void SecRVCInit(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) {
- secdebug("alloc", "%p", rvc);
- rvc->pvc = pvc;
- rvc->certIX = certIX;
- rvc->http.queue = SecPathBuilderGetQueue(pvc->builder);
- rvc->http.completed = SecOCSPFetchCompleted;
- rvc->http.info = rvc;
- rvc->ocspRequest = NULL;
- rvc->responderIX = 0;
- rvc->responder = NULL;
- rvc->nextUpdate = 0;
- rvc->done = false;
-}
-
-
-static bool SecPVCCheckRevocation(SecPVCRef pvc) {
- secdebug("ocsp", "checking revocation");
- CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc);
- bool completed = true;
- if (certCount <= 1) {
- /* 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;
- }
- certCount--;
-
-#if 0
- /* @@@ 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
-
- if (pvc->rvcs) {
- /* We have done revocation checking already, we're done. */
- secdebug("ocsp", "Not rechecking revocation");
- return completed;
- }
-
- /* 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. */
- }
- }
- }
-#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;
-
- 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);
- SecOCSPResponseRef ocspResponse;
- ocspResponse = SecOCSPCacheCopyMatching(rvc->ocspRequest, NULL);
- if (ocspResponse) {
- SecOCSPSingleResponseRef sr =
- SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest);
- if (!sr) {
- /* The cached ocsp response didn't have a singleResponse for
- the cert we are looking for, it's shouldn't be in the cache. */
- secdebug("ocsp", "cached ocsp response did not include status"
- " of requested cert");
- } else {
- /* We got a singleResponse for the cert we are interested in,
- let's proccess it. */
-
- /* @@@ 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). */
- /* Recheck the OCSP response signature and verify the
- response. */
- if (SecOCSPResponseVerify(ocspResponse, rvc)) {
- secdebug("ocsp","cached response still has valid signature");
-
- if (SecOCSPSingleResponseProccess(sr, rvc)) {
- CFAbsoluteTime expTime =
- SecOCSPResponseGetExpirationTime(ocspResponse);
- if (rvc->nextUpdate == 0 || expTime < rvc->nextUpdate)
- rvc->nextUpdate = expTime;
- rvc->done = true;
- }
- }
- SecOCSPSingleResponseDestroy(sr);
- }
- SecOCSPResponseFinalize(ocspResponse);
- }
-
- /* Unless we succefully checked the revocation status of this cert
- based on the cache, Attempt to fire off an async http request
- for this certs 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. */
- 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;
- }
- }
-
- /* 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) {
- gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks, NULL);
- gSecPolicyPathCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks, NULL);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckBasicCertificateProcessing,
- SecPolicyCheckBasicCertificateProcessing);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckCriticalExtensions, SecPolicyCheckCriticalExtensions);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckIdLinkage, SecPolicyCheckIdLinkage);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckKeyUsage, SecPolicyCheckKeyUsage);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckExtendedKeyUsage, SecPolicyCheckExtendedKeyUsage);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckBasicContraints, SecPolicyCheckBasicContraints);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckNonEmptySubject, SecPolicyCheckNonEmptySubject);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckQualifiedCertStatements,
- SecPolicyCheckQualifiedCertStatements);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSSLHostname, SecPolicyCheckSSLHostname);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckEmail, SecPolicyCheckEmail);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckValidIntermediates, SecPolicyCheckValidIntermediates);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckValidLeaf, SecPolicyCheckValidLeaf);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckValidRoot, SecPolicyCheckValidRoot);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckIssuerCommonName, SecPolicyCheckIssuerCommonName);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSubjectCommonNamePrefix,
- SecPolicyCheckSubjectCommonNamePrefix);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSubjectCommonName,
- SecPolicyCheckSubjectCommonName);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckNotValidBefore,
- SecPolicyCheckNotValidBefore);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckChainLength, SecPolicyCheckChainLength);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckAnchorSHA1, SecPolicyCheckAnchorSHA1);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSubjectOrganization,
- SecPolicyCheckSubjectOrganization);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSubjectOrganizationalUnit,
- SecPolicyCheckSubjectOrganizationalUnit);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckEAPTrustedServerNames,
- SecPolicyCheckEAPTrustedServerNames);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckSubjectCommonNameTEST,
- SecPolicyCheckSubjectCommonNameTEST);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckRevocation,
- SecPolicyCheckRevocation);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckNoNetworkAccess,
- SecPolicyCheckNoNetworkAccess);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckBlackListedLeaf,
- SecPolicyCheckBlackListedLeaf);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckGrayListedLeaf,
- SecPolicyCheckGrayListedLeaf);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckLeafMarkerOid,
- SecPolicyCheckLeafMarkerOid);
- CFDictionaryAddValue(gSecPolicyPathCallbacks,
- kSecPolicyCheckIntermediateMarkerOid,
- SecPolicyCheckIntermediateMarkerOid);
- CFDictionaryAddValue(gSecPolicyLeafCallbacks,
- kSecPolicyCheckCertificatePolicy,
- SecPolicyCheckCertificatePolicyOid);
-}
-
-/* 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;
-}
-
-// MARK: -
-// MARK: SecPVCRef
-/********************************************************
- ****************** SecPVCRef Functions *****************
- ********************************************************/
-
-void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies,
- CFAbsoluteTime verifyTime) {
- secdebug("alloc", "%p", pvc);
- // Weird logging policies crashes.
- //secdebug("policy", "%@", policies);
- pvc->builder = builder;
- pvc->policies = policies;
- if (policies)
- CFRetain(policies);
- pvc->verifyTime = verifyTime;
- pvc->path = NULL;
- pvc->details = NULL;
- pvc->info = NULL;
- pvc->valid_policy_tree = NULL;
- pvc->callbacks = NULL;
- pvc->policyIX = 0;
- pvc->rvcs = NULL;
- pvc->asyncJobCount = 0;
- pvc->check_revocation = false;
- pvc->optionally_ev = false;
- pvc->is_ev = false;
- pvc->result = true;
-}
-
-static void SecPVCDeleteRVCs(SecPVCRef pvc) {
- secdebug("alloc", "%p", pvc);
- if (pvc->rvcs) {
- free(pvc->rvcs);
- pvc->rvcs = NULL;
- }
-}
-
-void SecPVCDelete(SecPVCRef pvc) {
- secdebug("alloc", "%p", pvc);
- CFReleaseNull(pvc->policies);
- CFReleaseNull(pvc->details);
- CFReleaseNull(pvc->info);
- if (pvc->valid_policy_tree) {
- policy_tree_prune(&pvc->valid_policy_tree);
- }
- SecPVCDeleteRVCs(pvc);
-}
-
-void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathRef path,
- CFArrayRef details) {
- secdebug("policy", "%@", path);
- if (pvc->path != path) {
- /* 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;
-}
-
-SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) {
- return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
-}
-
-CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
- return SecCertificatePathGetCount(pvc->path);
-}
-
-SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
- return SecCertificatePathGetCertificateAtIndex(pvc->path, ix);
-}
-
-bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc, CFIndex ix) {
- return SecCertificatePathSelfSignedIndex(pvc->path) == ix;
-}
-
-void SecPVCSetCheckRevocation(SecPVCRef pvc) {
- pvc->check_revocation = true;
- secdebug("ocsp", "deferred revocation checking enabled");
-}
-
-bool SecPVCIsAnchored(SecPVCRef pvc) {
- return SecCertificatePathIsAnchored(pvc->path);
-}
-
-CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
- return pvc->verifyTime;
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-bool SecPVCSetResultForced(SecPVCRef pvc,
- CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
-
- secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
- (pvc->callbacks == gSecPolicyLeafCallbacks ? "leaf"
- : (pvc->callbacks == gSecPolicyPathCallbacks ? "path"
- : "custom")),
- (force ? "force" : ""), result);
-
- /* If 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. */
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- if (policy && !CFDictionaryContainsKey(policy->_options, key))
- return true;
- }
-
- /* @@@ Check to see if the SecTrustSettings for the certificate in question
- tell us to ignore this error. */
- pvc->result = false;
- if (!pvc->details)
- return false;
-
- CFMutableDictionaryRef detail =
- (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
-
- /* Perhaps detail should have an array of results per key? As it stands
- in the case of multiple policy failures the last failure stands. */
- CFDictionarySetValue(detail, key, result);
-
- return true;
-}
-
-bool SecPVCSetResult(SecPVCRef pvc,
- CFStringRef key, CFIndex ix, CFTypeRef result) {
- return SecPVCSetResultForced(pvc, key, ix, result, false);
-}
-
-/* AUDIT[securityd](done):
- key(ok) is a caller provided.
- value(ok, unused) is a caller provided.
- */
-static void SecPVCValidateKey(const void *key, const void *value,
- void *context) {
- SecPVCRef pvc = (SecPVCRef)context;
-
- /* 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)
- 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;
- }
- } else if (pvc->callbacks == gSecPolicyPathCallbacks) {
- if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks, key)) {
- pvc->result = false;
- }
- } else {
- /* Non standard valdation phase, nothing is optional. */
- pvc->result = false;
- }
- return;
- }
-
- fcn(pvc, (CFStringRef)key);
-}
-
-/* AUDIT[securityd](done):
- policy->_options is a caller provided dictionary, only its cf type has
- been checked.
- */
-bool SecPVCLeafChecks(SecPVCRef pvc) {
- pvc->result = true;
- CFArrayRef policies = pvc->policies;
- CFIndex ix, count = CFArrayGetCount(policies);
- for (ix = 0; ix < count; ++ix) {
- SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
- pvc->policyIX = ix;
- /* Validate all keys for all policies. */
- pvc->callbacks = gSecPolicyLeafCallbacks;
- CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
- if (!pvc->result && !pvc->details)
- return pvc->result;
- }
-
- return pvc->result;
-}
-
-bool SecPVCParentCertificateChecks(SecPVCRef pvc, CFIndex ix) {
- /* 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))
- goto errOut;
- }
-
- if (is_anchor) {
- /* Perform anchor specific checks. */
- /* Don't think we have any of these. */
- } 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;
- }
- /* Consider adding (l) max_path_length checking here. */
-
- /* (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))
- goto errOut;
- }
- }
-
-errOut:
- return pvc->result;
-}
-
-bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) {
- /* Check stuff common to intermediate and anchors. */
-
- SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL != otapkiRef)
- {
- CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef);
- CFRelease(otapkiRef);
- if (NULL != blackListedKeys)
- {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
- && SecPVCIsAnchored(pvc));
- if (!is_anchor) {
- /* 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);
- }
- }
- CFRelease(blackListedKeys);
- return pvc->result;
- }
- }
- // Assume OK
- return true;
-}
-
-bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix)
-{
- /* Check stuff common to intermediate and anchors. */
- SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
- if (NULL != otapkiRef)
- {
- CFSetRef grayListKeys = SecOTAPKICopyGrayList(otapkiRef);
- CFRelease(otapkiRef);
- if (NULL != grayListKeys)
- {
- SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix);
- bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1
- && SecPVCIsAnchored(pvc));
- if (!is_anchor) {
- /* 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);
- }
- }
- CFRelease(grayListKeys);
- return pvc->result;
- }
- }
- // Assume ok
- return true;
-}
-
-/* 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;
- /* 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) {
- SecPolicyCheckBasicCertificateProcessing(pvc,
- kSecPolicyCheckBasicCertificateProcessing);
- }
-
- CFArrayRef policies = pvc->policies;
- CFIndex count = CFArrayGetCount(policies);
- for (; pvc->policyIX < count; ++pvc->policyIX) {
- /* Validate all keys for all policies. */
- pvc->callbacks = gSecPolicyPathCallbacks;
- SecPolicyRef policy = SecPVCGetPolicy(pvc);
- CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc);
- if (!pvc->result && !pvc->details)
- return completed;
- }
-
- /* Check the things we can't check statically for the certificate path. */
- /* Critical Extensions, chainLength. */
-
- /* 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);
- }
-
-//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("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 experation 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;
-}