TrustAdditions.cpp [plain text]
#include "TrustAdditions.h"
#include "TrustKeychains.h"
#include "SecBridge.h"
#include <security_keychain/SecCFTypes.h>
#include <security_keychain/Globals.h>
#include <security_keychain/Certificate.h>
#include <security_keychain/Item.h>
#include <security_keychain/KCCursor.h>
#include <security_keychain/KCUtilities.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/unistd.h>
#include <string>
#include <AvailabilityMacros.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CommonCrypto/CommonDigest.h>
#include <Security/Security.h>
#include <Security/cssmtype.h>
#include <Security/cssmapplePriv.h> // for CSSM_APPLE_TP_OCSP_OPTIONS, CSSM_APPLE_TP_OCSP_OPT_FLAGS
#include "SecTrustPriv.h"
#include "SecTrustSettings.h"
#include "SecTrustSettingsPriv.h"
#define BEGIN_SECAPI_INTERNAL_CALL \
try {
#define END_SECAPI_INTERNAL_CALL \
} \
catch (const MacOSError &err) { status=err.osStatus(); } \
catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); } \
catch (const std::bad_alloc &) { status=memFullErr; } \
catch (...) { status=internalComponentErr; }
static const char *EV_ROOTS_PLIST_SYSTEM_PATH = "/System/Library/Keychains/EVRoots.plist";
static const char *SYSTEM_ROOTS_PLIST_SYSTEM_PATH = "/System/Library/Keychains/SystemRootCertificates.keychain";
static const char *X509ANCHORS_SYSTEM_PATH = "/System/Library/Keychains/X509Anchors";
static CFArrayRef _allowedRootCertificatesForOidString(CFStringRef oidString);
static CSSM_DATA_PTR _copyFieldDataForOid(CSSM_OID_PTR oid, CSSM_DATA_PTR cert, CSSM_CL_HANDLE clHandle);
static CFStringRef _decimalStringForOid(CSSM_OID_PTR oid);
static CFDictionaryRef _evCAOidDict();
static void _freeFieldData(CSSM_DATA_PTR value, CSSM_OID_PTR oid, CSSM_CL_HANDLE clHandle);
static CFStringRef _oidStringForCertificatePolicies(const CE_CertPolicies *certPolicies);
static SecCertificateRef _rootCertificateWithSubjectOfCertificate(SecCertificateRef certificate);
static SecCertificateRef _rootCertificateWithSubjectKeyIDOfCertificate(SecCertificateRef certificate);
static void SafeCFRelease(void *cfTypeRefPtr)
{
CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr;
if (obj && *obj) {
CFRelease(*obj);
*obj = NULL;
}
}
static CFDataRef dataWithContentsOfFile(const char *fileName)
{
int rtn;
int fd;
struct stat sb;
off_t fileSize;
UInt8 *fileData = NULL;
CFDataRef outCFData = NULL;
fd = open(fileName, O_RDONLY, 0);
if(fd < 0)
return NULL;
rtn = fstat(fd, &sb);
if(rtn)
goto errOut;
fileSize = sb.st_size;
fileData = (UInt8 *) malloc(fileSize);
if(fileData == NULL)
goto errOut;
rtn = lseek(fd, 0, SEEK_SET);
if(rtn < 0)
goto errOut;
rtn = read(fd, fileData, fileSize);
if(rtn != (int)fileSize) {
rtn = EIO;
} else {
rtn = 0;
outCFData = CFDataCreate(NULL, fileData, fileSize);
}
errOut:
close(fd);
if (fileData) {
free(fileData);
}
return outCFData;
}
static SecKeychainRef systemRootStore()
{
SecKeychainStatus keychainStatus = 0;
SecKeychainRef systemRoots = NULL;
OSStatus status = noErr;
BEGIN_SECAPI_INTERNAL_CALL
systemRoots=globals().storageManager.make(SYSTEM_ROOTS_PLIST_SYSTEM_PATH, false)->handle();
END_SECAPI_INTERNAL_CALL
if (!status && systemRoots) {
BEGIN_SECAPI_INTERNAL_CALL
keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status();
END_SECAPI_INTERNAL_CALL
}
if (status || !systemRoots) {
SafeCFRelease(&systemRoots);
BEGIN_SECAPI_INTERNAL_CALL
systemRoots=globals().storageManager.make(X509ANCHORS_SYSTEM_PATH, false)->handle();
END_SECAPI_INTERNAL_CALL
if (!status && systemRoots) {
BEGIN_SECAPI_INTERNAL_CALL
keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status();
END_SECAPI_INTERNAL_CALL
}
}
if (status || !systemRoots) {
SafeCFRelease(&systemRoots);
return NULL;
}
return systemRoots;
}
static CFDictionaryRef dictionaryWithContentsOfPlistFile(const char *fileName)
{
CFDictionaryRef resultDict = NULL;
CFDataRef fileData = dataWithContentsOfFile(fileName);
if (fileData) {
CFPropertyListRef xmlPlist = CFPropertyListCreateFromXMLData(NULL, fileData, kCFPropertyListImmutable, NULL);
if (xmlPlist && CFGetTypeID(xmlPlist) == CFDictionaryGetTypeID()) {
resultDict = (CFDictionaryRef)xmlPlist;
} else {
SafeCFRelease(&xmlPlist);
}
SafeCFRelease(&fileData);
}
return resultDict;
}
static CFStringRef organizationNameForCertificate(SecCertificateRef certificate)
{
CFStringRef organizationName = nil;
OSStatus status = noErr;
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_OrganizationName;
BEGIN_SECAPI_INTERNAL_CALL
organizationName = Certificate::required(certificate)->distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, oidPtr);
END_SECAPI_INTERNAL_CALL
if (status) {
return (CFStringRef)NULL;
}
#else
CSSM_DATA_PTR *fieldValues = NULL;
BEGIN_SECAPI_INTERNAL_CALL
fieldValues = Certificate::required(certificate)->copyFieldValues(&CSSMOID_X509V1SubjectNameCStruct);
END_SECAPI_INTERNAL_CALL
if (*fieldValues == NULL) {
return (CFStringRef)NULL;
}
if (status || (*fieldValues)->Length == 0 || (*fieldValues)->Data == NULL) {
BEGIN_SECAPI_INTERNAL_CALL
Certificate::required(certificate)->releaseFieldValues(&CSSMOID_X509V1SubjectNameCStruct, fieldValues);
END_SECAPI_INTERNAL_CALL
return (CFStringRef)NULL;
}
CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)(*fieldValues)->Data;
unsigned rdnIndex = 0;
bool foundIt = FALSE;
for (rdnIndex = 0; rdnIndex < x509Name->numberOfRDNs; rdnIndex++) {
CSSM_X509_RDN *rdnPtr = x509Name->RelativeDistinguishedName + rdnIndex;
unsigned pairIndex;
for (pairIndex = 0; pairIndex < rdnPtr->numberOfPairs; pairIndex++) {
CSSM_X509_TYPE_VALUE_PAIR *pair = rdnPtr->AttributeTypeAndValue + pairIndex;
if (!oidsAreEqual(&pair->type, &CSSMOID_OrganizationName))
continue;
switch (pair->valueType) {
case BER_TAG_PKIX_UTF8_STRING:
case BER_TAG_PKIX_UNIVERSAL_STRING:
case BER_TAG_GENERAL_STRING:
organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUTF8, FALSE);
break;
case BER_TAG_PRINTABLE_STRING:
case BER_TAG_IA5_STRING:
organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingASCII, FALSE);
break;
case BER_TAG_T61_STRING:
case BER_TAG_VIDEOTEX_STRING:
case BER_TAG_ISO646_STRING:
organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUTF8, FALSE);
if (!organizationName) {
organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingISOLatin1, FALSE);
}
break;
case BER_TAG_PKIX_BMP_STRING:
organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUnicode, FALSE);
break;
default:
break;
}
if (organizationName) {
foundIt = TRUE;
break;
}
}
if (foundIt)
break;
}
BEGIN_SECAPI_INTERNAL_CALL
Certificate::required(certificate)->releaseFieldValues(&CSSMOID_X509V1SubjectNameCStruct, fieldValues);
END_SECAPI_INTERNAL_CALL
#endif
return organizationName;
}
#if !defined(NDEBUG)
void showCertSKID(const void *value, void *context);
#endif
static ModuleNexus<Mutex> gPotentialEVChainWithCertificatesMutex;
CFArrayRef potentialEVChainWithCertificates(CFArrayRef certificates)
{
StLock<Mutex> _(gPotentialEVChainWithCertificatesMutex());
CFIndex chainIndex, chainLen = (certificates) ? CFArrayGetCount(certificates) : 0;
secdebug("trusteval", "potentialEVChainWithCertificates: chainLen: %ld", chainLen);
if (chainLen < 2) {
if (certificates) {
CFRetain(certificates);
}
return certificates;
}
CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (chainIndex = 0; chainIndex < chainLen; chainIndex++) {
SecCertificateRef aCert = (SecCertificateRef) CFArrayGetValueAtIndex(certificates, chainIndex);
SecCertificateRef replacementCert = NULL;
secdebug("trusteval", "potentialEVChainWithCertificates: examining chainIndex: %ld", chainIndex);
if (chainIndex > 0) {
replacementCert = _rootCertificateWithSubjectKeyIDOfCertificate(aCert);
if (!replacementCert)
{
secdebug("trusteval", " not found using SKID, try by subject");
replacementCert = _rootCertificateWithSubjectOfCertificate(aCert);
}
}
if (!replacementCert) {
secdebug("trusteval", " No replacement found using SKID or subject; keeping original intermediate");
CFArrayAppendValue(certArray, aCert);
}
SafeCFRelease(&replacementCert);
}
secdebug("trusteval", "potentialEVChainWithCertificates: exit: new chainLen: %ld", CFArrayGetCount(certArray));
#if !defined(NDEBUG)
CFArrayApplyFunction(certArray, CFRangeMake(0, CFArrayGetCount(certArray)), showCertSKID, NULL);
#endif
return certArray;
}
static SecCertificateRef _rootCertificateWithSubjectOfCertificate(SecCertificateRef certificate)
{
if (!certificate)
return NULL;
StLock<Mutex> _(SecTrustKeychainsGetMutex());
CSSM_CL_HANDLE clHandle = 0;
CSSM_DATA certData = { 0, NULL };
OSStatus status = noErr;
BEGIN_SECAPI_INTERNAL_CALL
clHandle = Certificate::required(certificate)->clHandle();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
BEGIN_SECAPI_INTERNAL_CALL
certData = Certificate::required(certificate)->data();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
SecKeychainRef systemRoots = systemRootStore();
if (!systemRoots)
return NULL;
const CSSM_OID_PTR oidPtr = (const CSSM_OID_PTR) &CSSMOID_X509V1SubjectName;
const CSSM_DATA_PTR subjectDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
if (!subjectDataPtr)
return NULL;
SecKeyRef keyRef = NULL;
SecCertificateRef resultCert = NULL;
BEGIN_SECAPI_INTERNAL_CALL
keyRef = Certificate::required(certificate)->publicKey()->handle();
END_SECAPI_INTERNAL_CALL
if (!status) {
const CSSM_KEY *cssmKey = NULL;
BEGIN_SECAPI_INTERNAL_CALL
cssmKey = KeyItem::required(keyRef)->key();
END_SECAPI_INTERNAL_CALL
if (!status) {
uint8 buf[CC_SHA1_DIGEST_LENGTH];
CSSM_DATA digest = { sizeof(buf), buf };
if (!cssmKey || !cssmKey->KeyData.Data || !cssmKey->KeyData.Length) {
status = paramErr;
} else {
CC_SHA1(cssmKey->KeyData.Data, cssmKey->KeyData.Length, buf);
}
if (!status) {
SecKeychainAttribute attrs[] = {
{ kSecPublicKeyHashItemAttr, digest.Length, (void *)digest.Data },
{ kSecSubjectItemAttr, subjectDataPtr->Length, (void *)subjectDataPtr->Data },
{ kSecIssuerItemAttr, subjectDataPtr->Length, (void *)subjectDataPtr->Data }
};
const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
SecKeychainSearchRef searchRef = NULL;
BEGIN_SECAPI_INTERNAL_CALL
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(systemRoots, keychains);
KCCursor cursor(keychains, kSecCertificateItemClass, &attributes);
searchRef = cursor->handle();
END_SECAPI_INTERNAL_CALL
if (!status && searchRef) {
SecKeychainItemRef certRef = nil;
BEGIN_SECAPI_INTERNAL_CALL
Item item;
if (!KCCursorImpl::required(searchRef)->next(item)) {
status=errSecItemNotFound;
} else {
certRef=item->handle();
}
END_SECAPI_INTERNAL_CALL
if (!status)
resultCert = (SecCertificateRef)certRef; SafeCFRelease(&searchRef);
}
}
}
}
_freeFieldData(subjectDataPtr, oidPtr, clHandle);
SafeCFRelease(&keyRef);
SafeCFRelease(&systemRoots);
return resultCert;
}
#if !defined(NDEBUG)
static void logSKID(const char *msg, const CssmData &subjectKeyID)
{
const unsigned char *px = (const unsigned char *)subjectKeyID.data();
char buffer[256]={0,};
char bytes[16];
if (px && msg)
{
strcpy(buffer, msg);
for (unsigned int ix=0; ix<20; ix++)
{
sprintf(bytes, "%02X", px[ix]);
strcat(buffer, bytes);
}
secdebug("trusteval", " SKID: %s",buffer);
}
}
void showCertSKID(const void *value, void *context)
{
SecCertificateRef certificate = (SecCertificateRef)value;
OSStatus status = noErr;
BEGIN_SECAPI_INTERNAL_CALL
const CssmData &subjectKeyID = Certificate::required(certificate)->subjectKeyIdentifier();
logSKID("subjectKeyID: ", subjectKeyID);
END_SECAPI_INTERNAL_CALL
}
#endif
static SecCertificateRef _rootCertificateWithSubjectKeyIDOfCertificate(SecCertificateRef certificate)
{
SecCertificateRef resultCert = NULL;
OSStatus status = noErr;
if (!certificate)
return NULL;
StLock<Mutex> _(SecTrustKeychainsGetMutex());
SecKeychainRef systemRoots = systemRootStore();
if (!systemRoots)
return NULL;
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(systemRoots, keychains);
BEGIN_SECAPI_INTERNAL_CALL
const CssmData &subjectKeyID = Certificate::required(certificate)->subjectKeyIdentifier();
#if !defined(NDEBUG)
logSKID("search for SKID: ", subjectKeyID);
#endif
resultCert = Certificate::required(certificate)->findBySubjectKeyID(keychains, subjectKeyID)->handle();
#if !defined(NDEBUG)
logSKID(" found SKID: ", subjectKeyID);
#endif
END_SECAPI_INTERNAL_CALL
SafeCFRelease(&systemRoots);
return resultCert;
}
CFArrayRef _possibleRootCertificatesForOidString(CFStringRef oidString)
{
StLock<Mutex> _(SecTrustKeychainsGetMutex());
if (!oidString)
return NULL;
CFDictionaryRef evOidDict = _evCAOidDict();
if (!evOidDict)
return NULL;
CFArrayRef possibleCertificateHashes = (CFArrayRef) CFDictionaryGetValue(evOidDict, oidString);
SecKeychainRef systemRoots = systemRootStore();
if (!possibleCertificateHashes || !systemRoots) {
SafeCFRelease(&evOidDict);
return NULL;
}
CFMutableArrayRef possibleRootCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFIndex hashCount = CFArrayGetCount(possibleCertificateHashes);
secdebug("evTrust", "_possibleRootCertificatesForOidString: %d possible hashes", (int)hashCount);
OSStatus status = noErr;
SecKeychainSearchRef searchRef = NULL;
BEGIN_SECAPI_INTERNAL_CALL
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(systemRoots, keychains);
KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
searchRef = cursor->handle();
END_SECAPI_INTERNAL_CALL
if (searchRef) {
while (!status) {
SecKeychainItemRef certRef = NULL;
BEGIN_SECAPI_INTERNAL_CALL
Item item;
if (!KCCursorImpl::required(searchRef)->next(item)) {
certRef=NULL;
status=errSecItemNotFound;
} else {
certRef=item->handle();
}
END_SECAPI_INTERNAL_CALL
if (status || !certRef) {
break;
}
CSSM_DATA certData = { 0, NULL };
BEGIN_SECAPI_INTERNAL_CALL
certData = Certificate::required((SecCertificateRef)certRef)->data();
END_SECAPI_INTERNAL_CALL
if (!status) {
uint8 buf[CC_SHA1_DIGEST_LENGTH];
CSSM_DATA digest = { sizeof(buf), buf };
if (!certData.Data || !certData.Length) {
status = paramErr;
} else {
CC_SHA1(certData.Data, certData.Length, buf);
}
if (!status) {
CFDataRef hashData = CFDataCreateWithBytesNoCopy(NULL, digest.Data, digest.Length, kCFAllocatorNull);
if (hashData && CFArrayContainsValue(possibleCertificateHashes, CFRangeMake(0, hashCount), hashData)) {
CFArrayAppendValue(possibleRootCertificates, certRef);
}
SafeCFRelease(&hashData);
}
}
SafeCFRelease(&certRef);
}
}
SafeCFRelease(&searchRef);
SafeCFRelease(&systemRoots);
SafeCFRelease(&evOidDict);
return possibleRootCertificates;
}
CFArrayRef _allowedRootCertificatesForOidString(CFStringRef oidString)
{
CFMutableArrayRef allowedRootCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayRef possibleRootCertificates = _possibleRootCertificatesForOidString(oidString);
if (possibleRootCertificates) {
CFIndex idx, count = CFArrayGetCount(possibleRootCertificates);
for (idx=0; idx<count; idx++) {
SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(possibleRootCertificates, idx);
CFStringRef hashStr = SecTrustSettingsCertHashStrFromCert(cert);
if (hashStr) {
bool foundMatch = false;
bool foundAny = false;
CSSM_RETURN *errors = NULL;
uint32 errorCount = 0;
SecTrustSettingsDomain foundDomain = 0;
SecTrustSettingsResult result = kSecTrustSettingsResultInvalid;
OSStatus status = SecTrustSettingsEvaluateCert(
hashStr,
NULL,
NULL,
0,
0,
true,
&foundDomain,
&errors,
&errorCount,
&result,
&foundMatch,
&foundAny);
if (status == noErr) {
secdebug("evTrust", "_allowedRootCertificatesForOidString: cert %lu has result %d from domain %d",
idx, (int)result, (int)foundDomain);
if (foundMatch && foundDomain == kSecTrustSettingsDomainSystem &&
result == kSecTrustSettingsResultTrustRoot) {
CFArrayAppendValue(allowedRootCertificates, cert);
}
} else {
secdebug("evTrust", "_allowedRootCertificatesForOidString: cert %lu SecTrustSettingsEvaluateCert error %d",
idx, (int)status);
}
if (errors) {
free(errors);
}
CFRelease(hashStr);
}
}
CFRelease(possibleRootCertificates);
}
return allowedRootCertificates;
}
static CSSM_DATA_PTR _copyFieldDataForOid(CSSM_OID_PTR oid, CSSM_DATA_PTR cert, CSSM_CL_HANDLE clHandle)
{
uint32 numFields = 0;
CSSM_HANDLE results = 0;
CSSM_DATA_PTR value = 0;
CSSM_RETURN crtn = CSSM_CL_CertGetFirstFieldValue(clHandle, cert, oid, &results, &numFields, &value);
if (results) {
CSSM_CL_CertAbortQuery(clHandle, results);
}
return (crtn || !numFields) ? NULL : value;
}
bool isRevocationServerMetaError(CSSM_RETURN statusCode)
{
switch (statusCode) {
case CSSMERR_APPLETP_CRL_NOT_FOUND: case CSSMERR_APPLETP_CRL_SERVER_DOWN: case CSSMERR_APPLETP_OCSP_UNAVAILABLE: case CSSMERR_APPLETP_NETWORK_FAILURE: case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ: case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR: case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER: case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED: case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED: return true;
default:
return false;
}
}
bool isOCSPStatusCode(CSSM_RETURN statusCode)
{
switch (statusCode)
{
case CSSMERR_APPLETP_OCSP_BAD_RESPONSE: case CSSMERR_APPLETP_OCSP_BAD_REQUEST: case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ: case CSSMERR_APPLETP_OCSP_UNAVAILABLE: case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED: case CSSMERR_APPLETP_OCSP_NOT_TRUSTED: case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT: case CSSMERR_APPLETP_OCSP_SIG_ERROR: case CSSMERR_APPLETP_OCSP_NO_SIGNER: case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR: case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER: case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED: case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED: case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH: return true;
default:
return false;
}
}
bool isCRLStatusCode(CSSM_RETURN statusCode)
{
switch (statusCode)
{
case CSSMERR_APPLETP_CRL_EXPIRED: case CSSMERR_APPLETP_CRL_NOT_VALID_YET: case CSSMERR_APPLETP_CRL_NOT_FOUND: case CSSMERR_APPLETP_CRL_SERVER_DOWN: case CSSMERR_APPLETP_CRL_BAD_URI: case CSSMERR_APPLETP_CRL_NOT_TRUSTED: case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT: case CSSMERR_APPLETP_CRL_POLICY_FAIL: return true;
default:
return false;
}
}
bool isRevocationStatusCode(CSSM_RETURN statusCode)
{
if (statusCode == CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK || statusCode == CSSMERR_APPLETP_NETWORK_FAILURE || isOCSPStatusCode(statusCode) == true || isCRLStatusCode(statusCode) == true) return true;
else
return false;
}
bool ignorableRevocationStatusCode(CSSM_RETURN statusCode)
{
if (!isRevocationStatusCode(statusCode))
return false;
CFStringRef ocsp_val = (CFStringRef) CFPreferencesCopyValue(kSecRevocationOcspStyle, CFSTR(kSecRevocationDomain), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
CFStringRef crl_val = (CFStringRef) CFPreferencesCopyValue(kSecRevocationCrlStyle, CFSTR(kSecRevocationDomain), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
bool ocspRequired = (ocsp_val && CFEqual(ocsp_val, kSecRevocationRequireForAll));
bool crlRequired = (crl_val && CFEqual(crl_val, kSecRevocationRequireForAll));
if (!ocspRequired && ocsp_val && CFEqual(ocsp_val, kSecRevocationRequireIfPresent))
ocspRequired = (statusCode != CSSMERR_APPLETP_OCSP_UNAVAILABLE);
if (!crlRequired && crl_val && CFEqual(crl_val, kSecRevocationRequireIfPresent))
crlRequired = (statusCode != CSSMERR_APPLETP_CRL_NOT_FOUND);
if (ocsp_val)
CFRelease(ocsp_val);
if (crl_val)
CFRelease(crl_val);
if (isOCSPStatusCode(statusCode))
return (ocspRequired) ? false : true;
if (isCRLStatusCode(statusCode))
return (crlRequired) ? false : true;
return false;
}
CFArrayRef allowedEVRootsForLeafCertificate(CFArrayRef certificates)
{
CFIndex count = (certificates) ? CFArrayGetCount(certificates) : 0;
if (count < 1)
return NULL;
CSSM_CL_HANDLE clHandle = 0;
CSSM_DATA certData = { 0, NULL };
SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(certificates, 0);
OSStatus status = noErr;
BEGIN_SECAPI_INTERNAL_CALL
clHandle = Certificate::required(certRef)->clHandle();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
BEGIN_SECAPI_INTERNAL_CALL
certData = Certificate::required(certRef)->data();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
const CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_CertificatePolicies;
CSSM_DATA_PTR extensionDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
if (!extensionDataPtr)
return NULL;
CSSM_X509_EXTENSION *cssmExtension = (CSSM_X509_EXTENSION *)extensionDataPtr->Data;
CE_CertPolicies *certPolicies = (CE_CertPolicies *)cssmExtension->value.parsedValue;
CFStringRef oidString = _oidStringForCertificatePolicies(certPolicies);
_freeFieldData(extensionDataPtr, oidPtr, clHandle);
CFArrayRef allowedRoots = (oidString) ? _allowedRootCertificatesForOidString(oidString) : NULL;
CFIndex rootCount = (allowedRoots) ? CFArrayGetCount(allowedRoots) : 0;
secdebug("evTrust", "allowedEVRootsForLeafCertificate: found %d allowed roots", (int)rootCount);
SafeCFRelease(&oidString);
if (!allowedRoots || !rootCount) {
SafeCFRelease(&allowedRoots);
return NULL;
}
return allowedRoots;
}
CFDictionaryRef extendedValidationResults(CFArrayRef certChain, SecTrustResultType trustResult, OSStatus tpResult)
{
CFIndex chainIndex, chainLen = (certChain) ? CFArrayGetCount(certChain) : 0;
if (chainLen < 2) {
return NULL; }
if (trustResult != kSecTrustResultUnspecified) {
bool recovered = false;
if (trustResult == kSecTrustResultRecoverableTrustFailure) {
recovered = isRevocationServerMetaError((CSSM_RETURN)tpResult);
}
if (!recovered) {
return NULL;
}
}
bool hasRequiredExtensions = true;
CSSM_CL_HANDLE clHandle = 0;
CSSM_DATA certData = { 0, NULL };
CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_CertificatePolicies;
for (chainIndex = 1; hasRequiredExtensions && chainLen > 2 && chainIndex < chainLen - 1; chainIndex++) {
SecCertificateRef intermediateCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, chainIndex);
OSStatus status = noErr;
BEGIN_SECAPI_INTERNAL_CALL
clHandle = Certificate::required(intermediateCert)->clHandle();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
BEGIN_SECAPI_INTERNAL_CALL
certData = Certificate::required(intermediateCert)->data();
END_SECAPI_INTERNAL_CALL
if (status)
return NULL;
CSSM_DATA_PTR extensionDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
if (!extensionDataPtr)
return NULL;
CSSM_X509_EXTENSION *cssmExtension = (CSSM_X509_EXTENSION *)extensionDataPtr->Data;
CE_CertPolicies *certPolicies = (CE_CertPolicies *)cssmExtension->value.parsedValue;
CFStringRef oidString = _oidStringForCertificatePolicies(certPolicies);
hasRequiredExtensions = (oidString != NULL);
SafeCFRelease(&oidString);
_freeFieldData(extensionDataPtr, oidPtr, clHandle);
}
if (hasRequiredExtensions) {
SecCertificateRef leafCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, 0);
CFStringRef organizationName = organizationNameForCertificate(leafCert);
if (organizationName != NULL) {
CFMutableDictionaryRef resultDict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(resultDict, kSecEVOrganizationName, organizationName);
SafeCFRelease(&organizationName);
return resultDict;
}
}
return NULL;
}
CFDictionaryRef extendedTrustResults(CFArrayRef certChain, SecTrustResultType trustResult, OSStatus tpResult, bool isEVCandidate)
{
CFMutableDictionaryRef resultDict = NULL;
if (isEVCandidate) {
resultDict = (CFMutableDictionaryRef) extendedValidationResults(certChain, trustResult, tpResult);
}
if (!resultDict) {
resultDict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!resultDict) {
return NULL;
}
}
CFAbsoluteTime at = CFAbsoluteTimeGetCurrent();
CFDateRef trustEvaluationDate = CFDateCreate(kCFAllocatorDefault, at);
CFDateRef trustExpirationDate = CFDateCreate(kCFAllocatorDefault, at + (60*60*2));
CFDictionaryAddValue(resultDict, kSecTrustEvaluationDate, trustEvaluationDate);
SafeCFRelease(&trustEvaluationDate);
CFDictionaryAddValue(resultDict, kSecTrustExpirationDate, trustExpirationDate);
SafeCFRelease(&trustExpirationDate);
return resultDict;
}
static CFDictionaryRef _evCAOidDict()
{
static CFDictionaryRef s_evCAOidDict = NULL;
if (s_evCAOidDict) {
CFRetain(s_evCAOidDict);
secdebug("evTrust", "_evCAOidDict: returning static instance (rc=%d)", (int)CFGetRetainCount(s_evCAOidDict));
return s_evCAOidDict;
}
secdebug("evTrust", "_evCAOidDict: initializing static instance");
s_evCAOidDict = dictionaryWithContentsOfPlistFile(EV_ROOTS_PLIST_SYSTEM_PATH);
if (!s_evCAOidDict)
return NULL;
#if !defined MAC_OS_X_VERSION_10_6 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
CFStringRef oidString = CFSTR("2.16.840.1.114028.10.1.2");
CFMutableArrayRef hashes = (CFMutableArrayRef) CFDictionaryGetValue(s_evCAOidDict, oidString);
if (hashes) {
uint8 hashBytes[] = {0xB3, 0x1E, 0xB1, 0xB7, 0x40, 0xE3, 0x6C, 0x84, 0x02, 0xDA, 0xDC, 0x37, 0xD4, 0x4D, 0xF5, 0xD4, 0x67, 0x49, 0x52, 0xF9};
CFDataRef hashData = CFDataCreate(NULL, hashBytes, sizeof(hashBytes));
CFIndex hashCount = CFArrayGetCount(hashes);
if (hashData && CFArrayContainsValue(hashes, CFRangeMake(0, hashCount), hashData)) {
secdebug("evTrust", "_evCAOidDict: added hardcoded hash value");
CFArrayAppendValue(hashes, hashData);
}
SafeCFRelease(&hashData);
}
#endif
CFRetain(s_evCAOidDict);
secdebug("evTrust", "_evCAOidDict: returning static instance (rc=%d)", (int)CFGetRetainCount(s_evCAOidDict));
return s_evCAOidDict;
}
static CFStringRef _decimalStringForOid(CSSM_OID_PTR oid)
{
CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
if (!str || oid->Length > 32)
return str;
unsigned long value = 0;
unsigned int x = oid->Data[0] / 40;
unsigned int y = oid->Data[0] % 40;
if (x > 2) {
y += (x - 2) * 40;
x = 2;
}
CFStringAppendFormat(str, NULL, CFSTR("%d.%d"), x, y);
for (x = 1; x < oid->Length; x++) {
value = (value << 7) | (oid->Data[x] & 0x7F);
if(!(oid->Data[x] & 0x80)) {
CFStringAppendFormat(str, NULL, CFSTR(".%ld"), value);
value = 0;
}
}
#if !defined(NDEBUG)
CFIndex nameLen = CFStringGetLength(str);
CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
char *nameBuf = (char *)malloc(bufLen);
if (!CFStringGetCString(str, nameBuf, bufLen-1, kCFStringEncodingUTF8))
nameBuf[0]=0;
secdebug("evTrust", "_decimalStringForOid: \"%s\"", nameBuf);
free(nameBuf);
#endif
return str;
}
static void _freeFieldData(CSSM_DATA_PTR value, CSSM_OID_PTR oid, CSSM_CL_HANDLE clHandle)
{
if (value && value->Data) {
CSSM_CL_FreeFieldValue(clHandle, oid, value);
}
return;
}
static ModuleNexus<Mutex> gOidStringForCertificatePoliciesMutex;
static CFStringRef _oidStringForCertificatePolicies(const CE_CertPolicies *certPolicies)
{
StLock<Mutex> _(gOidStringForCertificatePoliciesMutex());
if (!certPolicies) {
secdebug("evTrust", "oidStringForCertificatePolicies: missing certPolicies!");
return NULL;
}
CFDictionaryRef evOidDict = _evCAOidDict();
if (!evOidDict) {
secdebug("evTrust", "oidStringForCertificatePolicies: nil OID dictionary!");
return NULL;
}
CFStringRef foundOidStr = NULL;
uint32 policyIndex, maxIndex = 10; for (policyIndex = 0; policyIndex < certPolicies->numPolicies && policyIndex < maxIndex; policyIndex++) {
CE_PolicyInformation *certPolicyInfo = &certPolicies->policies[policyIndex];
CSSM_OID_PTR oid = &certPolicyInfo->certPolicyId;
CFStringRef oidStr = _decimalStringForOid(oid);
if (!oidStr)
continue;
if (!CFStringCompare(oidStr, CFSTR("2.5.29.32.0"), 0) || CFDictionaryGetValue(evOidDict, oidStr) != NULL) { foundOidStr = CFStringCreateCopy(NULL, oidStr);
}
SafeCFRelease(&oidStr);
if (foundOidStr)
break;
}
SafeCFRelease(&evOidDict);
return foundOidStr;
}