X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/TrustAdditions.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/TrustAdditions.cpp b/Security/libsecurity_keychain/lib/TrustAdditions.cpp deleted file mode 100644 index 8bfd749a..00000000 --- a/Security/libsecurity_keychain/lib/TrustAdditions.cpp +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * Copyright (c) 2002-2009,2011-2014 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@ - */ - -// -// TrustAdditions.cpp -// -#include "TrustAdditions.h" -#include "TrustKeychains.h" -#include "SecBridge.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for CSSM_APPLE_TP_OCSP_OPTIONS, CSSM_APPLE_TP_OCSP_OPT_FLAGS - -#include "SecTrustPriv.h" -#include "SecTrustSettings.h" -#include "SecTrustSettingsPriv.h" - -// -// Macros -// -#define BEGIN_SECAPI_INTERNAL_CALL \ - try { -#define END_SECAPI_INTERNAL_CALL \ - } /* status is only set on error */ \ - 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; } - -#ifdef NDEBUG -/* this actually compiles to nothing */ -#define trustDebug(args...) secdebug("trust", ## args) -#else -#define trustDebug(args...) printf(args) -#endif - -// -// Static constants -// -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 functions -// -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); - -// utility function to safely release (and clear) the given CFTypeRef variable. -// -static void SafeCFRelease(void *cfTypeRefPtr) -{ - CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr; - if (obj && *obj) { - CFRelease(*obj); - *obj = NULL; - } -} - -// utility function to create a CFDataRef from the contents of the specified file; -// caller must release -// -static CFDataRef dataWithContentsOfFile(const char *fileName) -{ - int rtn; - int fd; - struct stat sb; - size_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 = (size_t)sb.st_size; - fileData = (UInt8 *) malloc(fileSize); - if(fileData == NULL) - goto errOut; - - rtn = (int)lseek(fd, 0, SEEK_SET); - if(rtn < 0) - goto errOut; - - rtn = (int)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; -} - -// returns a SecKeychainRef for the system root certificate store; caller must release -// -static SecKeychainRef systemRootStore() -{ - SecKeychainStatus keychainStatus = 0; - SecKeychainRef systemRoots = NULL; - OSStatus status = errSecSuccess; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainOpen(SYSTEM_ROOTS_PLIST_SYSTEM_PATH, &systemRoots); - BEGIN_SECAPI_INTERNAL_CALL - systemRoots=globals().storageManager.make(SYSTEM_ROOTS_PLIST_SYSTEM_PATH, false)->handle(); - END_SECAPI_INTERNAL_CALL - - // SecKeychainOpen will return errSecSuccess even if the file didn't exist on disk. - // We need to do a further check using SecKeychainGetStatus(). - if (!status && systemRoots) { - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainGetStatus(systemRoots, &keychainStatus); - BEGIN_SECAPI_INTERNAL_CALL - keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status(); - END_SECAPI_INTERNAL_CALL - } - if (status || !systemRoots) { - // SystemRootCertificates.keychain can't be opened; look in X509Anchors instead. - SafeCFRelease(&systemRoots); - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainOpen(X509ANCHORS_SYSTEM_PATH, &systemRoots); - BEGIN_SECAPI_INTERNAL_CALL - systemRoots=globals().storageManager.make(X509ANCHORS_SYSTEM_PATH, false)->handle(); - END_SECAPI_INTERNAL_CALL - // SecKeychainOpen will return errSecSuccess even if the file didn't exist on disk. - // We need to do a further check using SecKeychainGetStatus(). - if (!status && systemRoots) { - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainGetStatus(systemRoots, &keychainStatus); - BEGIN_SECAPI_INTERNAL_CALL - keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status(); - END_SECAPI_INTERNAL_CALL - } - } - if (status || !systemRoots) { - // Cannot get root certificates if there is no trusted system root certificate store. - SafeCFRelease(&systemRoots); - return NULL; - } - return systemRoots; -} - -// returns a CFDictionaryRef created from the specified XML plist file; caller must release -// -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; -} - -// returns the Organization component of the given certificate's subject name, -// or nil if that component could not be found. Caller must release the string. -// -static CFStringRef organizationNameForCertificate(SecCertificateRef certificate) -{ - CFStringRef organizationName = nil; - OSStatus status = errSecSuccess; - -#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 - CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_OrganizationName; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateCopySubjectComponent(certificate, oidPtr, &organizationName); - BEGIN_SECAPI_INTERNAL_CALL - organizationName = Certificate::required(certificate)->distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, oidPtr); - END_SECAPI_INTERNAL_CALL - if (status) { - return (CFStringRef)NULL; - } -#else - // SecCertificateCopySubjectComponent() doesn't exist on Tiger, so we have - // to go get the CSSMOID_OrganizationName the hard way, ourselves. - CSSM_DATA_PTR *fieldValues = NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateCopyFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, &fieldValues); - 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) { - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateReleaseFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, fieldValues); - 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; - - // Iterate over all the relative distinguished name (RDN) entries... - unsigned rdnIndex = 0; - bool foundIt = FALSE; - for (rdnIndex = 0; rdnIndex < x509Name->numberOfRDNs; rdnIndex++) { - CSSM_X509_RDN *rdnPtr = x509Name->RelativeDistinguishedName + rdnIndex; - - // And then iterate over the attribute-value pairs of each RDN, looking for a CSSMOID_OrganizationName. - unsigned pairIndex; - for (pairIndex = 0; pairIndex < rdnPtr->numberOfPairs; pairIndex++) { - CSSM_X509_TYPE_VALUE_PAIR *pair = rdnPtr->AttributeTypeAndValue + pairIndex; - - // If this pair isn't the organization name, move on to check the next one. - if (!oidsAreEqual(&pair->type, &CSSMOID_OrganizationName)) - continue; - - // We've found the organization name. Convert value to a string (eg, "Apple Inc.") - // Note: there can be more than one organization name in any given CSSM_X509_RDN. - // In practice, it's OK to use the first one. In future, if we have a means for - // displaying more than one name, this would be where they should be collected - // into an array. - 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 the data cannot be represented as a UTF-8 string, fall back to ISO Latin 1 - 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 we found the organization name, there's no need to keep looping. - if (organizationName) { - foundIt = TRUE; - break; - } - } - if (foundIt) - break; - } - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateReleaseFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, fieldValues); - 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 gPotentialEVChainWithCertificatesMutex; - -// returns a CFArrayRef of SecCertificateRef instances; caller must release the returned array -// -CFArrayRef potentialEVChainWithCertificates(CFArrayRef certificates) -{ - StLock _(gPotentialEVChainWithCertificatesMutex()); - - // Given a partial certificate chain (which may or may not include the root, - // and does not have a guaranteed order except the first item is the leaf), - // examine intermediate certificates to see if they are cross-certified (i.e. - // have the same subject and public key as a trusted root); if so, remove the - // intermediate from the returned certificate array. - - 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) { - // if this is not the leaf, then look for a possible replacement root to end the chain - // Try lookup using Subject Key ID first - 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; -} - -// returns a reference to a root certificate, if one can be found in the -// system root store whose subject name and public key are identical to -// that of the provided certificate, otherwise returns nil. -// -static SecCertificateRef _rootCertificateWithSubjectOfCertificate(SecCertificateRef certificate) -{ - if (!certificate) - return NULL; - - StLock _(SecTrustKeychainsGetMutex()); - - // get data+length for the provided certificate - CSSM_CL_HANDLE clHandle = 0; - CSSM_DATA certData = { 0, NULL }; - OSStatus status = errSecSuccess; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetCLHandle(certificate, &clHandle); - BEGIN_SECAPI_INTERNAL_CALL - clHandle = Certificate::required(certificate)->clHandle(); - END_SECAPI_INTERNAL_CALL - if (status) - return NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetData(certificate, &certData); - BEGIN_SECAPI_INTERNAL_CALL - certData = Certificate::required(certificate)->data(); - END_SECAPI_INTERNAL_CALL - if (status) - return NULL; - - // get system roots keychain reference - SecKeychainRef systemRoots = systemRootStore(); - if (!systemRoots) - return NULL; - - // copy (normalized) subject for the provided certificate - const CSSM_OID_PTR oidPtr = (const CSSM_OID_PTR) &CSSMOID_X509V1SubjectName; - const CSSM_DATA_PTR subjectDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle); - if (!subjectDataPtr) - return NULL; - - // copy public key for the provided certificate - SecKeyRef keyRef = NULL; - SecCertificateRef resultCert = NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateCopyPublicKey(certificate, &keyRef); - BEGIN_SECAPI_INTERNAL_CALL - keyRef = Certificate::required(certificate)->publicKey()->handle(); - END_SECAPI_INTERNAL_CALL - if (!status) { - const CSSM_KEY *cssmKey = NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeyGetCSSMKey(keyRef, &cssmKey); - BEGIN_SECAPI_INTERNAL_CALL - cssmKey = KeyItem::required(keyRef)->key(); - END_SECAPI_INTERNAL_CALL - if (!status) { - // get SHA-1 hash of the public key - uint8 buf[CC_SHA1_DIGEST_LENGTH]; - CSSM_DATA digest = { sizeof(buf), buf }; - if (!cssmKey || !cssmKey->KeyData.Data || !cssmKey->KeyData.Length) { - status = errSecParam; - } else { - CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, buf); - } - if (!status) { - // set up attribute vector (each attribute consists of {tag, length, pointer}) - // we want to match on the public key hash and the normalized subject name - // as well as ensure that the issuer matches the subject - SecKeychainAttribute attrs[] = { - { kSecPublicKeyHashItemAttr, (UInt32)digest.Length, (void *)digest.Data }, - { kSecSubjectItemAttr, (UInt32)subjectDataPtr->Length, (void *)subjectDataPtr->Data }, - { kSecIssuerItemAttr, (UInt32)subjectDataPtr->Length, (void *)subjectDataPtr->Data } - }; - const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; - SecKeychainSearchRef searchRef = NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainSearchCreateFromAttributes(systemRoots, kSecCertificateItemClass, &attributes, &searchRef); - 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; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainSearchCopyNext(searchRef, &certRef); // only need the first one that matches - 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; // caller must release - 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 = errSecSuccess; - BEGIN_SECAPI_INTERNAL_CALL - const CssmData &subjectKeyID = Certificate::required(certificate)->subjectKeyIdentifier(); - logSKID("subjectKeyID: ", subjectKeyID); - END_SECAPI_INTERNAL_CALL -} -#endif - -// returns a reference to a root certificate, if one can be found in the -// system root store whose subject key ID are identical to -// that of the provided certificate, otherwise returns nil. -// -static SecCertificateRef _rootCertificateWithSubjectKeyIDOfCertificate(SecCertificateRef certificate) -{ - SecCertificateRef resultCert = NULL; - OSStatus status = errSecSuccess; - - if (!certificate) - return NULL; - - StLock _(SecTrustKeychainsGetMutex()); - - // get system roots keychain reference - 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 - // caller must release - resultCert = Certificate::required(certificate)->findBySubjectKeyID(keychains, subjectKeyID)->handle(); -#if !defined(NDEBUG) - logSKID(" found SKID: ", subjectKeyID); -#endif - END_SECAPI_INTERNAL_CALL - - SafeCFRelease(&systemRoots); - - return resultCert; -} - -// returns an array of possible root certificates (SecCertificateRef instances) -// for the given EV OID (a hex string); caller must release the array -// -static -CFArrayRef _possibleRootCertificatesForOidString(CFStringRef oidString) -{ - StLock _(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 = errSecSuccess; - SecKeychainSearchRef searchRef = NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainSearchCreateFromAttributes(systemRoots, kSecCertificateItemClass, NULL, &searchRef); - 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; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecKeychainSearchCopyNext(searchRef, &certRef); - 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 }; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetData((SecCertificateRef) certRef, &certData); - 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 = errSecParam; - } else { - CC_SHA1(certData.Data, (CC_LONG)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; -} - -// returns an array of allowed root certificates (SecCertificateRef instances) -// for the given EV OID (a hex string); caller must release the array. -// This differs from _possibleRootCertificatesForOidString in that each possible -// certificate is further checked for trust settings, so we don't include -// a certificate which is untrusted (or explicitly distrusted). -// -CFArrayRef _allowedRootCertificatesForOidString(CFStringRef oidString) -{ - CFMutableArrayRef allowedRootCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFArrayRef possibleRootCertificates = _possibleRootCertificatesForOidString(oidString); - if (possibleRootCertificates) { - CFIndex idx, count = CFArrayGetCount(possibleRootCertificates); - for (idx=0; idxclHandle(); - END_SECAPI_INTERNAL_CALL - if (status) - return NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetData(certRef, &certData); - BEGIN_SECAPI_INTERNAL_CALL - certData = Certificate::required(certRef)->data(); - END_SECAPI_INTERNAL_CALL - if (status) - return NULL; - - // Does the leaf certificate contain a Certificate Policies extension? - const CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_CertificatePolicies; - CSSM_DATA_PTR extensionDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle); - if (!extensionDataPtr) - return NULL; - - // Does the extension contain one of the magic EV CA OIDs we know about? - 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); - - // Fetch the allowed root CA certificates for this OID, if any - 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; - } - - // The leaf certificate needs extended validation (with revocation checking). - // Return the array of allowed roots for this leaf certificate. - return allowedRoots; -} - -// returns true if the provided certificate contains a wildcard in either -// its common name or subject alternative name. -// -static -bool hasWildcardDNSName(SecCertificateRef certRef) -{ - OSStatus status = errSecSuccess; - CFArrayRef dnsNames = NULL; - - BEGIN_SECAPI_INTERNAL_CALL - Required(&dnsNames) = Certificate::required(certRef)->copyDNSNames(); - END_SECAPI_INTERNAL_CALL - if (status || !dnsNames) - return false; - - bool hasWildcard = false; - const CFStringRef wildcard = CFSTR("*"); - CFIndex index, count = CFArrayGetCount(dnsNames); - for (index = 0; index < count; index ++) { - CFStringRef name = (CFStringRef) CFArrayGetValueAtIndex(dnsNames, index); - if (name) { - CFRange foundRange = CFStringFind(name, wildcard, 0); - if (foundRange.length != 0 && foundRange.location != kCFNotFound) { - hasWildcard = true; - break; - } - } - } - CFRelease(dnsNames); - return hasWildcard; -} - -// returns a CFDictionaryRef of extended validation results for the given chain, -// or NULL if the certificate chain did not meet all EV criteria. (Caller must -// release the result if not NULL.) -// -static -CFDictionaryRef extendedValidationResults(CFArrayRef certChain, SecTrustResultType trustResult, OSStatus tpResult) -{ - // This function is intended to be called after the "regular" TP evaluation - // has taken place (i.e. trustResult and tpResult are available), and there - // is a full certificate chain to examine. - - CFIndex chainIndex, chainLen = (certChain) ? CFArrayGetCount(certChain) : 0; - if (chainLen < 2) { - return NULL; // invalid chain length - } - - if (trustResult != kSecTrustResultUnspecified) { - - // "Recoverable" means the certificate failed to meet all policy requirements, but is intrinsically OK. - // One of the failures we might encounter is if the OCSP responder tells us to go away. Since this is a - // real-world case, we'll check for OCSP and CRL meta-errors specifically. - bool recovered = false; - if (trustResult == kSecTrustResultRecoverableTrustFailure) { - recovered = isRevocationServerMetaError((CSSM_RETURN)tpResult); - } - if (!recovered) { - return NULL; - } - } - - // - // What we know at this point: - // - // 1. From a previous call to allowedEVRootsForLeafCertificate - // (or we wouldn't be getting called by extendedTrustResults): - // - a leaf certificate exists - // - that certificate contains a Certificate Policies extension - // - that extension contains an OID from one of the trusted EV CAs we know about - // - we have found at least one allowed EV root for that OID - // - // 2. From the TP evaluation: - // - the leaf certificate verifies back to a trusted EV root (with no trust settings overrides) - // - SSL trust evaluation with OCSP revocation checking enabled returned no (fatal) errors - // - // We need to verify the following additional requirements for the leaf (as of EV 1.1, 6(a)(2)): - // - cannot specify a wildcard in commonName or subjectAltName - // (note: this is a change since EV 1.0 (9.2.1), which stated that "Wildcard FQDNs are permitted.") - // - // Finally, we need to check the following requirements (EV 1.1 specification, Appendix B): - // - the trusted root, if created after 10/31/2006, must have: - // - critical basicConstraints extension with CA bit set - // - critical keyUsage extension with keyCertSign and cRLSign bits set - // - intermediate certs, if present, must have: - // - certificatePolicies extension, containing either a known EV CA OID, or anyPolicy - // - non-critical cRLDistributionPoint extension - // - critical basicConstraints extension with CA bit set - // - critical keyUsage extension with keyCertSign and cRLSign bits set - // - - // check leaf certificate for wildcard names - if (hasWildcardDNSName((SecCertificateRef) CFArrayGetValueAtIndex(certChain, 0))) { - trustDebug("has wildcard name (does not meet EV criteria)"); - return NULL; - } - - // check intermediate CA certificates for required extensions per Appendix B of EV 1.1 specification. - 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 = errSecSuccess; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetCLHandle(intermediateCert, &clHandle); - BEGIN_SECAPI_INTERNAL_CALL - clHandle = Certificate::required(intermediateCert)->clHandle(); - END_SECAPI_INTERNAL_CALL - if (status) - return NULL; - // note: Sec* APIs are not re-entrant due to the API lock - // status = SecCertificateGetData(intermediateCert, &certData); - 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); - - // FIX: add checks for the following (not essential to this implementation): - // - non-critical cRLDistributionPoint extension - // - critical basicConstraints extension with CA bit set - // - critical keyUsage extension with keyCertSign and cRLSign bits set - // Tracked by - } - - 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); - trustDebug("[EV] extended validation succeeded"); - SafeCFRelease(&organizationName); - return resultDict; - } - } - - return NULL; -} - -// returns a CFDictionaryRef containing extended trust results. -// Caller must release this dictionary. -// -// If the isEVCandidate argument is true, extended validation checking is performed -// and the kSecEVOrganizationName key will be set in the dictionary if EV criteria is met. -// In all cases, kSecTrustEvaluationDate and kSecTrustExpirationDate will be set. -// -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); - // by default, permit caching of trust evaluation results for up to 2 hours - // FIXME: need to modify this based on cert expiration and OCSP/CRL validity - CFDateRef trustExpirationDate = CFDateCreate(kCFAllocatorDefault, at + (60*60*2)); - CFDictionaryAddValue(resultDict, kSecTrustEvaluationDate, trustEvaluationDate); - SafeCFRelease(&trustEvaluationDate); - CFDictionaryAddValue(resultDict, kSecTrustExpirationDate, trustExpirationDate); - SafeCFRelease(&trustExpirationDate); - - return resultDict; -} - -// returns a CFDictionaryRef containing mappings from supported EV CA OIDs to SHA-1 hash values; -// caller must release -// -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 - // Work around rdar://6302788 by hard coding a hash that was missed when addressing - // This is being addressed in SnowLeopard by rdar://6305989 - 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; -} - -// returns a CFStringRef containing a decimal representation of the given OID. -// Caller must release. - -static CFStringRef _decimalStringForOid(CSSM_OID_PTR oid) -{ - CFMutableStringRef str = CFStringCreateMutable(NULL, 0); - if (!str || oid->Length > 32) - return str; - - // The first two levels are encoded into one byte, since the root level - // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then - // y may be > 39, so we have to add special-case handling for this. - unsigned long value = 0; - unsigned int x = oid->Data[0] / 40; - unsigned int y = oid->Data[0] % 40; - if (x > 2) { - // Handle special case for large y 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 gOidStringForCertificatePoliciesMutex; - -static CFStringRef _oidStringForCertificatePolicies(const CE_CertPolicies *certPolicies) -{ - StLock _(gOidStringForCertificatePoliciesMutex()); - - // returns the first EV OID (as a string) found in the given Certificate Policies extension, - // or NULL if the extension does not contain any known EV OIDs. (Note that the "any policy" OID - // is a special case and will be returned if present, although its presence is only meaningful - // in an intermediate CA.) - - 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; // sanity check; EV certs normally have EV OID as first policy - 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) || // is it the "any" OID, or - CFDictionaryGetValue(evOidDict, oidStr) != NULL) { // a known EV CA OID? - foundOidStr = CFStringCreateCopy(NULL, oidStr); - } - SafeCFRelease(&oidStr); - if (foundOidStr) - break; - } - SafeCFRelease(&evOidDict); - - return foundOidStr; -} -