#include <libDER/oids.h>
#include <Security/oidscert.h>
#include <Security/SecTrust.h>
#include <Security/SecTrustPriv.h>
#include "Trust.h"
#include <Security/SecBase.h>
#include "SecBridge.h"
#include <Security/SecInternal.h>
#include <Security/SecTrustSettings.h>
#include <Security/SecTrustSettingsPriv.h>
#include <Security/SecTrustStatusCodes.h>
#include <Security/SecCertificatePriv.h>
#include <Security/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;
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 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;
}
}
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;
}
OSStatus cssmResultCode = errSecSuccess;
uint8_t resultCodePriority = 0xFF;
CFIndex ix, count = SecTrustGetCertificateCount(trustRef);
for (ix = 0; ix < count; ix++) {
CFIndex numStatusCodes;
CSSM_RETURN *statusCodes = NULL;
statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trustRef, 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
CFArrayRef outArray;
OSStatus status = SecTrustSettingsCopyUnrestrictedRoots(
true, true, true,
&outArray);
if (status != errSecSuccess) {
return status;
}
CFIndex count = outArray ? CFArrayGetCount(outArray) : 0;
if(count == 0) {
return errSecNoTrustSettings;
}
CFIndex i;
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust = NULL;
CFMutableArrayRef trustedCertArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < count ; i++) {
SecTrustResultType result;
SecCertificateRef certificate = (SecCertificateRef) CFArrayGetValueAtIndex(outArray, i);
status = SecTrustCreateWithCertificates(certificate, policy, &trust);
if (status != errSecSuccess) {
CFReleaseSafe(trustedCertArray);
goto out;
}
status = SecTrustEvaluate(trust, &result);
if (status != errSecSuccess) {
CFReleaseSafe(trustedCertArray);
goto out;
}
if (result != kSecTrustResultFatalTrustFailure) {
CFArrayAppendValue(trustedCertArray, certificate);
}
CFReleaseNull(trust);
}
if (CFArrayGetCount(trustedCertArray) == 0) {
status = errSecNoTrustSettings;
CFReleaseSafe(trustedCertArray);
goto out;
}
*anchorCertificates = trustedCertArray;
out:
CFReleaseSafe(outArray);
CFReleaseSafe(policy);
CFReleaseSafe(trust);
return status;
END_SECAPI
}
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust)
{
SecKeyRef pubKey = NULL;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, 0);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
(void) SecCertificateCopyPublicKey(certificate, &pubKey);
#pragma clang diagnostic pop
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;
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;
CFIndex 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);
}
CFIndex numCodes=0;
CSSM_RETURN *statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trust, idx, &numCodes);
if (statusCodes) {
CFIndex totalStatusCodes = numStatusCodes + numCodes + 1;
statusArray = (CSSM_RETURN *)realloc(statusArray, totalStatusCodes * sizeof(CSSM_RETURN));
evInfo->StatusCodes = &statusArray[numStatusCodes];
evInfo->NumStatusCodes = (uint32)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);
}
}
CFIndex numStatusCodes;
CSSM_RETURN *statusCodes = NULL;
statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trust, ix, &numStatusCodes);
if (statusCodes) {
SInt32 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;
}