SecPolicyServer.c   [plain text]


/*
 * Copyright (c) 2008-2011 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 <securityd/asynchttp.h>
#include <securityd/policytree.h>
#include <pthread.h>
#include <CoreFoundation/CFTimeZone.h>
#include <wctype.h>
#include <libDER/oids.h>
#include <CoreFoundation/CFNumber.h>
#include <Security/SecCertificateInternal.h>
#include <AssertMacros.h>
#include <security_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>

#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 CFSetRef gBlackListedKeys = NULL;

static pthread_once_t gSecEVPolicyToAnchorDigestsOnce = PTHREAD_ONCE_INIT;
static CFDictionaryRef gSecEVPolicyToAnchorDigests = NULL;

/* Helper functions. */

static bool isArray(CFTypeRef cfType) {
    return cfType && CFGetTypeID(cfType) == CFArrayGetTypeID();
}

static bool isData(CFTypeRef cfType) {
    return cfType && CFGetTypeID(cfType) == CFDataGetTypeID();
}

static bool isDate(CFTypeRef cfType) {
    return cfType && CFGetTypeID(cfType) == CFDateGetTypeID();
}

static bool isDictionary(CFTypeRef cfType) {
    return cfType && CFGetTypeID(cfType) == CFDictionaryGetTypeID();
}

static bool isString(CFTypeRef cfType) {
    return cfType && CFGetTypeID(cfType) == CFStringGetTypeID();
}

static void SecEVPolicyToAnchorDigestsInit(void) {
	CFDataRef xmlData = SecFrameworkCopyResourceContents(
		CFSTR("EVRoots"), CFSTR("plist"), NULL);
	CFPropertyListRef evroots = NULL;
    if (xmlData) {
        evroots = CFPropertyListCreateFromXMLData(
            kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL);
        CFRelease(xmlData);
    }
	if (evroots) {
		if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
            /* @@@ Ensure that each dictionary key is a dotted list of digits,
               each value is an NSArrayRef and each element in the array is a
               20 byte digest. */
			gSecEVPolicyToAnchorDigests = (CFDictionaryRef)evroots;
		} else {
			secwarning("EVRoot.plist is wrong type.");
			CFRelease(evroots);
		}
    }
}

static CFArrayRef SecPolicyAnchorDigestsForEVPolicy(const DERItem *policyOID) {
    pthread_once(&gSecEVPolicyToAnchorDigestsOnce,
        SecEVPolicyToAnchorDigestsInit);
    CFArrayRef roots = NULL;
    CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(
        kCFAllocatorDefault, policyOID);
    if (oid) {
        roots = (CFArrayRef)CFDictionaryGetValue(gSecEVPolicyToAnchorDigests,
            oid);
		if (roots && CFGetTypeID(roots) != CFArrayGetTypeID()) {
            ocspdErrorLog("EVRoot.plist has non array value");
            roots = NULL;
        }
        CFRelease(oid);
    }
    return roots;
}


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);
    CFDataRef sha1Digest =
        (CFDataRef)CFDictionaryGetValue(policy->_options, key);
    if (!isData(sha1Digest)) {
        /* @@@ We can't return an error here and making the evaluation fail
           won't help much either. */
        return;
    }
    CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
	if (!CFEqual(anchorSHA1, sha1Digest)) {
		/* Certificate chain is not issued by required anchor. */
		if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, 0, kCFBooleanFalse))
			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);
}

/* 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. */
                    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 const 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 < sizeof(UTN_USERFirst_Hardware_Serial)/sizeof(*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);
                        return;
                    }
                }
            }
            CFRelease(serial);
        }
    }
}

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 = node->children;
    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 SecPolicyCheckRevocation(SecPVCRef pvc,
	CFStringRef key) {
    SecPVCSetCheckRevocation(pvc);
}

static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc,
	CFStringRef key) {
    SecPathBuilderSetCanAccessNetwork(pvc->builder, false);
}

#pragma mark -
#pragma 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 %u", 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 %u", 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 %u", rvc->certIX);
        proccessed = false;
        break;
    default:
        secdebug("ocsp", "BAD certStatus (%d) for cert %u",
            (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.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 = 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 acces 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,
		kSecPolicyCheckEAPTrustedServerNames,
        SecPolicyCheckEAPTrustedServerNames);
	CFDictionaryAddValue(gSecPolicyLeafCallbacks,
		kSecPolicyCheckSubjectCommonNameTEST,
		SecPolicyCheckSubjectCommonNameTEST);
	CFDictionaryAddValue(gSecPolicyLeafCallbacks,
		kSecPolicyCheckRevocation,
		SecPolicyCheckRevocation);
	CFDictionaryAddValue(gSecPolicyLeafCallbacks,
		kSecPolicyCheckNoNetworkAccess,
		SecPolicyCheckNoNetworkAccess);
    CFDictionaryAddValue(gSecPolicyLeafCallbacks,
        kSecPolicyCheckBlackListedLeaf,
        SecPolicyCheckBlackListedLeaf);
    CFDictionaryAddValue(gSecPolicyLeafCallbacks,
        kSecPolicyCheckLeafMarkerOid,
        SecPolicyCheckLeafMarkerOid);
    CFDictionaryAddValue(gSecPolicyPathCallbacks,
        kSecPolicyCheckIntermediateMarkerOid,
        SecPolicyCheckIntermediateMarkerOid);

    /* Initialize gBlackListedKeys. */
    const uint8_t blacklisted_keys[][CC_SHA1_DIGEST_LENGTH] = {
        /* DigiNotar Root CA by DigiNotar Root CA (is also cross certified by entrust) */
        { 0x88, 0x68, 0xBF, 0xE0, 0x8E, 0x35, 0xC4, 0x3B, 0x38, 0x6B, 0x62, 0xF7, 0x28, 0x3B, 0x84, 0x81, 0xC8, 0x0C, 0xD7, 0x4D },
        /* DigiNotar Services 1024 CA.cer by Entrust.net Secure Server Certification Authority (revoked) */
        { 0xFE, 0xDC, 0x94, 0x49, 0x0C, 0x6F, 0xEF, 0x5C, 0x7F, 0xC6, 0xF1, 0x12, 0x99, 0x4F, 0x16, 0x49, 0xAD, 0xFB, 0x82, 0x65 },
        /* DigiNotar Cyber CA issued by GTE CyberTrust Global Root. (no yet revoked) */
        { 0xAB, 0xF9, 0x68, 0xDF, 0xCF, 0x4A, 0x37, 0xD7, 0x7B, 0x45, 0x8C, 0x5F, 0x72, 0xDE, 0x40, 0x44, 0xC3, 0x65, 0xBB, 0xC2 },
        /* DigiNotar PKIoverheid CA Overheid en Bedrijven */
        { 0x4C, 0x08, 0xC9, 0x8D, 0x76, 0xF1, 0x98, 0xC7, 0x3E, 0xDF, 0x3C, 0xD7, 0x2F, 0x75, 0x0D, 0xB1, 0x76, 0x79, 0x97, 0xCC },
        /* DigiNotar PKIoverheid CA Organisatie - G2 */
        { 0xBC, 0x5D, 0x94, 0x3B, 0xD9, 0xAB, 0x7B, 0x03, 0x25, 0x73, 0x61, 0xC2, 0xDB, 0x2D, 0xEE, 0xFC, 0xAB, 0x8F, 0x65, 0xA1 },
        /* Digisign Server ID - (Enrich) cross-certified by Entrust.net Certification Authority (2048), serial 1276011370 */
        {   0xa1, 0x3f, 0xd3, 0x7c, 0x04, 0x5b, 0xb4, 0xa3, 0x11, 0x2b,
            0xd8, 0x9b, 0x1a, 0x07, 0xe9, 0x04, 0xb2, 0xd2, 0x6e, 0x26 },
        /* Digisign Server ID - (Enrich) cross-certified by GTE CyberTrust Global Root, serial 120001705 */
        {   0xc6, 0x16, 0x93, 0x4e, 0x16, 0x17, 0xec, 0x16, 0xae, 0x8c,
            0x94, 0x76, 0xf3, 0x86, 0x6d, 0xc5, 0x74, 0x6e, 0x84, 0x77 },
    };
#define NUM_BLACKLISTED_KEYS  (sizeof(blacklisted_keys) / sizeof(*blacklisted_keys))

    const void *keys[NUM_BLACKLISTED_KEYS];
    size_t ix;
    for (ix = 0; ix < NUM_BLACKLISTED_KEYS; ++ix) {
        keys[ix] = CFDataCreateWithBytesNoCopy(0, blacklisted_keys[ix], CC_SHA1_DIGEST_LENGTH, kCFAllocatorNull);
    }
    gBlackListedKeys = CFSetCreate(0, keys, NUM_BLACKLISTED_KEYS, &kCFTypeSetCallBacks);
    CFSetApplyFunction(gBlackListedKeys, (CFSetApplierFunction)CFRelease, 0);
}

/* 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;
}

#pragma mark -
#pragma 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]> %@", 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. */
	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(gBlackListedKeys, dgst)) {
                SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey,
                                      ix, kCFBooleanFalse, true);
            }
            CFRelease(dgst);
        }
    }

    return pvc->result;
}

/* 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;
}