]> git.saurik.com Git - apple/security.git/blobdiff - Security/sec/securityd/SecTrustServer.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / securityd / SecTrustServer.c
diff --git a/Security/sec/securityd/SecTrustServer.c b/Security/sec/securityd/SecTrustServer.c
new file mode 100644 (file)
index 0000000..f5bb542
--- /dev/null
@@ -0,0 +1,1341 @@
+/*
+ * Copyright (c) 2006-2010,2012-2014 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@
+ *
+ * SecTrustServer.c - certificate trust evaluation engine
+ *
+ *
+ */
+
+#include <securityd/SecTrustServer.h>
+#include <securityd/SecPolicyServer.h>
+#include <securityd/SecTrustStoreServer.h>
+#include <securityd/SecCAIssuerRequest.h>
+#include <securityd/SecItemServer.h>
+
+#include <utilities/SecIOFormat.h>
+#include <utilities/SecDispatchRelease.h>
+
+#include <Security/SecTrustPriv.h>
+#include <Security/SecItem.h>
+#include <Security/SecCertificateInternal.h>
+#include <Security/SecCertificatePath.h>
+#include <Security/SecFramework.h>
+#include <Security/SecPolicyInternal.h>
+#include <CoreFoundation/CFRuntime.h>
+#include <CoreFoundation/CFSet.h>
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFPropertyList.h>
+#include <AssertMacros.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <Security/SecBase.h>
+#include "SecRSAKey.h"
+#include <libDER/oids.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCFWrappers.h>
+#include <Security/SecInternal.h>
+#include <ipc/securityd_client.h>
+#include <CommonCrypto/CommonDigest.h>
+#include "OTATrustUtilities.h"
+
+
+/********************************************************
+ ***************** OTA Trust support ********************
+ ********************************************************/
+
+
+#ifndef SECITEM_SHIM_OSX
+
+static CFArrayRef subject_to_anchors(CFDataRef nic);
+static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets);
+
+static CFArrayRef subject_to_anchors(CFDataRef nic)
+{
+    CFArrayRef result = NULL;
+
+    if (NULL == nic)
+    {
+        return result;
+    }
+
+       SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+       if (NULL == otapkiref)
+       {
+               return result;
+       }
+       
+       CFDictionaryRef lookupTable = SecOTAPKICopyAnchorLookupTable(otapkiref);
+       CFRelease(otapkiref);
+
+       if (NULL == lookupTable)
+       {
+               return result;
+       }
+
+    unsigned char subject_digest[CC_SHA1_DIGEST_LENGTH];
+    memset(subject_digest, 0, CC_SHA1_DIGEST_LENGTH);
+
+    (void)CC_SHA1(CFDataGetBytePtr(nic), (CC_LONG)CFDataGetLength(nic), subject_digest);
+    CFDataRef sha1Digest = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subject_digest, CC_SHA1_DIGEST_LENGTH, kCFAllocatorNull);
+
+
+    result = (CFArrayRef)CFDictionaryGetValue(lookupTable, sha1Digest);
+       CFReleaseSafe(lookupTable);
+    CFReleaseSafe(sha1Digest);
+
+    return result;
+}
+
+static CFArrayRef CopyCertDataFromIndices(CFArrayRef offsets)
+{
+    CFMutableArrayRef result = NULL;
+
+       SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+       if (NULL == otapkiref)
+       {
+               return result;
+       }
+       
+       const char* anchorTable = SecOTAPKIGetAnchorTable(otapkiref);   
+       if (NULL == anchorTable)
+       {
+               CFReleaseSafe(otapkiref);
+               return result;
+       }
+
+       CFIndex num_offsets = CFArrayGetCount(offsets);
+       
+       result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+       
+       for (CFIndex idx = 0; idx < num_offsets; idx++)
+    {
+               CFNumberRef offset = (CFNumberRef)CFArrayGetValueAtIndex(offsets, idx);
+               uint32_t offset_value = 0;
+               if (CFNumberGetValue(offset, kCFNumberSInt32Type, &offset_value))
+               {
+                       char* pDataPtr = (char *)(anchorTable + offset_value);
+                       //int32_t record_length = *((int32_t * )pDataPtr);
+                       //record_length = record_length;
+                       pDataPtr += sizeof(uint32_t);
+
+                       int32_t cert_data_length = *((int32_t * )pDataPtr);
+                       pDataPtr += sizeof(uint32_t);
+
+                       CFDataRef cert_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)pDataPtr,
+                                                              cert_data_length, kCFAllocatorNull);
+                       if (NULL != cert_data)
+                       {
+                CFArrayAppendValue(result, cert_data);
+                CFReleaseSafe(cert_data);
+            }
+               }
+       }
+       CFReleaseSafe(otapkiref);
+       return result;
+}
+
+static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets)
+{
+       CFMutableArrayRef result = NULL;
+
+    CFArrayRef cert_data_array = CopyCertDataFromIndices(offsets);
+
+    if (NULL != cert_data_array)
+    {
+        result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+        CFIndex num_cert_datas = CFArrayGetCount(cert_data_array);
+        for (CFIndex idx = 0; idx < num_cert_datas; idx++)
+        {
+            CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_data_array, idx);
+            if (NULL != cert_data)
+            {
+                SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_data);
+                if (NULL != cert)
+                {
+                    CFArrayAppendValue(result, cert);
+                    CFRelease(cert);
+                }
+            }
+        }
+        CFRelease(cert_data_array);
+    }
+    return result;
+
+}
+#endif
+
+/********************************************************
+ *************** END OTA Trust support ******************
+ ********************************************************/
+
+#define MAX_CHAIN_LENGTH  15
+
+/* Forward declaration for use in SecCertificateSource. */
+static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
+
+
+// MARK: -
+// MARK: SecCertificateSource
+/********************************************************
+ ************ SecCertificateSource object ***************
+ ********************************************************/
+
+typedef struct SecCertificateSource *SecCertificateSourceRef;
+typedef void(*SecCertificateSourceParents)(void *, CFArrayRef);
+typedef bool(*CopyParents)(SecCertificateSourceRef source,
+       SecCertificateRef certificate, void *context, SecCertificateSourceParents);
+typedef bool(*Contains)(SecCertificateSourceRef source,
+       SecCertificateRef certificate);
+
+struct SecCertificateSource {
+       CopyParents             copyParents;
+       Contains                contains;
+};
+
+static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source,
+    SecCertificateRef certificate,
+    void *context, SecCertificateSourceParents callback) {
+    return source->copyParents(source, certificate, context, callback);
+}
+
+static bool SecCertificateSourceContains(SecCertificateSourceRef source,
+       SecCertificateRef certificate) {
+       return source->contains(source, certificate);
+}
+
+// MARK: -
+// MARK: SecItemCertificateSource
+/********************************************************
+ *********** SecItemCertificateSource object ************
+ ********************************************************/
+struct SecItemCertificateSource {
+       struct SecCertificateSource base;
+       CFArrayRef accessGroups;
+};
+typedef struct SecItemCertificateSource *SecItemCertificateSourceRef;
+
+static CFTypeRef SecItemCertificateSourceResultsPost(CFTypeRef raw_results) {
+    if (isArray(raw_results)) {
+        CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(raw_results), &kCFTypeArrayCallBacks);
+        CFArrayForEach(raw_results, ^(const void *value) {
+            SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, value);
+            if (cert) {
+                CFArrayAppendValue(result, cert);
+                               CFRelease(cert);
+                       }
+        });
+        return result;
+    } else if (isData(raw_results)) {
+        return SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)raw_results);
+    }
+    return NULL;
+}
+
+static bool SecItemCertificateSourceCopyParents(
+       SecCertificateSourceRef source, SecCertificateRef certificate,
+        void *context, SecCertificateSourceParents callback) {
+       SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
+    /* FIXME: Search for things other than just subject of our issuer if we
+       have a subjectID or authorityKeyIdentifier. */
+    CFDataRef normalizedIssuer =
+        SecCertificateGetNormalizedIssuerContent(certificate);
+    const void *keys[] = {
+        kSecClass,
+        kSecReturnData,
+        kSecMatchLimit,
+        kSecAttrSubject
+    },
+    *values[] = {
+        kSecClassCertificate,
+        kCFBooleanTrue,
+        kSecMatchLimitAll,
+        normalizedIssuer
+    };
+    CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4,
+               NULL, NULL);
+    CFTypeRef results = NULL;
+    /* We can make this async or run this on a queue now easily. */
+    CFErrorRef localError = NULL;
+    if (!_SecItemCopyMatching(query, msource->accessGroups, &results, &localError)) {
+        if (CFErrorGetCode(localError) != errSecItemNotFound) {
+            secdebug("trust", "_SecItemCopyMatching: %@", localError);
+        }
+        CFRelease(localError);
+    }
+    CFRelease(query);
+    CFTypeRef certs = SecItemCertificateSourceResultsPost(results);
+    CFReleaseSafe(results);
+    callback(context, certs);
+    CFReleaseSafe(certs);
+    return true;
+}
+
+static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
+       SecCertificateRef certificate) {
+       SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
+    /* Lookup a certificate by issuer and serial number. */
+    CFDataRef normalizedSubject =
+        SecCertificateGetNormalizedSubjectContent(certificate);
+    CFDataRef serialNumber =
+        SecCertificateCopySerialNumber(certificate);
+    const void *keys[] = {
+        kSecClass,
+        kSecMatchLimit,
+        kSecAttrIssuer,
+               kSecAttrSerialNumber
+    },
+    *values[] = {
+        kSecClassCertificate,
+        kSecMatchLimitOne,
+        normalizedSubject,
+               serialNumber
+    };
+    CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5,
+        NULL, NULL);
+    CFErrorRef localError = NULL;
+    CFTypeRef results = NULL;
+    bool ok = _SecItemCopyMatching(query, msource->accessGroups, &results, &localError);
+    CFRelease(query);
+    CFRelease(serialNumber);
+    CFReleaseSafe(results);
+    if (!ok) {
+        if (CFErrorGetCode(localError) != errSecItemNotFound) {
+            secdebug("trust", "_SecItemCopyMatching: %@", localError);
+        }
+        CFRelease(localError);
+               return false;
+    }
+    return true;
+}
+
+static SecCertificateSourceRef SecItemCertificateSourceCreate(CFArrayRef accessGroups) {
+       SecItemCertificateSourceRef result = (SecItemCertificateSourceRef)malloc(sizeof(*result));
+       result->base.copyParents = SecItemCertificateSourceCopyParents;
+       result->base.contains = SecItemCertificateSourceContains;
+       result->accessGroups = accessGroups;
+    CFRetainSafe(accessGroups);
+       return (SecCertificateSourceRef)result;
+}
+
+static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source) {
+       SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
+       CFReleaseSafe(msource->accessGroups);
+       free(msource);
+}
+
+// MARK: -
+// MARK: SecSystemAnchorSource
+/********************************************************
+ *********** SecSystemAnchorSource object ************
+ ********************************************************/
+
+static bool SecSystemAnchorSourceCopyParents(
+       SecCertificateSourceRef source, SecCertificateRef certificate,
+        void *context, SecCertificateSourceParents callback) {
+#ifndef SECITEM_SHIM_OSX
+    CFArrayRef parents = NULL;
+       CFArrayRef anchors = NULL;
+       SecOTAPKIRef otapkiref = NULL;
+       
+    CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate);
+    /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
+       It does not matter since we would be returning the wrong anchors */
+    assert((unsigned long)CFDataGetLength(nic)<UINT_MAX); /* Debug check. correct as long as CFIndex is signed long */
+
+       otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+       require_quiet(otapkiref, errOut);
+       anchors = subject_to_anchors(nic);
+       require_quiet(anchors, errOut);
+       parents = CopyCertsFromIndices(anchors);
+
+errOut:
+    callback(context, parents);
+    CFReleaseSafe(parents);
+       CFReleaseSafe(otapkiref);
+#endif
+    return true;
+}
+
+/* Quick thought: we can eliminate this method if we search anchor sources
+   before all others and we remember if we got a cert from an anchorsource. */
+static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
+       SecCertificateRef certificate) {
+       bool result = false;
+#ifndef SECITEM_SHIM_OSX
+       CFArrayRef anchors = NULL;
+       SecOTAPKIRef otapkiref = NULL;
+       CFArrayRef cert_datas = NULL;
+       
+    CFDataRef nic = SecCertificateGetNormalizedSubjectContent(certificate);
+    /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
+     It does not matter since we would be returning the wrong anchors */
+    assert((unsigned long)CFDataGetLength(nic)<UINT_MAX); /* Debug check. correct as long as CFIndex is signed long */
+    
+       otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+       require_quiet(otapkiref, errOut);
+    anchors = subject_to_anchors(nic);
+       require_quiet(anchors, errOut);
+    cert_datas = CopyCertDataFromIndices(anchors);
+    require_quiet(cert_datas, errOut);
+  
+    CFIndex cert_length = SecCertificateGetLength(certificate);
+    const UInt8 *cert_data_ptr = SecCertificateGetBytePtr(certificate);
+    
+    CFIndex num_cert_datas = CFArrayGetCount(cert_datas);
+    for (CFIndex idx = 0; idx < num_cert_datas; idx++)
+    {
+        CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_datas, idx);
+               
+               if (NULL != cert_data)
+               {
+            if (CFGetTypeID(cert_data) == CFDataGetTypeID())
+            {
+                CFIndex  aCert_Length = CFDataGetLength(cert_data);
+                const UInt8*  aCert_Data_Ptr = CFDataGetBytePtr(cert_data);
+                
+                if (aCert_Length == cert_length)
+                {
+                    if (!memcmp(cert_data_ptr, aCert_Data_Ptr, cert_length))
+                    {
+                                               result = true;
+                                               break;
+                    }
+                }
+            }
+               }
+    }
+
+errOut:
+       CFReleaseSafe(cert_datas);
+       CFReleaseSafe(otapkiref);
+#endif
+    return result;
+}
+
+
+
+struct SecCertificateSource kSecSystemAnchorSource = {
+       SecSystemAnchorSourceCopyParents,
+       SecSystemAnchorSourceContains
+};
+
+// MARK: -
+// MARK: SecUserAnchorSource
+/********************************************************
+ *********** SecUserAnchorSource object ************
+ ********************************************************/
+static bool SecUserAnchorSourceCopyParents(
+       SecCertificateSourceRef source, SecCertificateRef certificate,
+        void *context, SecCertificateSourceParents callback) {
+    CFArrayRef parents = SecTrustStoreCopyParents(
+        SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate, NULL);
+    callback(context, parents);
+    CFReleaseSafe(parents);
+    return true;
+}
+
+static bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
+       SecCertificateRef certificate) {
+    return SecTrustStoreContains(
+        SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate);
+}
+
+struct SecCertificateSource kSecUserAnchorSource = {
+       SecUserAnchorSourceCopyParents,
+       SecUserAnchorSourceContains
+};
+
+// MARK: -
+// MARK: SecMemoryCertificateSource
+/********************************************************
+ *********** SecMemoryCertificateSource object ************
+ ********************************************************/
+struct SecMemoryCertificateSource {
+       struct SecCertificateSource base;
+       CFMutableSetRef certificates;
+       CFMutableDictionaryRef subjects;
+};
+typedef struct SecMemoryCertificateSource *SecMemoryCertificateSourceRef;
+
+static bool SecMemoryCertificateSourceCopyParents(
+       SecCertificateSourceRef source, SecCertificateRef certificate,
+        void *context, SecCertificateSourceParents callback) {
+       SecMemoryCertificateSourceRef msource =
+               (SecMemoryCertificateSourceRef)source;
+       CFDataRef normalizedIssuer =
+        SecCertificateGetNormalizedIssuerContent(certificate);
+       CFArrayRef parents = CFDictionaryGetValue(msource->subjects,
+               normalizedIssuer);
+    /* FIXME filter parents by subjectID if certificate has an
+       authorityKeyIdentifier. */
+    secdebug("trust", "%@ parents -> %@", certificate, parents);
+    callback(context, parents);
+    return true;
+}
+
+static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source,
+       SecCertificateRef certificate) {
+       SecMemoryCertificateSourceRef msource =
+               (SecMemoryCertificateSourceRef)source;
+       return CFSetContainsValue(msource->certificates, certificate);
+}
+
+static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict,
+       const void *key, const void *value) {
+       if (!key)
+               return;
+
+       CFMutableArrayRef values =
+               (CFMutableArrayRef)CFDictionaryGetValue(dict, key);
+       if (!values) {
+               values = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+                       &kCFTypeArrayCallBacks);
+               CFDictionaryAddValue(dict, key, values);
+               CFRelease(values);
+       }
+
+       if (values)
+               CFArrayAppendValue(values, value);
+}
+
+static void SecMemoryCertificateSourceApplierFunction(const void *value,
+       void *context) {
+       SecMemoryCertificateSourceRef msource =
+               (SecMemoryCertificateSourceRef)context;
+       SecCertificateRef certificate = (SecCertificateRef)value;
+
+       /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
+       if (CFSetContainsValue(msource->certificates, certificate))
+               return;
+       CFSetAddValue(msource->certificates, certificate);
+
+       CFDataRef key = SecCertificateGetNormalizedSubjectContent(certificate);
+       dictAddValueToArrayForKey(msource->subjects, key, value);
+}
+
+static SecCertificateSourceRef SecMemoryCertificateSourceCreate(
+       CFArrayRef certificates) {
+       SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
+               malloc(sizeof(*result));
+       result->base.copyParents = SecMemoryCertificateSourceCopyParents;
+       result->base.contains = SecMemoryCertificateSourceContains;
+       CFIndex count = CFArrayGetCount(certificates);
+       result->certificates = CFSetCreateMutable(kCFAllocatorDefault, count,
+               &kCFTypeSetCallBacks);
+       result->subjects = CFDictionaryCreateMutable(kCFAllocatorDefault,
+               count, &kCFTypeDictionaryKeyCallBacks,
+               &kCFTypeDictionaryValueCallBacks);
+       CFRange range = { 0, count };
+       CFArrayApplyFunction(certificates, range,
+               SecMemoryCertificateSourceApplierFunction, result);
+
+       return (SecCertificateSourceRef)result;
+}
+
+static void SecMemoryCertificateSourceDestroy(
+       SecCertificateSourceRef source) {
+       SecMemoryCertificateSourceRef msource =
+               (SecMemoryCertificateSourceRef)source;
+       CFRelease(msource->certificates);
+       CFRelease(msource->subjects);
+       free(msource);
+}
+
+// MARK: -
+// MARK: SecCAIssuerCertificateSource
+/********************************************************
+ ********* SecCAIssuerCertificateSource object **********
+ ********************************************************/
+static bool SecCAIssuerCertificateSourceCopyParents(
+       SecCertificateSourceRef source, SecCertificateRef certificate,
+        void *context, SecCertificateSourceParents callback) {
+    return SecCAIssuerCopyParents(certificate, SecPathBuilderGetQueue((SecPathBuilderRef)context), context, callback);
+}
+
+static bool SecCAIssuerCertificateSourceContains(
+    SecCertificateSourceRef source, SecCertificateRef certificate) {
+       return false;
+}
+
+struct SecCertificateSource kSecCAIssuerSource = {
+       SecCAIssuerCertificateSourceCopyParents,
+       SecCAIssuerCertificateSourceContains
+};
+
+// MARK: -
+// MARK: SecPathBuilder
+/********************************************************
+ *************** SecPathBuilder object ******************
+ ********************************************************/
+struct SecPathBuilder {
+    dispatch_queue_t queue;
+       SecCertificateSourceRef certificateSource;
+       SecCertificateSourceRef itemCertificateSource;
+       SecCertificateSourceRef anchorSource;
+       CFMutableArrayRef               anchorSources;
+       CFIndex                                 nextParentSource;
+       CFMutableArrayRef               parentSources;
+
+    /* Hashed set of all paths we've constructed so far, used to prevent
+       re-considering a path that was already constructed once before.
+       Note that this is the only container in which certificatePath
+       objects are retained.
+       Every certificatePath being considered is always in allPaths and in at
+       most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
+       all of which don't retain their values.  */
+       CFMutableSetRef                 allPaths;
+
+    /* No trusted anchor, satisfies the linking to intermediates for all
+       policies (unless considerRejected is true). */
+       CFMutableArrayRef               partialPaths;
+    /* No trusted anchor, does not satisfy linking to intermediates for all
+       policies. */
+       CFMutableArrayRef               rejectedPaths;
+    /* Trusted anchor, satisfies the policies so far. */
+       CFMutableArrayRef               candidatePaths;
+
+       CFIndex                                 partialIX;
+
+       CFArrayRef              leafDetails;
+
+       CFIndex                                 rejectScore;
+
+       bool                    considerRejected;
+       bool                    considerPartials;
+       bool                    canAccessNetwork;
+
+    struct OpaqueSecPVC     path;
+       SecCertificatePathRef   bestPath;
+    bool                    bestPathIsEV;
+
+    CFIndex                 activations;
+    bool (*state)(SecPathBuilderRef);
+    SecPathBuilderCompleted completed;
+    const void *context;
+};
+
+/* State functions.  Return false if a async job was scheduled, return
+   true to execute the next state. */
+static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
+static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
+static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
+static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
+static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
+
+/* Forward declarations. */
+static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
+       SecCertificateRef certificate);
+
+/* IDEA: policies could be made cabable of replacing incoming anchors and
+   anchorsOnly argument values.  For example some policies require the
+   Apple Inc. CA and not any other anchor.  This can be done in
+   SecPathBuilderLeafCertificateChecks since this only runs once. */
+static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
+    SecCertificatePathRef path) {
+    CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
+        kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+        &kCFTypeDictionaryValueCallBacks);
+    builder->leafDetails = CFArrayCreate(kCFAllocatorDefault,
+        (const void **)&certDetail, 1, &kCFTypeArrayCallBacks);
+    CFRelease(certDetail);
+    SecPVCRef pvc = &builder->path;
+    SecPVCSetPath(pvc, path, builder->leafDetails);
+    builder->considerRejected = !SecPVCLeafChecks(pvc);
+}
+
+static void SecPathBuilderInit(SecPathBuilderRef builder,
+       CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
+    CFArrayRef policies, CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
+    SecPathBuilderCompleted completed, const void *context) {
+    secdebug("alloc", "%p", builder);
+       CFAllocatorRef allocator = kCFAllocatorDefault;
+
+    builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
+
+       builder->nextParentSource = 1;
+       builder->considerPartials = false;
+    builder->canAccessNetwork = true;
+
+    builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
+    builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
+    builder->allPaths = CFSetCreateMutable(allocator, 0,
+               &kCFTypeSetCallBacks);
+
+    builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
+    builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
+    builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
+    builder->partialIX = 0;
+
+    /* Init the policy verification context. */
+    SecPVCInit(&builder->path, builder, policies, verifyTime);
+       builder->bestPath = NULL;
+       builder->bestPathIsEV = false;
+       builder->rejectScore = 0;
+
+       /* Let's create all the certificate sources we might want to use. */
+       builder->certificateSource =
+               SecMemoryCertificateSourceCreate(certificates);
+       if (anchors)
+               builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
+       else
+               builder->anchorSource = NULL;
+
+       /* We always search certificateSource for parents since it includes the
+          leaf itself and it might be self signed. */
+       CFArrayAppendValue(builder->parentSources, builder->certificateSource);
+       if (builder->anchorSource) {
+               CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
+       }
+    builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
+       CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
+    if (anchorsOnly) {
+        /* Add the system and user anchor certificate db to the search list
+           if we don't explicitly trust them. */
+        CFArrayAppendValue(builder->parentSources, &kSecSystemAnchorSource);
+        CFArrayAppendValue(builder->parentSources, &kSecUserAnchorSource);
+    } else {
+        /* Only add the system and user anchor certificate db to the
+           anchorSources if we are supposed to trust them. */
+        CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
+        CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
+    }
+    CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
+
+       /* Now let's get the leaf cert and turn it into a path. */
+       SecCertificateRef leaf =
+               (SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
+       SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf);
+       CFSetAddValue(builder->allPaths, path);
+       CFArrayAppendValue(builder->partialPaths, path);
+    if (SecPathBuilderIsAnchor(builder, leaf)) {
+        SecCertificatePathSetIsAnchored(path);
+        CFArrayAppendValue(builder->candidatePaths, path);
+    }
+    SecPathBuilderLeafCertificateChecks(builder, path);
+       CFRelease(path);
+
+    builder->activations = 0;
+    builder->state = SecPathBuilderGetNext;
+    builder->completed = completed;
+    builder->context = context;
+}
+
+SecPathBuilderRef SecPathBuilderCreate(CFArrayRef certificates,
+    CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies,
+    CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
+    SecPathBuilderCompleted completed, const void *context) {
+    SecPathBuilderRef builder = malloc(sizeof(*builder));
+    SecPathBuilderInit(builder, certificates, anchors, anchorsOnly,
+        policies, verifyTime, accessGroups, completed, context);
+    return builder;
+}
+
+static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
+    secdebug("alloc", "%p", builder);
+    dispatch_release_null(builder->queue);
+       if (builder->anchorSource)
+               SecMemoryCertificateSourceDestroy(builder->anchorSource);
+       if (builder->certificateSource)
+               SecMemoryCertificateSourceDestroy(builder->certificateSource);
+    if (builder->itemCertificateSource)
+        SecItemCertificateSourceDestroy(builder->itemCertificateSource);
+       CFReleaseSafe(builder->anchorSources);
+       CFReleaseSafe(builder->parentSources);
+       CFReleaseSafe(builder->allPaths);
+       CFReleaseSafe(builder->partialPaths);
+       CFReleaseSafe(builder->rejectedPaths);
+       CFReleaseSafe(builder->candidatePaths);
+       CFReleaseSafe(builder->leafDetails);
+
+    SecPVCDelete(&builder->path);
+}
+
+bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
+    return builder->canAccessNetwork;
+}
+
+void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
+    if (builder->canAccessNetwork != allow) {
+        builder->canAccessNetwork = allow;
+        if (allow) {
+            secdebug("http", "network access re-enabled by policy");
+            /* re-enabling network_access re-adds kSecCAIssuerSource as
+               a parent source. */
+            CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
+        } else {
+            secdebug("http", "network access disabled by policy");
+            /* disabling network_access removes kSecCAIssuerSource from
+               the list of parent sources. */
+            CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
+                CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
+                &kSecCAIssuerSource);
+            if (ix >= 0)
+                CFArrayRemoveValueAtIndex(builder->parentSources, ix);
+        }
+    }
+}
+
+static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
+       SecCertificateRef certificate) {
+       /* We always look through all anchor sources. */
+       CFIndex count = CFArrayGetCount(builder->anchorSources);
+       CFIndex ix;
+       for (ix = 0; ix < count; ++ix) {
+               SecCertificateSourceRef source = (SecCertificateSourceRef)
+                       CFArrayGetValueAtIndex(builder->anchorSources, ix);
+               if (SecCertificateSourceContains(source, certificate)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/* Return false if path is not a partial, if path was a valid candidate it
+   will have been added to builder->candidatePaths, if path was rejected
+   by the parent certificate checks (because it's expired or some other
+   static chaining check failed) it will have been added to rejectedPaths.
+   Return true path if path is a partial. */
+static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
+       SecCertificatePathRef path) {
+    SecPVCRef pvc = &builder->path;
+    SecPVCSetPath(pvc, path, NULL);
+
+    if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
+        SecPVCGetCertificateCount(pvc) - 1)) {
+        secdebug("trust", "Found rejected path %@", path);
+               CFArrayAppendValue(builder->rejectedPaths, path);
+               return false;
+       }
+
+       SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
+       /* Candidate paths with failed signatures are discarded. */
+       if (vstatus == kSecPathVerifyFailed) {
+        secdebug("trust", "Verify failed for path %@", path);
+               return false;
+       }
+
+       if (vstatus == kSecPathVerifySuccess) {
+               /* The signature chain verified sucessfully, now let's find
+                  out if we have an anchor for path.  */
+               if (SecCertificatePathIsAnchored(path)) {
+            secdebug("trust", "Adding candidate %@", path);
+                       CFArrayAppendValue(builder->candidatePaths, path);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/* Given the builder, a partial chain partial and the parents array, construct
+   a SecCertificatePath for each parent.  After discarding previously
+   considered paths and paths with cycles, sort out which array each path
+   should go in, if any. */
+static void SecPathBuilderProccessParents(SecPathBuilderRef builder,
+    SecCertificatePathRef partial, CFArrayRef parents) {
+    CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
+    CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
+    CFIndex parentIX;
+    bool is_anchor = SecCertificatePathGetNextSourceIndex(partial) <=
+        CFArrayGetCount(builder->anchorSources);
+    secdebug("trust", "found %" PRIdCFIndex " candidate %s", num_parents,
+             (is_anchor ? "anchors" : "parents"));
+    for (parentIX = 0; parentIX < num_parents; ++parentIX) {
+        SecCertificateRef parent = (SecCertificateRef)
+            CFArrayGetValueAtIndex(parents, parentIX);
+        CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
+            parent);
+        if (ixOfParent != kCFNotFound) {
+            /* partial already contains parent.  Let's not add the same
+               certificate again. */
+            if (ixOfParent == rootIX) {
+                /* parent is equal to the root of the partial, so partial
+                   looks to be self issued. */
+                SecCertificatePathSetSelfIssued(partial);
+            }
+            continue;
+        }
+
+        /* FIXME Add more sanity checks to see that parent really can be
+           a parent of partial_root.  subjectKeyID == authorityKeyID,
+           signature algorithm matches public key algorithm, etc. */
+        SecCertificatePathRef path = SecCertificatePathCreate(partial, parent);
+        if (!path)
+            continue;
+        if (!CFSetContainsValue(builder->allPaths, path)) {
+            CFSetAddValue(builder->allPaths, path);
+            if (is_anchor)
+                SecCertificatePathSetIsAnchored(path);
+            if (SecPathBuilderIsPartial(builder, path)) {
+                /* Insert path right at the current position since it's a new
+                   candiate partial. */
+                CFArrayInsertValueAtIndex(builder->partialPaths,
+                    ++builder->partialIX, path);
+                secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
+                    parentIX + 1, num_parents, path);
+            }
+            secdebug("trust", "found new path %@", path);
+        }
+        CFRelease(path);
+    }
+}
+
+/* Callback for the SecPathBuilderGetNext() functions call to
+   SecCertificateSourceCopyParents(). */
+static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
+    SecPathBuilderRef builder = (SecPathBuilderRef)context;
+    SecCertificatePathRef partial = (SecCertificatePathRef)
+        CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
+    secdebug("async", "%@ parents %@", partial, parents);
+    SecPathBuilderProccessParents(builder, partial, parents);
+
+    builder->state = SecPathBuilderGetNext;
+    SecPathBuilderStep(builder);
+}
+
+static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
+    /* If we have any candidates left to go return those first. */
+    if (CFArrayGetCount(builder->candidatePaths)) {
+        SecCertificatePathRef path = (SecCertificatePathRef)
+            CFArrayGetValueAtIndex(builder->candidatePaths, 0);
+        CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
+        secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
+            path);
+        SecPVCSetPath(&builder->path, path, NULL);
+        builder->state = SecPathBuilderValidatePath;
+        return true;
+    }
+
+    /* If we are considering rejected chains we check each rejected path
+       with SecPathBuilderIsPartial() which checks the signature chain and
+       either drops the path if it's not properly signed, add it as a
+       candidate if it has a trusted anchor, or adds it as a partial
+       to be considered once we finish considering all the rejects. */
+    if (builder->considerRejected) {
+        CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
+        if (rejectedIX) {
+            rejectedIX--;
+            SecCertificatePathRef path = (SecCertificatePathRef)
+                CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
+            if (SecPathBuilderIsPartial(builder, path)) {
+                CFArrayInsertValueAtIndex(builder->partialPaths,
+                    ++builder->partialIX, path);
+            }
+            CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
+
+            /* Keep going until we have moved all rejected partials into
+               the regular partials or candidates array. */
+            return true;
+        }
+    }
+
+    /* If builder->partialIX is < 0 we have considered all partial chains
+       this block must ensure partialIX >= 0 if execution continues past
+       it's end. */
+    if (builder->partialIX < 0) {
+        CFIndex num_sources = CFArrayGetCount(builder->parentSources);
+        if (builder->nextParentSource < num_sources) {
+            builder->nextParentSource++;
+            secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
+                builder->nextParentSource, num_sources);
+        } else {
+            /* We've run out of new sources to consider so let's look at
+               rejected chains and after that even consider partials
+               directly.
+               FIXME we might not want to consider partial paths that
+               are subsets of other partial paths, or not consider them
+               at all if we already have an anchored reject. */
+            if (!builder->considerRejected) {
+                builder->considerRejected = true;
+                secdebug("trust", "considering rejected paths");
+            } else if (!builder->considerPartials) {
+                builder->considerPartials = true;
+                secdebug("trust", "considering partials");
+            } else {
+                /* We're all out of options, so we can't produce any more
+                   candidates.  Let's calculate details and return the best
+                   path we found. */
+                builder->state = SecPathBuilderComputeDetails;
+                return true;
+            }
+        }
+        builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
+        secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
+        return true;
+    }
+
+    /* We know builder->partialIX >= 0 if we get here.  */
+    SecCertificatePathRef partial = (SecCertificatePathRef)
+        CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
+    /* Don't try to extend partials anymore once we are in the considerPartials
+       state, since at this point every partial has been extended with every
+       possible parentSource already. */
+    if (builder->considerPartials) {
+        --builder->partialIX;
+        SecPVCSetPath(&builder->path, partial, NULL);
+        builder->state = SecPathBuilderValidatePath;
+        return true;
+    }
+
+    /* Attempt to extend this partial path with another certificate. This
+       should give us a list of potential parents to consider. */
+    secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
+        builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
+        partial);
+
+    /* Attempt to extend partial, leaving all possible extended versions
+       of partial in builder->extendedPaths. */
+       CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
+    CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
+    if (sourceIX < num_anchor_sources + builder->nextParentSource) {
+        SecCertificateSourceRef source;
+        if (sourceIX < num_anchor_sources) {
+            source = (SecCertificateSourceRef)
+                CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
+            secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
+                     num_anchor_sources);
+        } else {
+            CFIndex parentIX = sourceIX - num_anchor_sources;
+            source = (SecCertificateSourceRef)
+                CFArrayGetValueAtIndex(builder->parentSources, parentIX);
+            secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
+                     builder->nextParentSource);
+        }
+        SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
+        SecCertificateRef root = SecCertificatePathGetRoot(partial);
+               return SecCertificateSourceCopyParents(source, root,
+            builder, SecPathBuilderExtendPaths);
+    } else {
+        --builder->partialIX;
+    }
+
+    return true;
+}
+
+/* One or more of the policies did not accept the candidate path. */
+static void SecPathBuilderReject(SecPathBuilderRef builder) {
+    check(builder);
+    SecPVCRef pvc = &builder->path;
+
+    builder->state = SecPathBuilderGetNext;
+
+    if (builder->bestPathIsEV && !pvc->is_ev) {
+        /* We never replace an ev reject with a non ev reject. */
+        return;
+    }
+
+    CFIndex rejectScore = builder->rejectScore;
+       CFIndex score = SecCertificatePathScore(builder->path.path,
+        SecPVCGetVerifyTime(&builder->path));
+
+    /* The current chain is valid for EV, but revocation checking failed.  We
+       replace any previously accepted or rejected non EV chains with the
+       current one. */
+    if (pvc->is_ev && !builder->bestPathIsEV) {
+        rejectScore = 0;
+    }
+
+#if 0
+    if (pvc->is_ev) {
+        /* Since this means we found a valid ev chain that was revoked,
+           we might want to switch directly to the
+           SecPathBuilderComputeDetails state here if we think further
+           searching for new chains is pointless.  For now we'll keep
+           going, since we could accept an alternate EV certification
+           path that isn't revoked. */
+        builder->state = SecPathBuilderComputeDetails;
+    }
+#endif
+
+    /* Do this last so that changes to rejectScore above will take affect. */
+       if (!builder->bestPath || score > rejectScore) {
+        if (builder->bestPath) {
+            secdebug("reject",
+                "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex " %@",
+                (builder->bestPathIsEV ? "" : "non "),
+                (builder->rejectScore == INTPTR_MAX ? "accept" : "reject"),
+                builder->rejectScore,
+                (pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
+        } else {
+            secdebug("reject", "%sev reject score: %" PRIdCFIndex " %@",
+                (pvc->is_ev ? "" : "non "), score, builder->path.path);
+        }
+
+               builder->rejectScore = score;
+        builder->bestPath = pvc->path;
+        builder->bestPathIsEV = pvc->is_ev;
+       } else {
+        secdebug("reject", "%sev reject score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
+            (pvc->is_ev ? "" : "non "), score, rejectScore, builder->path.path);
+    }
+}
+
+/* All policies accepted the candidate path. */
+static void SecPathBuilderAccept(SecPathBuilderRef builder) {
+    check(builder);
+    SecPVCRef pvc = &builder->path;
+    if (pvc->is_ev || !builder->bestPathIsEV) {
+               secdebug("accept", "replacing %sev accept with %sev %@",
+            (builder->bestPathIsEV ? "" : "non "),
+            (pvc->is_ev ? "" : "non "), builder->path.path);
+        builder->rejectScore = INTPTR_MAX; /* CFIndex is signed long which is INTPTR_T */
+               builder->bestPathIsEV = pvc->is_ev;
+        builder->bestPath = pvc->path;
+    }
+
+    /* If we found the best accept we can we want to switch directly to the
+       SecPathBuilderComputeDetails state here, since we're done. */
+    if (pvc->is_ev || !pvc->optionally_ev)
+        builder->state = SecPathBuilderComputeDetails;
+    else
+        builder->state = SecPathBuilderGetNext;
+}
+
+/* Return true iff a given path satisfies all the specified policies at
+   verifyTime. */
+static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
+    SecPVCRef pvc = &builder->path;
+
+    if (builder->considerRejected) {
+        SecPathBuilderReject(builder);
+        return true;
+    }
+
+    builder->state = SecPathBuilderDidValidatePath;
+    return SecPVCPathChecks(pvc);
+}
+
+static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
+    SecPVCRef pvc = &builder->path;
+    if (pvc->result) {
+        SecPathBuilderAccept(builder);
+    } else {
+        SecPathBuilderReject(builder);
+    }
+    assert(builder->state != SecPathBuilderDidValidatePath);
+    return true;
+}
+
+static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
+    // foobar
+    SecPVCRef pvc = &builder->path;
+#if 0
+    if (!builder->caller_wants_details) {
+        SecPVCSetPath(pvc, builder->bestPath, NULL);
+        pvc->result = builder->rejectScore == INTPTR_MAX;
+        builder->state = SecPathBuilderReportResult;
+        return true;
+    }
+#endif
+    CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
+    CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
+        pathLength, builder->leafDetails);
+    CFRetainSafe(details);
+    SecPVCSetPath(pvc, builder->bestPath, details);
+    /* Only report on EV stuff if the bestPath actually was valid for EV. */
+    pvc->optionally_ev = builder->bestPathIsEV;
+    pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
+        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    for (ix = 1; ix < pathLength; ++ix) {
+        CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
+            kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+            &kCFTypeDictionaryValueCallBacks);
+        CFArrayAppendValue(details, certDetail);
+        CFRelease(certDetail);
+        SecPVCParentCertificateChecks(pvc, ix);
+               SecPVCGrayListedKeyChecks(pvc, ix);
+               SecPVCBlackListedKeyChecks(pvc, ix);
+    }
+    builder->state = SecPathBuilderReportResult;
+    bool completed = SecPVCPathChecks(pvc);
+
+    /* Reject the certificate if it was accepted before but we failed it now. */
+    if (builder->rejectScore == INTPTR_MAX && !pvc->result) {
+        builder->rejectScore = 0;
+    }
+
+    CFReleaseSafe(details);
+
+    return completed;
+}
+
+static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
+    SecPVCRef pvc = &builder->path;
+    if (pvc->info && pvc->is_ev && pvc->result) {
+        CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
+            kCFBooleanTrue);
+        SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
+        CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
+        if (leafCompanyName) {
+            CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
+                leafCompanyName);
+            CFRelease(leafCompanyName);
+        }
+        if (pvc->rvcs) {
+            CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
+            if (nextUpdate == 0) {
+                CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
+                    kCFBooleanFalse);
+            } else {
+                CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
+                CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
+                    validUntil);
+                CFRelease(validUntil);
+                CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
+                    kCFBooleanTrue);
+            }
+        }
+    }
+
+    /* This will trigger the outer step function to call the completion
+       function. */
+    builder->state = NULL;
+    return false;
+}
+
+/* @function SecPathBuilderStep
+   @summary This is the core of the async engine.
+   @description Return false iff job is complete, true if a network request
+   is pending.
+   builder->state is a function pointer which is to be invoked.
+   If you call this function from within a builder->state invocation it
+   immediately returns true.
+   Otherwise the following steps are repeated endlessly (unless a step returns)
+   builder->state is invoked.  If it returns true and builder->state is still
+   non NULL this proccess is repeated.
+   If a state returns false, SecPathBuilder will return true
+   if builder->state is non NULL.
+   If builder->state is NULL then regardless of what the state function returns
+   the completion callback will be invoked and the builder will be deallocated.
+ */
+bool SecPathBuilderStep(SecPathBuilderRef builder) {
+    if (builder->activations) {
+        secdebug("async", "activations: %lu returning true",
+            builder->activations);
+        return true;
+    }
+
+    secdebug("async", "activations: %lu", builder->activations);
+    builder->activations++;
+    while (builder->state && builder->state(builder));
+    --builder->activations;
+
+    if (builder->state) {
+        secdebug("async", "waiting for async reply, exiting");
+        /* A state returned false, it's waiting for network traffic.  Let's
+           return. */
+        return true;
+    }
+
+    if (builder->activations) {
+        /* There is still at least one other running instance of this builder
+           somewhere on the stack, we let that instance take care of sending
+           the client a response. */
+        return false;
+    }
+
+    SecTrustResultType result = (builder->rejectScore == INTPTR_MAX
+        ? kSecTrustResultUnspecified : kSecTrustResultRecoverableTrustFailure);
+    
+    secdebug("trust", "completed: %@ details: %@ result: %d",
+        builder->bestPath, builder->path.details, result);
+    
+    if (builder->completed) {
+        builder->completed(builder->context, builder->bestPath,
+            builder->path.details, builder->path.info, result);
+    }
+
+    /* Finally, destroy the builder and free it. */
+    SecPathBuilderDestroy(builder);
+    free(builder);
+
+    return false;
+}
+
+dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
+    return builder->queue;
+}
+
+// MARK: -
+// MARK: SecTrustServer
+/********************************************************
+ ****************** SecTrustServer **********************
+ ********************************************************/
+
+typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
+
+static void
+SecTrustServerEvaluateCompleted(const void *userData,
+                                SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
+                                SecTrustResultType result) {
+    SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
+    evaluated(result, details, info, chain, NULL);
+    Block_release(evaluated);
+}
+
+void
+SecTrustServerEvaluateBlock(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
+    SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
+    /* Call the actual evaluator function. */
+    SecPathBuilderRef builder = SecPathBuilderCreate(certificates, anchors,
+                                                     anchorsOnly, policies,
+                                                     verifyTime, accessGroups,
+                                                     SecTrustServerEvaluateCompleted, userData);
+    dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
+}
+
+
+// NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
+SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
+    dispatch_semaphore_t done = dispatch_semaphore_create(0);
+    __block SecTrustResultType result = kSecTrustResultInvalid;
+    SecTrustServerEvaluateBlock(certificates, anchors, anchorsOnly, policies, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
+        result = tr;
+        if (tr == kSecTrustResultInvalid) {
+            if (perror) {
+                *perror = error;
+                CFRetainSafe(error);
+            }
+        } else {
+            if (pdetails) {
+                *pdetails = details;
+                CFRetainSafe(details);
+            }
+            if (pinfo) {
+                *pinfo = info;
+                CFRetainSafe(info);
+            }
+            if (pchain) {
+                *pchain = chain;
+                CFRetainSafe(chain);
+            }
+        }
+        dispatch_semaphore_signal(done);
+    });
+    dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
+
+    return result;
+}