SecTrustServer.c   [plain text]


/*
 * Copyright (c) 2006-2010 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
 *
 *  Created by Michael Brouwer on 12/12/08.
 *
 */

#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 "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);
    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;
    }

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