#include "SecTrust.h"
#include "SecTrustPriv.h"
#include "Trust.h"
#include "SecBase.h"
#include "SecBridge.h"
#include "SecInternal.h"
#include "SecTrustSettings.h"
#include "SecTrustSettingsPriv.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>
CFArrayRef SecTrustCopyInputCertificates(SecTrustRef trust);
CFArrayRef SecTrustCopyInputAnchors(SecTrustRef trust);
CFArrayRef SecTrustCopyConstructedChain(SecTrustRef trust);
static CSSM_TP_APPLE_EVIDENCE_INFO * SecTrustGetEvidenceInfo(SecTrustRef trust);
typedef struct SecTrustCheckExceptionContext {
CFDictionaryRef exception;
bool exceptionNotFound;
} SecTrustCheckExceptionContext;
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");
struct resultmap_entry_s {
const CFStringRef checkstr;
const CSSM_RETURN resultcode;
};
typedef struct resultmap_entry_s resultmap_entry_t;
const resultmap_entry_t cssmresultmap[] = {
{ CFSTR("SSLHostname"), CSSMERR_APPLETP_HOSTNAME_MISMATCH },
{ CFSTR("email"), CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND },
{ CFSTR("IssuerCommonName"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("SubjectCommonName"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("SubjectCommonNamePrefix"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("SubjectCommonNameTEST"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("SubjectOrganization"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("SubjectOrganizationalUnit"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("EAPTrustedServerNames"), CSSMERR_APPLETP_HOSTNAME_MISMATCH },
{ CFSTR("CertificatePolicy"), CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION },
{ CFSTR("KeyUsage"), CSSMERR_APPLETP_INVALID_KEY_USAGE },
{ CFSTR("ExtendedKeyUsage"), CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE },
{ CFSTR("BasicConstraints"), CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS },
{ CFSTR("QualifiedCertStatements"), CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT },
{ CFSTR("IntermediateSPKISHA256"), CSSMERR_APPLETP_IDENTIFIER_MISSING },
{ CFSTR("IntermediateEKU"), CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE },
{ CFSTR("AnchorSHA1"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("AnchorSHA256"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("AnchorTrusted"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("AnchorApple"), CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH },
{ CFSTR("NonEmptySubject"), CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT },
{ CFSTR("IdLinkage"), CSSMERR_APPLETP_INVALID_AUTHORITY_ID },
{ CFSTR("WeakIntermediates"), CSSMERR_TP_INVALID_CERTIFICATE },
{ CFSTR("WeakLeaf"), CSSMERR_TP_INVALID_CERTIFICATE },
{ CFSTR("WeakRoot"), CSSMERR_TP_INVALID_CERTIFICATE },
{ CFSTR("KeySize"), CSSMERR_CSP_UNSUPPORTED_KEY_SIZE },
{ CFSTR("SignatureHashAlgorithms"), CSSMERR_CSP_ALGID_MISMATCH },
{ CFSTR("SystemTrustedWeakHash"), CSSMERR_CSP_INVALID_DIGEST_ALGORITHM },
{ CFSTR("CriticalExtensions"), CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN },
{ CFSTR("ChainLength"), CSSMERR_APPLETP_PATH_LEN_CONSTRAINT },
{ CFSTR("BasicCertificateProcessing"), CSSMERR_TP_INVALID_CERTIFICATE },
{ CFSTR("ExtendedValidation"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("Revocation"), CSSMERR_TP_CERT_REVOKED },
{ CFSTR("RevocationResponseRequired"), CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK },
{ CFSTR("CertificateTransparency"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("BlackListedLeaf"), CSSMERR_TP_CERT_REVOKED },
{ CFSTR("GrayListedLeaf"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("GrayListedKey"), CSSMERR_TP_NOT_TRUSTED },
{ CFSTR("BlackListedKey"), CSSMERR_TP_CERT_REVOKED },
{ CFSTR("CheckLeafMarkerOid"), CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION },
{ CFSTR("CheckLeafMarkerOidNoValueCheck"), CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION },
{ CFSTR("CheckIntermediateMarkerOid"), CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION },
{ CFSTR("UsageConstraints"), CSSMERR_APPLETP_TRUST_SETTING_DENY },
{ CFSTR("NotValidBefore"), CSSMERR_TP_CERT_NOT_VALID_YET },
{ CFSTR("ValidIntermediates"), CSSMERR_TP_CERT_EXPIRED },
{ CFSTR("ValidLeaf"), CSSMERR_TP_CERT_EXPIRED },
{ CFSTR("ValidRoot"), CSSMERR_TP_CERT_EXPIRED },
};
typedef struct {
SecTrustOptionFlags flags;
CFIndex certIX;
SecTrustRef trustRef;
CFMutableDictionaryRef filteredException;
CFDictionaryRef oldException;
} SecExceptionFilterContext;
static inline char *CFStringToCString(CFStringRef inStr)
{
if (!inStr)
return (char *)strdup("");
CFRetain(inStr);
CFIndex length = CFStringGetLength(inStr); size_t len = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
char *buffer = (char *)malloc(len); if (!CFStringGetCString(inStr, buffer, len, kCFStringEncodingUTF8))
buffer[0] = 0;
CFRelease(inStr);
return buffer;
}
static void
filter_exception(const void *key, const void *value, void *context)
{
SecExceptionFilterContext *ctx = (SecExceptionFilterContext *)context;
if (!ctx) { return; }
SecTrustOptionFlags options = ctx->flags;
CFMutableDictionaryRef filteredException = ctx->filteredException;
CFStringRef keystr = (CFStringRef)key;
if (ctx->oldException && CFDictionaryContainsKey(ctx->oldException, key)) {
CFDictionaryAddValue(filteredException, key, CFDictionaryGetValue(ctx->oldException, key));
return;
}
bool allowed = false;
if (CFEqual(keystr, CFSTR("SHA1Digest"))) {
allowed = true; }
else if (CFEqual(keystr, CFSTR("NotValidBefore"))) {
allowed = ((options & kSecTrustOptionAllowExpired) != 0);
}
else if (CFEqual(keystr, CFSTR("ValidLeaf"))) {
allowed = ((options & kSecTrustOptionAllowExpired) != 0);
}
else if (CFEqual(keystr, CFSTR("ValidIntermediates"))) {
allowed = ((options & kSecTrustOptionAllowExpired) != 0);
}
else if (CFEqual(keystr, CFSTR("ValidRoot"))) {
if (((options & kSecTrustOptionAllowExpired) != 0) ||
((options & kSecTrustOptionAllowExpiredRoot) != 0)) {
allowed = true;
}
}
else if (CFEqual(keystr, CFSTR("AnchorTrusted"))) {
bool implicitAnchors = ((options & kSecTrustOptionImplicitAnchors) != 0);
if (implicitAnchors && ctx->trustRef &&
(ctx->certIX < SecTrustGetCertificateCount(ctx->trustRef))) {
Boolean isSelfSigned = false;
SecCertificateRef cert = SecTrustGetCertificateAtIndex(ctx->trustRef, ctx->certIX);
if (cert && (errSecSuccess == SecCertificateIsSelfSigned(cert, &isSelfSigned)) &&
isSelfSigned) {
allowed = true;
}
}
}
else if (CFEqual(keystr, CFSTR("KeyUsage")) ||
CFEqual(keystr, CFSTR("ExtendedKeyUsage")) ||
CFEqual(keystr, CFSTR("BasicConstraints")) ||
CFEqual(keystr, CFSTR("NonEmptySubject")) ||
CFEqual(keystr, CFSTR("IdLinkage"))) {
allowed = false;
}
else {
char *cstr = CFStringToCString(keystr);
syslog(LOG_ERR, "Unfiltered exception: %s", (cstr) ? cstr : "<NULL>");
if (cstr) { free(cstr); }
allowed = false;
}
if (allowed) {
CFDictionaryAddValue(filteredException, key, value);
}
}
OSStatus
SecTrustSetOptions(SecTrustRef trustRef, SecTrustOptionFlags options)
{
OSStatus status = errSecSuccess;
CFDataRef encodedExceptions = SecTrustCopyExceptions(trustRef);
CFArrayRef exceptions = NULL,
oldExceptions = SecTrustGetTrustExceptionsArray(trustRef);
if (encodedExceptions) {
exceptions = (CFArrayRef)CFPropertyListCreateWithData(kCFAllocatorDefault,
encodedExceptions, kCFPropertyListImmutable, NULL, NULL);
CFRelease(encodedExceptions);
encodedExceptions = NULL;
}
if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
CFRelease(exceptions);
exceptions = NULL;
}
if (oldExceptions && exceptions &&
CFArrayGetCount(oldExceptions) > CFArrayGetCount(exceptions)) {
oldExceptions = NULL;
}
if (oldExceptions && exceptions && CFArrayGetCount(oldExceptions) > 0) {
CFDictionaryRef oldLeafExceptions = (CFDictionaryRef)CFArrayGetValueAtIndex(oldExceptions, 0);
CFDictionaryRef leafExceptions = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, 0);
CFDataRef oldDigest = (CFDataRef)CFDictionaryGetValue(oldLeafExceptions, CFSTR("SHA1Digest"));
CFDataRef digest = (CFDataRef)CFDictionaryGetValue(leafExceptions, CFSTR("SHA1Digest"));
if (!oldDigest || !digest || !CFEqual(oldDigest, digest)) {
oldExceptions = NULL;
}
}
if (exceptions) {
CFMutableArrayRef filteredExceptions = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFIndex i, exceptionCount = (filteredExceptions) ? CFArrayGetCount(exceptions) : 0;
for (i = 0; i < exceptionCount; ++i) {
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, i);
CFDictionaryRef oldException = NULL;
if (oldExceptions && i < CFArrayGetCount(oldExceptions)) {
oldException = (CFDictionaryRef)CFArrayGetValueAtIndex(oldExceptions, i);
}
CFMutableDictionaryRef filteredException = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (exception && filteredException) {
SecExceptionFilterContext filterContext = { options, i, trustRef, filteredException, oldException };
CFDictionaryApplyFunction(exception, filter_exception, &filterContext);
CFArrayAppendValue(filteredExceptions, filteredException);
CFRelease(filteredException);
}
}
if (filteredExceptions) {
CFIndex filteredCount = CFArrayGetCount(filteredExceptions);
for (i = filteredCount; i-- > 1;) {
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(filteredExceptions, i);
if (CFDictionaryGetCount(exception) == 0) {
CFArrayRemoveValueAtIndex(filteredExceptions, i);
} else {
break;
}
}
encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
filteredExceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
CFRelease(filteredExceptions);
SecTrustSetExceptions(trustRef, encodedExceptions);
CFRelease(encodedExceptions);
}
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;
}
OSStatus SecTrustSetParameters(
SecTrustRef trustRef,
CSSM_TP_ACTION action,
CFDataRef actionData)
{
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;
}
}
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;
}
OSStatus SecTrustSetKeychains(SecTrustRef trust, CFTypeRef keychainOrArray)
{
#if SECTRUST_DEPRECATION_WARNINGS
syslog(LOG_ERR, "WARNING: SecTrustSetKeychains does nothing in 10.11. Use SecTrustSetAnchorCertificates{Only} to provide anchors.");
#endif
return errSecSuccess;
}
OSStatus SecTrustGetResult(
SecTrustRef trustRef,
SecTrustResultType *result,
CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain)
{
#if SECTRUST_DEPRECATION_WARNINGS
syslog(LOG_ERR, "WARNING: SecTrustGetResult has been deprecated since 10.7. Please use SecTrustGetTrustResult instead.");
#endif
SecTrustResultType trustResult;
OSStatus status = SecTrustGetTrustResult(trustRef, &trustResult);
if (status != errSecSuccess) {
return status;
}
if (result) {
*result = trustResult;
}
if (certChain) {
*certChain = SecTrustCopyConstructedChain(trustRef);
}
if (statusChain) {
*statusChain = SecTrustGetEvidenceInfo(trustRef);
}
return status;
}
OSStatus SecTrustCopyExtendedResult(SecTrustRef trust, CFDictionaryRef *result)
{
#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) {
CFReleaseNull(resultDict);
return errSecParam;
}
*result = resultDict;
return errSecSuccess;
}
OSStatus SecTrustGetCssmResult(SecTrustRef trust, CSSM_TP_VERIFY_CONTEXT_RESULT_PTR *result)
{
#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;
}
static CSSM_RETURN *copyCssmStatusCodes(SecTrustRef trust,
unsigned int index, unsigned int *numStatusCodes)
{
if (!trust || !numStatusCodes) {
return NULL;
}
*numStatusCodes = 0;
CFArrayRef details = SecTrustCopyFilteredDetails(trust);
CFIndex chainLength = (details) ? CFArrayGetCount(details) : 0;
if (!(index < chainLength)) {
CFReleaseSafe(details);
return NULL;
}
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, index);
CFIndex ix, detailCount = CFDictionaryGetCount(detail);
*numStatusCodes = (unsigned int)detailCount;
CSSM_RETURN *statusCodes = (CSSM_RETURN*)malloc((detailCount+1) * sizeof(CSSM_RETURN));
statusCodes[*numStatusCodes] = 0;
const unsigned int resultmaplen = sizeof(cssmresultmap) / sizeof(resultmap_entry_t);
const void *keys[detailCount];
CFDictionaryGetKeysAndValues(detail, &keys[0], NULL);
for (ix = 0; ix < detailCount; ix++) {
CFStringRef key = (CFStringRef)keys[ix];
CSSM_RETURN statusCode = CSSM_OK;
for (unsigned int mapix = 0; mapix < resultmaplen; mapix++) {
CFStringRef str = (CFStringRef) cssmresultmap[mapix].checkstr;
if (CFStringCompare(str, key, 0) == kCFCompareEqualTo) {
statusCode = (CSSM_RETURN) cssmresultmap[mapix].resultcode;
break;
}
}
if (statusCode == CSSMERR_TP_CERT_REVOKED) {
SInt32 reason;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(detail, key);
if (number && CFNumberGetValue(number, kCFNumberSInt32Type, &reason)) {
statusCodes[*numStatusCodes] = (CSSM_RETURN)reason;
}
}
statusCodes[ix] = statusCode;
}
CFReleaseSafe(details);
return statusCodes;
}
static uint8_t convertCssmResultToPriority(CSSM_RETURN resultCode) {
switch (resultCode) {
case CSSMERR_TP_CERT_REVOKED:
case CSSMERR_APPLETP_TRUST_SETTING_DENY:
return 1;
case CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS:
case CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT:
case CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT:
case CSSMERR_APPLETP_INVALID_AUTHORITY_ID:
case CSSMERR_TP_INVALID_CERTIFICATE:
case CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN:
return 2;
case CSSMERR_TP_CERT_EXPIRED:
return 3;
case CSSMERR_TP_NOT_TRUSTED:
case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH:
return 4;
default:
return 5;
}
}
#include <libDER/oidsPriv.h>
#include <Security/oidscert.h>
static bool isSoftwareUpdateDevelopment(SecTrustRef trust) {
bool isPolicy = false, isEKU = false;
CFArrayRef policies = NULL;
SecTrustCopyPolicies(trust, &policies);
if (policies) {
SecPolicyRef swUpdatePolicy = SecPolicyCreateAppleSWUpdateSigning();
if (swUpdatePolicy && CFArrayContainsValue(policies, CFRangeMake(0, CFArrayGetCount(policies)),
swUpdatePolicy)) {
isPolicy = true;
}
if (swUpdatePolicy) { CFRelease(swUpdatePolicy); }
CFRelease(policies);
}
if (!isPolicy) {
return false;
}
CFArrayRef details = SecTrustCopyFilteredDetails(trust);
CFIndex ix, count = CFArrayGetCount(details);
bool hasDisqualifyingError = false;
for (ix = 0; ix < count; ix++) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
if (ix == 0) { if (CFDictionaryGetCount(detail) != 1 || CFDictionaryGetValue(detail, CFSTR("ExtendedKeyUsage")) != kCFBooleanFalse) { hasDisqualifyingError = true;
break;
}
} else {
if (CFDictionaryGetCount(detail) > 0) { hasDisqualifyingError = true;
break;
}
}
}
CFReleaseSafe(details);
if (hasDisqualifyingError) {
return false;
}
SecCertificateRef leaf = SecTrustGetCertificateAtIndex(trust, 0);
CSSM_DATA *fieldValue = NULL;
if (errSecSuccess != SecCertificateCopyFirstFieldValue(leaf, &CSSMOID_ExtendedKeyUsage, &fieldValue)) {
return false;
}
if (fieldValue && fieldValue->Data && fieldValue->Length == sizeof(CSSM_X509_EXTENSION)) {
const CSSM_X509_EXTENSION *ext = (const CSSM_X509_EXTENSION *)fieldValue->Data;
if (ext->format == CSSM_X509_DATAFORMAT_PARSED) {
const CE_ExtendedKeyUsage *ekus = (const CE_ExtendedKeyUsage *)ext->value.parsedValue;
if (ekus && (ekus->numPurposes == 1) && ekus->purposes[0].Data &&
(ekus->purposes[0].Length == CSSMOID_APPLE_EKU_CODE_SIGNING_DEV.Length) &&
(memcmp(ekus->purposes[0].Data, CSSMOID_APPLE_EKU_CODE_SIGNING_DEV.Data,
ekus->purposes[0].Length) == 0)) {
isEKU = true;
}
}
}
SecCertificateReleaseFirstFieldValue(leaf, &CSSMOID_ExtendedKeyUsage, fieldValue);
return isEKU;
}
OSStatus SecTrustGetCssmResultCode(SecTrustRef trustRef, OSStatus *result)
{
#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;
}
SecTrustResultType trustResult = kSecTrustResultInvalid;
(void) SecTrustGetTrustResult(trustRef, &trustResult);
if (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified) {
if (result) { *result = 0; }
return errSecSuccess;
}
if (isSoftwareUpdateDevelopment(trustRef)) {
if (result) {
*result = CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT;
}
return errSecSuccess;
}
OSStatus cssmResultCode = errSecSuccess;
uint8_t resultCodePriority = 0xFF;
CFIndex ix, count = SecTrustGetCertificateCount(trustRef);
for (ix = 0; ix < count; ix++) {
unsigned int numStatusCodes;
CSSM_RETURN *statusCodes = NULL;
statusCodes = copyCssmStatusCodes(trustRef, (uint32_t)ix, &numStatusCodes);
if (statusCodes && numStatusCodes > 0) {
unsigned int statusIX;
for (statusIX = 0; statusIX < numStatusCodes; statusIX++) {
CSSM_RETURN currStatus = statusCodes[statusIX];
uint8_t currPriority = convertCssmResultToPriority(currStatus);
if (resultCodePriority > currPriority) {
cssmResultCode = currStatus;
resultCodePriority = currPriority;
}
}
}
if (statusCodes) { free(statusCodes); }
if (resultCodePriority == 1) { break; }
}
if (result) {
*result = cssmResultCode;
}
return errSecSuccess;
}
OSStatus SecTrustGetTPHandle(SecTrustRef trust, CSSM_TP_HANDLE *handle)
{
#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;
}
OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *anchorCertificates)
{
BEGIN_SECAPI
return SecTrustSettingsCopyUnrestrictedRoots(
true, true, true,
anchorCertificates);
END_SECAPI
}
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust)
{
SecKeyRef pubKey = NULL;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, 0);
(void) SecCertificateCopyPublicKey(certificate, &pubKey);
return pubKey;
}
typedef struct __TSecTrust {
CFRuntimeBase _base;
CFArrayRef _certificates;
CFArrayRef _anchors;
CFTypeRef _policies;
CFArrayRef _responses;
CFArrayRef _SCTs;
CFArrayRef _trustedLogs;
CFDateRef _verifyDate;
CFTypeRef _chain;
SecKeyRef _publicKey;
CFArrayRef _details;
CFDictionaryRef _info;
CFArrayRef _exceptions;
SecTrustResultType _trustResult;
bool _anchorsOnly;
bool _keychainsAllowed;
void* _legacy_info_array;
void* _legacy_status_array;
SecTrustResultType _trustResultBeforeExceptions;
dispatch_queue_t _trustQueue;
} TSecTrust;
CFArrayRef SecTrustCopyInputCertificates(SecTrustRef trust)
{
if (!trust) { return NULL; };
TSecTrust *secTrust = (TSecTrust *)trust;
if (secTrust->_certificates) {
CFRetain(secTrust->_certificates);
}
return secTrust->_certificates;
}
CFArrayRef SecTrustCopyInputAnchors(SecTrustRef trust)
{
if (!trust) { return NULL; };
TSecTrust *secTrust = (TSecTrust *)trust;
if (secTrust->_anchors) {
CFRetain(secTrust->_anchors);
}
return secTrust->_anchors;
}
CFArrayRef SecTrustCopyConstructedChain(SecTrustRef trust)
{
CFMutableArrayRef certChain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFIndex idx, count = SecTrustGetCertificateCount(trust);
for (idx=0; idx < count; idx++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, idx);
if (certificate) {
CFArrayAppendValue(certChain, certificate);
}
}
CFArrayRef inputCertArray = SecTrustCopyInputCertificates(trust);
CFArrayRef inputAnchorArray = SecTrustCopyInputAnchors(trust);
CFIndex inputCertIdx, inputCertCount = (inputCertArray) ? CFArrayGetCount(inputCertArray) : 0;
CFIndex inputAnchorIdx, inputAnchorCount = (inputAnchorArray) ? CFArrayGetCount(inputAnchorArray) : 0;
for (idx=0; idx < count; idx++) {
SecCertificateRef tmpCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, idx);
if (tmpCert) {
SecCertificateRef matchCert = NULL;
for (inputCertIdx=0; inputCertIdx < inputCertCount && !matchCert; inputCertIdx++) {
SecCertificateRef inputCert = (SecCertificateRef) CFArrayGetValueAtIndex(inputCertArray, inputCertIdx);
if (inputCert && CFEqual(inputCert, tmpCert)) {
matchCert = inputCert;
}
}
for (inputAnchorIdx=0; inputAnchorIdx < inputAnchorCount && !matchCert; inputAnchorIdx++) {
SecCertificateRef inputAnchor = (SecCertificateRef) CFArrayGetValueAtIndex(inputAnchorArray, inputAnchorIdx);
if (inputAnchor && CFEqual(inputAnchor, tmpCert)) {
matchCert = inputAnchor;
}
}
if (matchCert) {
CFArraySetValueAtIndex(certChain, idx, matchCert);
}
}
}
if (inputCertArray) {
CFRelease(inputCertArray);
}
if (inputAnchorArray) {
CFRelease(inputAnchorArray);
}
return certChain;
}
static CSSM_TP_APPLE_EVIDENCE_INFO *
SecTrustGetEvidenceInfo(SecTrustRef trust)
{
TSecTrust *secTrust = (TSecTrust *)trust;
if (!secTrust) {
return NULL;
}
if (secTrust->_trustResult != kSecTrustResultInvalid &&
secTrust->_legacy_info_array) {
return (CSSM_TP_APPLE_EVIDENCE_INFO *)secTrust->_legacy_info_array;
}
CFIndex idx, count = SecTrustGetCertificateCount(trust);
CFArrayRef inputCertArray = SecTrustCopyInputCertificates(trust);
CFArrayRef inputAnchorArray = SecTrustCopyInputAnchors(trust);
CFIndex inputCertIdx, inputCertCount = (inputCertArray) ? CFArrayGetCount(inputCertArray) : 0;
CFIndex inputAnchorIdx, inputAnchorCount = (inputAnchorArray) ? CFArrayGetCount(inputAnchorArray) : 0;
CSSM_TP_APPLE_EVIDENCE_INFO *infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)calloc(count, sizeof(CSSM_TP_APPLE_EVIDENCE_INFO));
CSSM_RETURN *statusArray = NULL;
unsigned int numStatusCodes = 0;
for (idx=0; idx < count; idx++) {
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, idx);
if (!cert) {
continue;
}
CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[idx];
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
if (secTrust->_verifyDate) {
now = CFDateGetAbsoluteTime(secTrust->_verifyDate);
}
CFAbsoluteTime na = SecCertificateNotValidAfter(cert);
if (na < now) {
evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED;
}
CFAbsoluteTime nb = SecCertificateNotValidBefore(cert);
if (nb > now) {
evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET;
}
for (inputAnchorIdx=0; inputAnchorIdx < inputAnchorCount; inputAnchorIdx++) {
SecCertificateRef inputAnchor = (SecCertificateRef) CFArrayGetValueAtIndex(inputAnchorArray, inputAnchorIdx);
if (inputAnchor && CFEqual(inputAnchor, cert)) {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS;
break;
}
}
for (inputCertIdx=0; inputCertIdx < inputCertCount; inputCertIdx++) {
SecCertificateRef inputCert = (SecCertificateRef) CFArrayGetValueAtIndex(inputCertArray, inputCertIdx);
if (inputCert && CFEqual(inputCert, cert)) {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS;
break;
}
}
CFStringRef hashStr = SecTrustSettingsCertHashStrFromCert(cert);
bool foundMatch = false;
bool foundAny = false;
CSSM_RETURN *errors = NULL;
uint32 errorCount = 0;
OSStatus status = 0;
SecTrustSettingsDomain foundDomain = kSecTrustSettingsDomainUser;
SecTrustSettingsResult foundResult = kSecTrustSettingsResultInvalid;
bool isSelfSigned = false;
if ((count - 1) == idx) {
Boolean selfSigned;
status = SecCertificateIsSelfSigned(cert, &selfSigned);
isSelfSigned = (status) ? false : ((selfSigned) ? true : false);
if (isSelfSigned) {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT;
}
}
status = SecTrustSettingsEvaluateCert(
hashStr,
NULL,
NULL,
0,
0,
isSelfSigned,
&foundDomain,
&errors,
&errorCount,
&foundResult,
&foundMatch,
&foundAny);
if (status == errSecSuccess) {
if (foundMatch) {
switch (foundResult) {
case kSecTrustSettingsResultTrustRoot:
case kSecTrustSettingsResultTrustAsRoot:
evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST;
break;
case kSecTrustSettingsResultDeny:
evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY;
break;
case kSecTrustSettingsResultUnspecified:
case kSecTrustSettingsResultInvalid:
default:
break;
}
}
}
if (errors) {
free(errors);
}
if (hashStr) {
CFRelease(hashStr);
}
unsigned int numCodes=0;
CSSM_RETURN *statusCodes = copyCssmStatusCodes(trust, (unsigned int)idx, &numCodes);
if (statusCodes) {
unsigned int totalStatusCodes = numStatusCodes + numCodes + 1;
statusArray = (CSSM_RETURN *)realloc(statusArray, totalStatusCodes * sizeof(CSSM_RETURN));
evInfo->StatusCodes = &statusArray[numStatusCodes];
evInfo->NumStatusCodes = numCodes;
for (unsigned int cpix = 0; cpix <= numCodes; cpix++) {
evInfo->StatusCodes[cpix] = statusCodes[cpix];
}
numStatusCodes = totalStatusCodes;
free(statusCodes);
}
if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST |
CSSM_CERT_STATUS_TRUST_SETTINGS_DENY |
CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) {
uint32 whichDomain = 0;
switch(foundDomain) {
case kSecTrustSettingsDomainUser:
whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER;
break;
case kSecTrustSettingsDomainAdmin:
whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN;
break;
case kSecTrustSettingsDomainSystem:
whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM;
break;
}
evInfo->StatusBits |= whichDomain;
}
}
numStatusCodes = 0;
for (idx=0; idx < count; idx++) {
CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[idx];
evInfo->StatusCodes = &statusArray[numStatusCodes];
numStatusCodes += evInfo->NumStatusCodes + 1;
}
secTrust->_legacy_info_array = infoArray;
secTrust->_legacy_status_array = statusArray;
if (inputCertArray) {
CFRelease(inputCertArray);
}
if (inputAnchorArray) {
CFRelease(inputAnchorArray);
}
return (CSSM_TP_APPLE_EVIDENCE_INFO *)secTrust->_legacy_info_array;
}
CFArrayRef SecTrustCopyProperties(SecTrustRef trust) {
CFIndex ix, count = SecTrustGetCertificateCount(trust);
CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, count,
&kCFTypeArrayCallBacks);
for (ix = 0; ix < count; ix++) {
CFMutableDictionaryRef certDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, ix);
if (cert) {
CFStringRef subjectSummary = SecCertificateCopySubjectSummary(cert);
if (subjectSummary) {
CFDictionaryAddValue(certDict, kSecPropertyTypeTitle, subjectSummary);
CFRelease(subjectSummary);
}
}
unsigned int numStatusCodes;
CSSM_RETURN *statusCodes = NULL;
statusCodes = copyCssmStatusCodes(trust, (uint32_t)ix, &numStatusCodes);
if (statusCodes) {
int32_t reason = statusCodes[numStatusCodes]; if (reason > 0) {
CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
if (cfreason) {
CFDictionarySetValue(certDict, kSecTrustRevocationReason, cfreason);
CFRelease(cfreason);
}
}
free(statusCodes);
}
if (ix == 0) {
OSStatus error = errSecSuccess;
(void)SecTrustGetCssmResultCode(trust, &error);
CFStringRef errorStr = SecCopyErrorMessageString(error, NULL);
if (errorStr) {
CFDictionarySetValue(certDict, kSecPropertyTypeError, errorStr);
CFRelease(errorStr);
}
}
CFArrayAppendValue(properties, certDict);
CFRelease(certDict);
}
return properties;
}
OSStatus SecTrustGetCSSMAnchorCertificates(const CSSM_DATA **cssmAnchors,
uint32 *cssmAnchorCount)
{
#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;
}
OSStatus SecTrustGetUserTrust(SecCertificateRef certificate,
SecPolicyRef policy, SecTrustUserSetting *trustSetting)
{
#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;
}
OSStatus SecTrustSetUserTrust(SecCertificateRef certificate,
SecPolicyRef policy, SecTrustUserSetting trustSetting)
{
#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;
}
OSStatus SecTrustSetUserTrustLegacy(SecCertificateRef certificate,
SecPolicyRef policy, SecTrustUserSetting trustSetting)
{
#if SECTRUST_DEPRECATION_WARNINGS
syslog(LOG_ERR, "WARNING: SecTrustSetUserTrustLegacy does nothing in 10.11. Please stop using it.");
#endif
return errSecServiceNotAvailable;
}