SecTrust.cpp   [plain text]


/*
 * Copyright (c) 2002-2015 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@
 */

#include "SecTrust.h"
#include "SecTrustPriv.h"
#include "Trust.h"
#include <security_keychain/SecTrustSettingsPriv.h>
#include "SecBase.h"
#include "SecBridge.h"
#include "SecInternal.h"
#include "SecInternalP.h"
#include "SecTrustSettings.h"
#include "SecCertificatePriv.h"
#include "SecCertificateP.h"
#include "SecCertificatePrivP.h"
#include "SecPolicyPriv.h"
#include <security_utilities/cfutilities.h>
#include <security_utilities/cfmunge.h>
#include <CoreFoundation/CoreFoundation.h>
#include <syslog.h>

// forward declarations
#if !SECTRUST_OSX
CFArrayRef SecTrustCopyDetails(SecTrustRef trust);
static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix);
static void SecTrustCheckException(const void *key, const void *value, void *context);
#endif

typedef struct SecTrustCheckExceptionContext {
	CFDictionaryRef exception;
	bool exceptionNotFound;
} SecTrustCheckExceptionContext;

// public trust result constants
const CFStringRef kSecTrustEvaluationDate           = CFSTR("TrustEvaluationDate");
const CFStringRef kSecTrustExtendedValidation       = CFSTR("TrustExtendedValidation");
const CFStringRef kSecTrustOrganizationName         = CFSTR("Organization");
const CFStringRef kSecTrustResultValue              = CFSTR("TrustResultValue");
const CFStringRef kSecTrustRevocationChecked        = CFSTR("TrustRevocationChecked");
const CFStringRef kSecTrustRevocationReason         = CFSTR("TrustRevocationReason");
const CFStringRef kSecTrustRevocationValidUntilDate = CFSTR("TrustExpirationDate");
const CFStringRef kSecTrustResultDetails            = CFSTR("TrustResultDetails");

//
// CF boilerplate
//
#if !SECTRUST_OSX
CFTypeID SecTrustGetTypeID(void)
{
	BEGIN_SECAPI

	return gTypes().Trust.typeID;

	END_SECAPI1(_kCFRuntimeNotATypeID)
}
#endif

//
// Sec* API bridge functions
//
#if !SECTRUST_OSX
OSStatus SecTrustCreateWithCertificates(
	CFTypeRef certificates,
	CFTypeRef policies,
	SecTrustRef *trustRef)
{
	BEGIN_SECAPI
	Required(trustRef);
	*trustRef = (new Trust(certificates, policies))->handle();
	END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus
SecTrustSetPolicies(SecTrustRef trustRef, CFTypeRef policies)
{
	BEGIN_SECAPI
	Trust::required(trustRef)->policies(policies);
	END_SECAPI
}
#endif

/* OS X only: __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA) */
OSStatus
SecTrustSetOptions(SecTrustRef trustRef, SecTrustOptionFlags options)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
	CSSM_APPLE_TP_ACTION_DATA actionData = {
		CSSM_APPLE_TP_ACTION_VERSION,
		(CSSM_APPLE_TP_ACTION_FLAGS)options
	};
	Trust *trust = Trust::required(trustRef);
	CFDataRef actionDataRef = CFDataCreate(NULL,
		(const UInt8 *)&actionData,
		(CFIndex)sizeof(CSSM_APPLE_TP_ACTION_DATA));
	trust->action(CSSM_TP_ACTION_DEFAULT);
	trust->actionData(actionDataRef);
	if (actionDataRef) CFRelease(actionDataRef);
	END_SECAPI
#else
	/* bridge to support API functionality for legacy callers */
	OSStatus status = errSecSuccess;
#if 1
#warning STU: <rdar://21328005>
//%%% need to ensure that the exception covers only the requested options
#else
	CFArrayRef details = SecTrustGetDetails(trustRef); // NOTE: performs the evaluation if not done already
	CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
	CFIndex ix;
	for (ix = 0; ix < pathLength; ++ix) {
		CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
		CFIndex detailCount = CFDictionaryGetCount(detail);
		if (detailCount > 0) {
			// see if we can ignore this error
			syslog(LOG_ERR, "SecTrustSetOptions: examining detail dictionary items at ix %ld", (long)ix);
			CFShow(detail);
		}
	}
	syslog(LOG_ERR, "SecTrustSetOptions: creating trust exception");
#endif
	CFDataRef exceptions = SecTrustCopyExceptions(trustRef);
	if (exceptions) {
		SecTrustSetExceptions(trustRef, exceptions);
		CFRelease(exceptions);
	}


#if SECTRUST_DEPRECATION_WARNINGS
	bool displayModifyMsg = false;
	bool displayNetworkMsg = false;
	bool displayPolicyMsg = false;
	const char *baseMsg = "WARNING: SecTrustSetOptions called with";
	const char *modifyMsg = "Use SecTrustSetExceptions and SecTrustCopyExceptions to modify default trust results.";
	const char *networkMsg = "Use SecTrustSetNetworkFetchAllowed to specify whether missing certificates can be fetched from the network.";
	const char *policyMsg = "Use SecPolicyCreateRevocation to specify revocation policy requirements.";

	if (options & kSecTrustOptionAllowExpired) {
		syslog(LOG_ERR, "%s %s.", baseMsg, "kSecTrustOptionAllowExpired");
		displayModifyMsg = true;
	}
	if (options & kSecTrustOptionAllowExpiredRoot) {
		syslog(LOG_ERR, "%s %s.", baseMsg, "kSecTrustOptionAllowExpiredRoot");
		displayModifyMsg = true;
	}
	if (options & kSecTrustOptionFetchIssuerFromNet) {
		syslog(LOG_ERR, "%s %s.", baseMsg, "kSecTrustOptionFetchIssuerFromNet");
		displayNetworkMsg = true;
	}
	if (options & kSecTrustOptionRequireRevPerCert) {
		syslog(LOG_ERR, "%s %s.", baseMsg, "kSecTrustOptionRequireRevPerCert");
		displayPolicyMsg = true;
	}
	if (displayModifyMsg || displayNetworkMsg || displayPolicyMsg) {
		syslog(LOG_ERR, "%s %s %s",
			(displayModifyMsg) ? modifyMsg : "",
			(displayNetworkMsg) ? networkMsg : "",
			(displayPolicyMsg) ? policyMsg : "");
	}
#endif

	return status;

#endif
}

/* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */
OSStatus SecTrustSetParameters(
    SecTrustRef trustRef,
    CSSM_TP_ACTION action,
    CFDataRef actionData)
{
#if !SECTRUST_OSX
    BEGIN_SECAPI
    Trust *trust = Trust::required(trustRef);
    trust->action(action);
    trust->actionData(actionData);
    END_SECAPI
#else
	/* bridge to support API functionality for legacy callers */
	OSStatus status;
	CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0;
	if (actionData) {
		CSSM_APPLE_TP_ACTION_DATA *actionDataPtr = (CSSM_APPLE_TP_ACTION_DATA *) CFDataGetBytePtr(actionData);
		if (actionDataPtr) {
			actionFlags = actionDataPtr->ActionFlags;
		}
	}
	// note that SecTrustOptionFlags == CSSM_APPLE_TP_ACTION_FLAGS;
	// both are sizeof(uint32) and the flag values have identical meanings
    status = SecTrustSetOptions(trustRef, (SecTrustOptionFlags)actionFlags);

#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustSetParameters was deprecated in 10.7. Use SecTrustSetOptions instead.");
#endif

	return status;

#endif
}

#if !SECTRUST_OSX
OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust, CFArrayRef anchorCertificates)
{
    BEGIN_SECAPI
    Trust::required(trust)->anchors(anchorCertificates);
    END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust, Boolean anchorCertificatesOnly)
{
    BEGIN_SECAPI
    Trust::AnchorPolicy policy = (anchorCertificatesOnly) ? Trust::useAnchorsOnly : Trust::useAnchorsAndBuiltIns;
    Trust::required(trust)->anchorPolicy(policy);
    END_SECAPI
}
#endif

/* OS X only: __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA) */
OSStatus SecTrustSetKeychains(SecTrustRef trust, CFTypeRef keychainOrArray)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
		StorageManager::KeychainList keychains;
	// avoid unnecessary global initializations if an empty array is passed in
	if (!( (keychainOrArray != NULL) &&
				(CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) &&
				(CFArrayGetCount((CFArrayRef)keychainOrArray) == 0) )) {
		globals().storageManager.optionalSearchList(keychainOrArray, keychains);
	}
	Trust::required(trust)->searchLibs(keychains);
	END_SECAPI
#else
	/* this function is currently unsupported in unified SecTrust */
    // TODO: pull all certs out of the specified keychains for the evaluation?
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustSetKeychains does nothing in 10.11. Use SecTrustSetAnchorCertificates{Only} to provide anchors.");
#endif
	return errSecSuccess;
#endif
}

#if !SECTRUST_OSX
OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate)
{
    BEGIN_SECAPI
    Trust::required(trust)->time(verifyDate);
    END_SECAPI
}
#endif

#if !SECTRUST_OSX
CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust)
{
	CFAbsoluteTime verifyTime = 0;
	OSStatus __secapiresult = errSecSuccess;
	try {
		CFRef<CFDateRef> verifyDate = Trust::required(trust)->time();
		verifyTime = CFDateGetAbsoluteTime(verifyDate);
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }
	return verifyTime;
}
#endif



#if !SECTRUST_OSX
OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *resultP)
{
	SecTrustResultType trustResult = kSecTrustResultInvalid;
	CFArrayRef exceptions = NULL;
	OSStatus __secapiresult = errSecSuccess;
	try {
		Trust *trustObj = Trust::required(trust);
		trustObj->evaluate();
		trustResult = trustObj->result();
		exceptions = trustObj->exceptions();
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (__secapiresult) {
		return __secapiresult;
	}

	/* post-process trust result based on exceptions */
	if (trustResult == kSecTrustResultUnspecified) {
		/* If leaf is in exceptions -> proceed, otherwise unspecified. */
		if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
			trustResult = kSecTrustResultProceed;
	}
	else if (trustResult == kSecTrustResultRecoverableTrustFailure && exceptions) {
		/* If we have exceptions get details and match to exceptions. */
		CFArrayRef details = SecTrustCopyDetails(trust);
		if (details) {
			CFIndex pathLength = CFArrayGetCount(details);
			struct SecTrustCheckExceptionContext context = {};
			CFIndex ix;
			for (ix = 0; ix < pathLength; ++ix) {
				CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
			//	if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf))
			//		trustResult = kSecTrustResultFatalTrustFailure;
				context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix);
				CFDictionaryApplyFunction(detail, SecTrustCheckException, &context);
				if (context.exceptionNotFound) {
					break;
				}
			}
			if (!context.exceptionNotFound)
				trustResult = kSecTrustResultProceed;
		}
	}


	secdebug("SecTrustEvaluate", "SecTrustEvaluate trust result = %d", (int)trustResult);
	if (resultP) {
		*resultP = trustResult;
	}
	return __secapiresult;
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustEvaluateAsync(SecTrustRef trust,
	dispatch_queue_t queue, SecTrustCallback result)
{
	BEGIN_SECAPI
	dispatch_async(queue, ^{
		try {
			Trust *trustObj = Trust::required(trust);
			trustObj->evaluate();
			SecTrustResultType trustResult = trustObj->result();
			result(trust, trustResult);
		}
		catch (...) {
			result(trust, kSecTrustResultInvalid);
		};
	});
	END_SECAPI
}
#endif

//
// Construct the "official" result evidence and return it
//
/* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */
OSStatus SecTrustGetResult(
    SecTrustRef trustRef,
    SecTrustResultType *result,
	CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain)
{
#if !SECTRUST_OSX
    BEGIN_SECAPI
    Trust *trust = Trust::required(trustRef);
    if (result)
        *result = trust->result();
    if (certChain && statusChain)
        trust->buildEvidence(*certChain, TPEvidenceInfo::overlayVar(*statusChain));
    END_SECAPI
#else
	/* bridge to support old functionality */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustGetResult has been deprecated since 10.7, and may not return a statusChain in 10.11. Please use SecTrustGetTrustResult instead.");
#endif
	SecTrustResultType trustResult;
	OSStatus status = SecTrustGetTrustResult(trustRef, &trustResult);
	if (result) {
		*result = trustResult;
	}
	if (certChain && !statusChain) {
		/* This is the easy case; caller only wants cert chain and not status chain. */
		CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
		CFIndex idx, count = SecTrustGetCertificateCount(trustRef);
		for (idx=0; idx < count; idx++) {
			SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trustRef, idx);
			if (certificate) {
				CFArrayAppendValue(certArray, certificate);
			}
		}
		*certChain = certArray;
	}
	else if (certChain && statusChain) {
		/*
		 * Here is where backward compatibility gets ugly. CSSM_TP_APPLE_EVIDENCE_INFO* is tied to a
		 * Trust object and does not exist in the new unified world. Unfortunately, some clients are
		 * still calling this legacy API and grubbing through the info for StatusBits and StatusCodes.
		 * If they want this info, then we have to do a legacy evaluation to get it. The info struct
		 * goes away once the old-style object does, so we must keep the old-style object alive after
		 * returning from the function.
		 *
		 * TODO: keep a dictionary and figure out how to expire entries when no longer needed.,
		 * or build the evidence info ourselves: rdar://21005914
		 */
		static CFMutableArrayRef sTrustArray = NULL;

		// make array of Certificate instances from unified SecCertificateRefs
		CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
		CFIndex idx, count = SecTrustGetCertificateCount(trustRef);
		for (idx=0; idx < count; idx++) {
			SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trustRef, idx);
			if (certificate) {
				SecCertificateRef itemImplRef = SecCertificateCreateItemImplInstance(certificate);
				if (itemImplRef) {
					CFArrayAppendValue(certArray, itemImplRef);
					CFRelease(itemImplRef);
				}
			}
		}
		// make array of Policy instances from unified SecPolicyRefs
		CFMutableArrayRef policyArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
		CFArrayRef policies = NULL;
		status = SecTrustCopyPolicies(trustRef, &policies);
		count = (!status && policies) ? CFArrayGetCount(policies) : 0;
		for (idx=0; idx < count; idx++) {
			SecPolicyRef policy = (SecPolicyRef) CFArrayGetValueAtIndex(policies, idx);
			if (policy) {
				SecPolicyRef itemImplRef = SecPolicyCreateItemImplInstance(policy);
				if (itemImplRef) {
					CFArrayAppendValue(policyArray, itemImplRef);
					CFRelease(itemImplRef);
				}
			}
		}
		// now make a Trust instance and evaluate it
		try {
			Trust *trustObj = new Trust(certArray, policyArray);
			SecTrustRef trust = trustObj->handle();
			if (!trust) {
				MacOSError::throwMe(errSecTrustNotAvailable);
			}
			if (!sTrustArray) {
				sTrustArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
				if (!sTrustArray) {
					MacOSError::throwMe(errSecAllocate);
				}
			}
			// fetch the built cert chain and status chain
			CFArrayRef itemImplCertArray = NULL;
			trustObj->evaluate();
			trustObj->buildEvidence(itemImplCertArray, TPEvidenceInfo::overlayVar(*statusChain));

			// convert each Certificate in the built chain to a unified SecCertificateRef
			CFMutableArrayRef outCertChain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
			CFIndex idx, count = (itemImplCertArray) ? CFArrayGetCount(itemImplCertArray) : 0;
			for (idx=0; idx < count; idx++) {
				SecCertificateRef inCert = (SecCertificateRef) CFArrayGetValueAtIndex(itemImplCertArray, idx);
				SecCertificateRef outCert = SecCertificateCreateFromItemImplInstance(inCert);
				if (outCert) {
					CFArrayAppendValue(outCertChain, outCert);
					CFRelease(outCert);
				}
			}
			*certChain = outCertChain;
			if (itemImplCertArray) {
				CFRelease(itemImplCertArray);
			}
			CFArrayAppendValue(sTrustArray, trust);
			status = errSecSuccess;
		}
		catch (const MacOSError &err) { status=err.osStatus(); }
		catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); }
		catch (const std::bad_alloc &) { status=errSecAllocate; }
		catch (...) { status=errSecInternalComponent; }

		if (policyArray)
			CFRelease(policyArray);
		if (certArray)
			CFRelease(certArray);
	}
	return status;
#endif
}

//
// Retrieve result of trust evaluation only
//
#if !SECTRUST_OSX
OSStatus SecTrustGetTrustResult(SecTrustRef trustRef,
	SecTrustResultType *result)
{
    BEGIN_SECAPI
    Trust *trust = Trust::required(trustRef);
    if (result) *result = trust->result();
    END_SECAPI
}
#endif

//
// Retrieve extended validation trust results
//
/* OS X only: __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA) */
OSStatus SecTrustCopyExtendedResult(SecTrustRef trust, CFDictionaryRef *result)
{
#if !SECTRUST_OSX
    BEGIN_SECAPI
	Trust *trustObj = Trust::required(trust);
	if (result == nil)
		return errSecParam;
	trustObj->extendedResult(*result);
    END_SECAPI
#else
	/* bridge to support old functionality */
#if SECTRUST_DEPRECATION_WARNINGS
    syslog(LOG_ERR, "WARNING: SecTrustCopyExtendedResult will be deprecated in an upcoming release. Please use SecTrustCopyResult instead.");
#endif
	CFDictionaryRef resultDict = SecTrustCopyResult(trust);
	if (result == nil) {
		return errSecParam;
	}
	*result = resultDict;
	return errSecSuccess;
#endif
}

//
// Retrieve CSSM-level information for those who want to dig down
//
/* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */
OSStatus SecTrustGetCssmResult(SecTrustRef trust, CSSM_TP_VERIFY_CONTEXT_RESULT_PTR *result)
{
#if !SECTRUST_OSX
    BEGIN_SECAPI
    Required(result) = Trust::required(trust)->cssmResult();
    END_SECAPI
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustGetCssmResult has been deprecated since 10.7, and has no functional equivalent in 10.11. Please use SecTrustCopyResult instead.");
#endif
	if (result) {
		*result = NULL;
	}
	return errSecServiceNotAvailable;
#endif
}

#if SECTRUST_OSX
static void applyPropertyToCssmResultCode(const void *_key, const void *_value, void *context) {
	CFStringRef key = (CFStringRef)_key;
	CFStringRef value = (CFStringRef)_value;
	OSStatus *result = (OSStatus *)context;
	if (CFGetTypeID(_value) != CFStringGetTypeID()) {
		return;
	}
	if (!CFEqual(CFSTR("value"), key)) {
		return;
	}
	if (CFEqual(CFSTR("Invalid certificate chain linkage."), value)) {
		*result = CSSMERR_APPLETP_INVALID_ID_LINKAGE;
	} else if (CFEqual(CFSTR("One or more unsupported critical extensions found."), value)) {
		*result = CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN;
	} else if (CFEqual(CFSTR("Root certificate is not trusted."), value)) {
		*result = CSSMERR_TP_NOT_TRUSTED;
	} else if (CFEqual(CFSTR("Hostname mismatch."), value)) {
		*result = CSSMERR_APPLETP_HOSTNAME_MISMATCH;
	} else if (CFEqual(CFSTR("One or more certificates have expired or are not valid yet."), value)) {
		*result = CSSMERR_TP_CERT_EXPIRED;
	} else if (CFEqual(CFSTR("Policy requirements not met."), value)) {
		*result = CSSMERR_TP_VERIFY_ACTION_FAILED;
	}
}
#endif

//
// Retrieve CSSM_LEVEL TP return code
//
/* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */
OSStatus SecTrustGetCssmResultCode(SecTrustRef trustRef, OSStatus *result)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
		Trust *trust = Trust::required(trustRef);
	if (trust->result() == kSecTrustResultInvalid)
		return errSecParam;
	else
		Required(result) = trust->cssmResultCode();
	END_SECAPI
#else
	/* bridge to support old functionality */
#if SECTRUST_DEPRECATION_WARNINGS
    syslog(LOG_ERR, "WARNING: SecTrustGetCssmResultCode has been deprecated since 10.7, and will be removed in a future release. Please use SecTrustCopyProperties instead.");
#endif
	if (!trustRef || !result) {
		return errSecParam;
	}
	CFArrayRef properties = SecTrustCopyProperties(trustRef);
	if (!properties) {
		*result = 0;
		return errSecSuccess;
	}
	OSStatus cssmResultCode = 0;
	CFIndex ix, count = CFArrayGetCount(properties);
	for (ix = 0; ix < count; ++ix) {
		CFDictionaryRef property = (CFDictionaryRef)
			CFArrayGetValueAtIndex(properties, ix);
		CFDictionaryApplyFunction(property, applyPropertyToCssmResultCode, &cssmResultCode);
	}
	if (result) {
		*result = cssmResultCode;
	}
	if (properties) {
		CFRelease(properties);
	}
	return errSecSuccess;
#endif
}

/* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */
OSStatus SecTrustGetTPHandle(SecTrustRef trust, CSSM_TP_HANDLE *handle)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
		Required(handle) = Trust::required(trust)->getTPHandle();
	END_SECAPI
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustGetTPHandle has been deprecated since 10.7, and cannot return CSSM objects in 10.11. Please stop using it.");
#endif
	if (handle) {
		*handle = NULL;
	}
	return errSecServiceNotAvailable;
#endif
}

#if !SECTRUST_OSX
OSStatus SecTrustCopyPolicies(SecTrustRef trust, CFArrayRef *policies)
{
	BEGIN_SECAPI
		CFArrayRef currentPolicies = Trust::required(trust)->policies();
	if (currentPolicies != NULL)
	{
		CFRetain(currentPolicies);
	}

	Required(policies) = currentPolicies;
	END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustSetNetworkFetchAllowed(SecTrustRef trust, Boolean allowFetch)
{
	BEGIN_SECAPI
	Trust *trustObj = Trust::required(trust);
	Trust::NetworkPolicy netPolicy = (allowFetch) ?
		Trust::useNetworkEnabled : Trust::useNetworkDisabled;
	trustObj->networkPolicy(netPolicy);
	END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustGetNetworkFetchAllowed(SecTrustRef trust, Boolean *allowFetch)
{
	BEGIN_SECAPI
	Boolean allowed = false;
	Trust *trustObj = Trust::required(trust);
	Trust::NetworkPolicy netPolicy = trustObj->networkPolicy();
	if (netPolicy == Trust::useNetworkDefault) {
		// network fetch is enabled by default for SSL only
		allowed = trustObj->policySpecified(trustObj->policies(), CSSMOID_APPLE_TP_SSL);
	} else {
		// caller has explicitly set the network policy
		allowed = (netPolicy == Trust::useNetworkEnabled);
	}
	Required(allowFetch) = allowed;
	END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustSetOCSPResponse(SecTrustRef trust, CFTypeRef responseData)
{
	BEGIN_SECAPI
	Trust::required(trust)->responses(responseData);
	END_SECAPI
}
#endif

#if !SECTRUST_OSX
OSStatus SecTrustCopyCustomAnchorCertificates(SecTrustRef trust, CFArrayRef *anchorCertificates)
{
	BEGIN_SECAPI
	CFArrayRef customAnchors = Trust::required(trust)->anchors();
	Required(anchorCertificates) = (customAnchors) ?
		(const CFArrayRef)CFRetain(customAnchors) : (const CFArrayRef)NULL;
	END_SECAPI
}
#endif

//
// Get the user's default anchor certificate set
//
/* OS X only */
OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *anchorCertificates)
{
	BEGIN_SECAPI

	return SecTrustSettingsCopyUnrestrictedRoots(
			true, true, true,		/* all domains */
			anchorCertificates);

	END_SECAPI
}

#if SECTRUST_OSX
/* We have an iOS-style SecTrustRef, but we need to return a CDSA-based SecKeyRef.
 */
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust)
{
	SecKeyRef pubKey = NULL;
	SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, 0);
	(void) SecCertificateCopyPublicKey(certificate, &pubKey);
	return pubKey;
}
#else
/* new in 10.6 */
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust)
{
	SecKeyRef pubKey = NULL;
	CFArrayRef certChain = NULL;
	CFArrayRef evidenceChain = NULL;
	CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
	OSStatus __secapiresult = errSecSuccess;
	try {
		Trust *trustObj = Trust::required(trust);
		if (trustObj->result() == kSecTrustResultInvalid) {
			// Trust hasn't been evaluated; attempt to retrieve public key from leaf.
			SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
			__secapiresult = SecCertificateCopyPublicKey(cert, &pubKey);
			if (pubKey) {
				return pubKey;
			}
			// Otherwise, we must evaluate first.
			trustObj->evaluate();
			if (trustObj->result() == kSecTrustResultInvalid) {
				MacOSError::throwMe(errSecTrustNotAvailable);
			}
		}
		if (trustObj->evidence() == nil) {
			trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
		}
		evidenceChain = trustObj->evidence();
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (certChain)
		CFRelease(certChain);

	if (evidenceChain) {
		if (CFArrayGetCount(evidenceChain) > 0) {
			SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(evidenceChain, 0);
			__secapiresult = SecCertificateCopyPublicKey(cert, &pubKey);
		}
		// do not release evidenceChain, as it is owned by the trust object.
	}
	return pubKey;
}
#endif

#if !SECTRUST_OSX
/* new in 10.6 */
CFIndex SecTrustGetCertificateCount(SecTrustRef trust)
{
	CFIndex chainLen = 0;
	CFArrayRef certChain = NULL;
	CFArrayRef evidenceChain = NULL;
	CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
    OSStatus __secapiresult = errSecSuccess;
	try {
		Trust *trustObj = Trust::required(trust);
		if (trustObj->result() == kSecTrustResultInvalid) {
			trustObj->evaluate();
			if (trustObj->result() == kSecTrustResultInvalid)
				MacOSError::throwMe(errSecTrustNotAvailable);
		}
		if (trustObj->evidence() == nil)
			trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
		evidenceChain = trustObj->evidence();
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (certChain)
		CFRelease(certChain);

	if (evidenceChain)
		chainLen = CFArrayGetCount(evidenceChain); // don't release, trust object owns it.

    return chainLen;
}
#endif

#if !SECTRUST_OSX
/* new in 10.6 */
SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust, CFIndex ix)
{
	SecCertificateRef certificate = NULL;
	CFArrayRef certChain = NULL;
	CFArrayRef evidenceChain = NULL;
	CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
    OSStatus __secapiresult = errSecSuccess;
	try {
		Trust *trustObj = Trust::required(trust);
		if (trustObj->result() == kSecTrustResultInvalid) {
			// If caller is asking for the leaf, we can return it without
			// having to evaluate the entire chain. Note that we don't retain
			// the cert as it's owned by the trust and this is a 'Get' API.
			if (ix == 0) {
				CFArrayRef certs = trustObj->certificates();
				if (certs && (CFArrayGetCount(certs) > 0)) {
					certificate = (SecCertificateRef) CFArrayGetValueAtIndex(certs, 0);
					if (certificate) {
						return certificate;
					}
				}
			}
			// Otherwise, we must evaluate first.
			trustObj->evaluate();
			if (trustObj->result() == kSecTrustResultInvalid) {
				MacOSError::throwMe(errSecTrustNotAvailable);
			}
		}
		if (trustObj->evidence() == nil) {
			trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
		}
		evidenceChain = trustObj->evidence();
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (certChain)
		CFRelease(certChain);

	if (evidenceChain) {
		if (ix < CFArrayGetCount(evidenceChain)) {
			certificate = (SecCertificateRef) CFArrayGetValueAtIndex(evidenceChain, ix);
			// note: we do not retain this certificate. The assumption here is
			// that the certificate is retained by the trust object, so it is
			// valid unil the trust is released (or until re-evaluated.)
			// also note: we do not release the evidenceChain, as it is owned
			// by the trust object.
		}
	}
	return certificate;
}
#endif


#if !SECTRUST_OSX
static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest");
static CFStringRef kSecCertificateDetailStatusCodes = CFSTR("StatusCodes");

static void
_AppendStatusCode(CFMutableArrayRef array, OSStatus statusCode)
{
	if (!array)
		return;
	SInt32 num = statusCode;
	CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberSInt32Type, &num);
	if (!numRef)
		return;
	CFArrayAppendValue(array, numRef);
	CFRelease(numRef);
}
#endif

#if !SECTRUST_OSX
CFArrayRef SecTrustCopyDetails(SecTrustRef trust)
{
	// This function returns an array of dictionaries, one per certificate,
	// holding status info for each certificate in the evaluated chain.
	//
	CFIndex count, chainLen = 0;
	CFArrayRef certChain = NULL;
	CFMutableArrayRef details = NULL;
	CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
    OSStatus __secapiresult = errSecSuccess;
	try {
		Trust *trustObj = Trust::required(trust);
		if (trustObj->result() == kSecTrustResultInvalid) {
			trustObj->evaluate();
			if (trustObj->result() == kSecTrustResultInvalid)
				MacOSError::throwMe(errSecTrustNotAvailable);
		}
		trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (certChain) {
		chainLen = CFArrayGetCount(certChain);
		CFRelease(certChain);
	}
	if (statusChain) {
		details = CFArrayCreateMutable(NULL, chainLen, &kCFTypeArrayCallBacks);
		for (count = 0; count < chainLen; count++) {
			CFMutableDictionaryRef certDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
				0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
			CFMutableArrayRef statusCodes = CFArrayCreateMutable(kCFAllocatorDefault,
				0, &kCFTypeArrayCallBacks);
			CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &statusChain[count];
			CSSM_TP_APPLE_CERT_STATUS statBits = evInfo->StatusBits;

			// translate status bits
			if (statBits & CSSM_CERT_STATUS_EXPIRED)
				_AppendStatusCode(statusCodes, errSecCertificateExpired);
			if (statBits & CSSM_CERT_STATUS_NOT_VALID_YET)
				_AppendStatusCode(statusCodes, errSecCertificateNotValidYet);
			if (statBits & CSSM_CERT_STATUS_TRUST_SETTINGS_DENY)
				_AppendStatusCode(statusCodes, errSecTrustSettingDeny);

			// translate status codes
			unsigned int i;
			for (i = 0; i < evInfo->NumStatusCodes; i++) {
				CSSM_RETURN scode = evInfo->StatusCodes[i];
				_AppendStatusCode(statusCodes, (OSStatus)scode);
			}

			CFDictionarySetValue(certDict, kSecCertificateDetailStatusCodes, statusCodes);
			CFRelease(statusCodes);
			CFArrayAppendValue(details, certDict);
			CFRelease(certDict);
		}
	}
	return details;
}
#endif

#if !SECTRUST_OSX
static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix)
{
	CFArrayRef exceptions = NULL;
    OSStatus __secapiresult = errSecSuccess;
	try {
		exceptions = Trust::required(trust)->exceptions();
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	if (!exceptions || ix >= CFArrayGetCount(exceptions))
		return NULL;
	CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
	if (CFGetTypeID(exception) != CFDictionaryGetTypeID())
		return NULL;

	SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
	if (!certificate)
		return NULL;

	/* If the exception contains the current certificate's sha1Digest in the
	   kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
	CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate);
	CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest);
	if (!digestValue || !CFEqual(sha1Digest, digestValue))
		exception = NULL;

	return exception;
}
#endif

#if !SECTRUST_OSX
static void SecTrustCheckException(const void *key, const void *value, void *context)
{
	struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context;
	if (cec->exception) {
		CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key);
		if (!exceptionValue || !CFEqual(value, exceptionValue)) {
			cec->exceptionNotFound = true;
		}
	} else {
		cec->exceptionNotFound = true;
	}
}
#endif

#if !SECTRUST_OSX
/* new in 10.9 */
CFDataRef SecTrustCopyExceptions(SecTrustRef trust)
{
	CFArrayRef details = SecTrustCopyDetails(trust);
	CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
	CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault,
			pathLength, &kCFTypeArrayCallBacks);
	CFIndex ix;
	for (ix = 0; ix < pathLength; ++ix) {
		CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
		CFIndex detailCount = CFDictionaryGetCount(detail);
		CFMutableDictionaryRef exception;
		if (ix == 0 || detailCount > 0) {
			exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
				detailCount + 1, detail);
			SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
			CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
			if (digest) {
				CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest);
			}
		} else {
			/* Add empty exception dictionaries for non leaf certs which have no exceptions
			 * to save space.
			 */
			exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault,
				NULL, NULL, 0,
				&kCFTypeDictionaryKeyCallBacks,
				&kCFTypeDictionaryValueCallBacks);
		}
		CFArrayAppendValue(exceptions, exception);
		CFReleaseNull(exception);
	}
	CFReleaseSafe(details);

	/* Remove any trailing empty dictionaries to save even more space (we skip the leaf
	   since it will never be empty). */
	for (ix = pathLength; ix-- > 1;) {
		CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
		if (CFDictionaryGetCount(exception) == 0) {
			CFArrayRemoveValueAtIndex(exceptions, ix);
		} else {
			break;
		}
	}

	CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
			exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
	CFRelease(exceptions);

	return encodedExceptions;
}
#endif

#if !SECTRUST_OSX
/* new in 10.9 */
bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions)
{
	CFArrayRef exceptions;
	exceptions = (CFArrayRef)CFPropertyListCreateWithData(kCFAllocatorDefault,
		encodedExceptions, kCFPropertyListImmutable, NULL, NULL);
	if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
		CFRelease(exceptions);
		exceptions = NULL;
	}

	OSStatus __secapiresult = errSecSuccess;
	try {
		Trust::required(trust)->exceptions(exceptions);
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	/* If there is a valid exception entry for our current leaf we're golden. */
	if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
		return true;

	/* The passed in exceptions didn't match our current leaf, so we discard it. */
	try {
		Trust::required(trust)->exceptions(NULL);
		__secapiresult = errSecSuccess;
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }

	return false;
}
#endif

#if !SECTRUST_OSX
/* new in 10.9 */
CFDictionaryRef
SecTrustCopyResult(SecTrustRef trust)
{
	CFDictionaryRef result = NULL;
	try {
		result = Trust::required(trust)->results();
		// merge details into result
		CFArrayRef details = SecTrustCopyDetails(trust);
		if (details) {
			CFDictionarySetValue((CFMutableDictionaryRef)result,
				kSecTrustResultDetails, details);
			CFRelease(details);
		}
	}
	catch (...) {
		if (result) {
			CFRelease(result);
			result = NULL;
		}
	}
	return result;
}
#endif

#if !SECTRUST_OSX
/* new in 10.7 */
CFArrayRef
SecTrustCopyProperties(SecTrustRef trust)
{
	/* can't use SECAPI macros, since this function does not return OSStatus */
	CFArrayRef result = NULL;
	try {
		result = Trust::required(trust)->properties();
	}
	catch (...) {
		if (result) {
			CFRelease(result);
			result = NULL;
		}
	}
	return result;
}
#endif

/* deprecated in 10.5 */
OSStatus SecTrustGetCSSMAnchorCertificates(const CSSM_DATA **cssmAnchors,
	uint32 *cssmAnchorCount)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
	CertGroup certs;
	Trust::gStore().getCssmRootCertificates(certs);
	Required(cssmAnchors) = certs.blobCerts();
	Required(cssmAnchorCount) = certs.count();
	END_SECAPI
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustGetCSSMAnchorCertificates has been deprecated since 10.5, and cannot return CSSM objects in 10.11. Please stop using it.");
#endif
	if (cssmAnchors) {
		*cssmAnchors = NULL;
	}
	if (cssmAnchorCount) {
		*cssmAnchorCount = 0;
	}
	return errSecServiceNotAvailable;
#endif
}


//
// Get and set user trust settings. Deprecated in 10.5.
// User Trust getter, deprecated, works as it always has.
//
OSStatus SecTrustGetUserTrust(SecCertificateRef certificate,
    SecPolicyRef policy, SecTrustUserSetting *trustSetting)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
	StorageManager::KeychainList searchList;
	globals().storageManager.getSearchList(searchList);
	Required(trustSetting) = Trust::gStore().find(
		Certificate::required(certificate),
		Policy::required(policy),
		searchList);
	END_SECAPI
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustGetUserTrust has been deprecated since 10.5, and does nothing in 10.11. Please stop using it.");
#endif
	return errSecServiceNotAvailable;
#endif
}

//
// The public setter, also deprecated; it maps to the appropriate
// Trust Settings call if possible, else throws errSecUnimplemented.
//
OSStatus SecTrustSetUserTrust(SecCertificateRef certificate,
    SecPolicyRef policy, SecTrustUserSetting trustSetting)
{
#if !SECTRUST_OSX
	SecTrustSettingsResult tsResult = kSecTrustSettingsResultInvalid;
	OSStatus ortn;
	Boolean isRoot;

	Policy::required(policy);
	switch(trustSetting) {
		case kSecTrustResultProceed:
			/* different SecTrustSettingsResult depending in root-ness */
			ortn = SecCertificateIsSelfSigned(certificate, &isRoot);
			if(ortn) {
				return ortn;
			}
			if(isRoot) {
				tsResult = kSecTrustSettingsResultTrustRoot;
			}
			else {
				tsResult = kSecTrustSettingsResultTrustAsRoot;
			}
			break;
		case kSecTrustResultDeny:
			tsResult = kSecTrustSettingsResultDeny;
			break;
		default:
			return errSecUnimplemented;
	}

	/* make a usage constraints dictionary */
	CFRef<CFMutableDictionaryRef> usageDict(CFDictionaryCreateMutable(NULL,
		0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
	CFDictionaryAddValue(usageDict, kSecTrustSettingsPolicy, policy);
	if(tsResult != kSecTrustSettingsResultTrustRoot) {
		/* skip if we're specifying the default */
		SInt32 result = tsResult;
		CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &result);
		CFDictionarySetValue(usageDict, kSecTrustSettingsResult, cfNum);
		CFRelease(cfNum);
	}
	return SecTrustSettingsSetTrustSettings(certificate, kSecTrustSettingsDomainUser,
		usageDict);
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustSetUserTrust has been deprecated since 10.5, and does nothing in 10.11. Please stop using it.");
#endif
	return errSecServiceNotAvailable;
#endif
}

//
// This one is the now-private version of what SecTrustSetUserTrust() used to
// be. The public API can no longer manipulate User Trust settings, only
// view them.
//
OSStatus SecTrustSetUserTrustLegacy(SecCertificateRef certificate,
    SecPolicyRef policy, SecTrustUserSetting trustSetting)
{
#if !SECTRUST_OSX
	BEGIN_SECAPI
	switch (trustSetting) {
    case kSecTrustResultProceed:
    case kSecTrustResultConfirm:
    case kSecTrustResultDeny:
    case kSecTrustResultUnspecified:
		break;
	default:
		MacOSError::throwMe(errSecInvalidTrustSetting);
	}
	Trust::gStore().assign(
		Certificate::required(certificate),
		Policy::required(policy),
		trustSetting);
	END_SECAPI
#else
	/* this function is unsupported in unified SecTrust */
#if SECTRUST_DEPRECATION_WARNINGS
	syslog(LOG_ERR, "WARNING: SecTrustSetUserTrustLegacy does nothing in 10.11. Please stop using it.");
#endif
	return errSecServiceNotAvailable;
#endif
}