X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_keychain/lib/Trust.cpp diff --git a/libsecurity_keychain/lib/Trust.cpp b/libsecurity_keychain/lib/Trust.cpp new file mode 100644 index 00000000..4105717b --- /dev/null +++ b/libsecurity_keychain/lib/Trust.cpp @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2002-2010 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@ + */ + +// +// Trust.cpp +// +#include +#include +#include +#include +#include +#include +#include +#include "SecBridge.h" +#include "TrustAdditions.h" +#include "TrustKeychains.h" + + +using namespace Security; +using namespace KeychainCore; + +// +// Translate CFDataRef to CssmData. The output shares the input's buffer. +// +static inline CssmData cfData(CFDataRef data) +{ + return CssmData(const_cast(CFDataGetBytePtr(data)), + CFDataGetLength(data)); +} + +// +// Convert a SecPointer to a CF object. +// +static SecCertificateRef +convert(const SecPointer &certificate) +{ + return *certificate; +} + +// +// For now, we use a global TrustStore +// +ModuleNexus Trust::gStore; + +#pragma mark -- TrustKeychains -- + +// +// TrustKeychains maintains a global reference to standard system keychains, +// to avoid having them be opened anew for each Trust instance. +// +class TrustKeychains +{ +public: + TrustKeychains(); + ~TrustKeychains() {} + CSSM_DL_DB_HANDLE rootStoreHandle() { return mRootStore->database()->handle(); } + CSSM_DL_DB_HANDLE systemKcHandle() { return mSystem->database()->handle(); } + Keychain &rootStore() { return mRootStore; } + Keychain &systemKc() { return mSystem; } +private: + Keychain mRootStore; + Keychain mSystem; +}; + +// +// Singleton maintaining open references to standard system keychains, +// to avoid having them be opened anew every time SecTrust is used. +// + +static ModuleNexus trustKeychains; +static ModuleNexus trustKeychainsMutex; + +TrustKeychains::TrustKeychains() : + mRootStore(globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false)), + mSystem(globals().storageManager.make(ADMIN_CERT_STORE_PATH, false)) +{ +} + +RecursiveMutex& SecTrustKeychainsGetMutex() +{ + return trustKeychainsMutex(); +} + +#pragma mark -- Trust -- +// +// Construct a Trust object with suitable defaults. +// Use setters for additional arguments before calling evaluate(). +// +Trust::Trust(CFTypeRef certificates, CFTypeRef policies) + : mTP(gGuidAppleX509TP), mAction(CSSM_TP_ACTION_DEFAULT), + mCerts(cfArrayize(certificates)), mPolicies(cfArrayize(policies)), + mResult(kSecTrustResultInvalid), mUsingTrustSettings(false), + mAnchorPolicy(useAnchorsDefault), mSearchLibsSet(false), + mSearchLibs(NULL), mMutex(Mutex::recursive) +{ +} + + +// +// Clean up a Trust object +// +Trust::~Trust() +{ + clearResults(); + if (mSearchLibs) { + delete mSearchLibs; + } +} + + +// +// Get searchLibs (a vector of Keychain objects); +// normally initialized to default search list +// +StorageManager::KeychainList& Trust::searchLibs(bool init) +{ + if (!mSearchLibs) { + mSearchLibs = new StorageManager::KeychainList; + if (init) { + globals().storageManager.getSearchList(*mSearchLibs); + } + } + return *mSearchLibs; +} + + +// +// Set searchLibs to provided vector of Keychain objects +// +void Trust::searchLibs(StorageManager::KeychainList &libs) +{ + searchLibs(false) = libs; + mSearchLibsSet = true; +} + + +// +// Retrieve the last TP evaluation result, if any +// +CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult() +{ + if (mResult == kSecTrustResultInvalid) + MacOSError::throwMe(errSecTrustNotAvailable); + return &mTpResult; +} + + +// SecCertificateRef -> CssmData +CssmData cfCertificateData(SecCertificateRef certificate) +{ + return Certificate::required(certificate)->data(); +} + +// SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy) +CssmField cfField(SecPolicyRef item) +{ + SecPointer policy = Policy::required(SecPolicyRef(item)); + return CssmField(policy->oid(), policy->value()); +} + +// SecKeychain -> CssmDlDbHandle +CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref) +{ + Keychain keychain = KeychainImpl::required(ref); + return keychain->database()->handle(); +} + +#if !defined(NDEBUG) +void showCertSKID(const void *value, void *context); +#endif + +// +// Here's the big "E" - evaluation. +// We build most of the CSSM-layer input structures dynamically right here; +// they will auto-destruct when we're done. The output structures are kept +// around (in our data members) for later analysis. +// Note that evaluate() can be called repeatedly, so we must be careful to +// dispose of prior results. +// +void Trust::evaluate(bool disableEV) +{ + bool isEVCandidate=false; + // begin evaluation block with stack-based mutex + { + StLock_(mMutex); + // if we have evaluated before, release prior result + clearResults(); + + // determine whether the leaf certificate is an EV candidate + CFArrayRef allowedAnchors = allowedEVRootsForLeafCertificate(mCerts); + CFArrayRef filteredCerts = NULL; + isEVCandidate = (allowedAnchors && !disableEV) ? true : false; + if (isEVCandidate) { + secdebug("evTrust", "Trust::evaluate() certificate is EV candidate"); + filteredCerts = potentialEVChainWithCertificates(mCerts); + mCerts = filteredCerts; + } else { + secdebug("evTrust", "Trust::evaluate() performing standard evaluation"); + if (mCerts) { + filteredCerts = CFArrayCreateMutableCopy(NULL, 0, mCerts); + } + if (mAnchors) { + allowedAnchors = CFArrayCreateMutableCopy(NULL, 0, mAnchors); + } + } + // retain these certs as long as we potentially could have results involving them + // (note that assignment to a CFRef type performs an implicit retain) + mAllowedAnchors = allowedAnchors; + mFilteredCerts = filteredCerts; + + if (allowedAnchors) + CFRelease(allowedAnchors); + if (filteredCerts) + CFRelease(filteredCerts); + + if (mAllowedAnchors) + { + secdebug("trusteval", "Trust::evaluate: anchors: %ld", CFArrayGetCount(mAllowedAnchors)); +#if !defined(NDEBUG) + CFArrayApplyFunction(mAllowedAnchors, CFRangeMake(0, CFArrayGetCount(mAllowedAnchors)), showCertSKID, NULL); +#endif + } + + // set default search list from user's default, if caller did not explicitly supply it + if(!mSearchLibsSet) { + globals().storageManager.getSearchList(searchLibs()); + mSearchLibsSet = true; + } + + // build the target cert group + CFToVector subjects(mFilteredCerts); + CertGroup subjectCertGroup(CSSM_CERT_X_509v3, + CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA); + subjectCertGroup.count() = subjects; + subjectCertGroup.blobCerts() = subjects; + + // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure + TPBuildVerifyContext context(mAction); + + /* + * Guarantee *some* action data... + * NOTE this only works with the local X509 TP. When this module can deal + * with other TPs, this must be revisited. + */ + CSSM_APPLE_TP_ACTION_DATA localActionData; + memset(&localActionData, 0, sizeof(localActionData)); + CssmData localActionCData((uint8 *)&localActionData, sizeof(localActionData)); + CSSM_APPLE_TP_ACTION_DATA *actionDataP = &localActionData; + if (mActionData) { + context.actionData() = cfData(mActionData); + actionDataP = (CSSM_APPLE_TP_ACTION_DATA *)context.actionData().data(); + } + else { + context.actionData() = localActionCData; + } + + if (!mAnchors) { + // always check trust settings if caller did not provide explicit trust anchors + actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS; + } + + if (policySpecified(mPolicies, CSSMOID_APPLE_TP_SSL)) { + // enable network cert fetch for SSL only: + actionDataP->ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; + } + + /* + * Policies (one at least, please). + * For revocation policies, see if any have been explicitly specified... + */ + CFMutableArrayRef allPolicies = NULL; + uint32 numSpecAdded = 0; + uint32 numPrefAdded = 0; + bool requirePerCert = (actionDataP->ActionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT); + if (isEVCandidate || requirePerCert) { + // force revocation checking for this evaluation + secdebug("evTrust", "Trust::evaluate() forcing OCSP/CRL revocation checking"); + allPolicies = forceRevocationPolicies(numPrefAdded, context.allocator, requirePerCert); + } + else if (mAnchors && (CFArrayGetCount(mAnchors)==0) && (searchLibs().size()==0)) { + // caller explicitly provided empty anchors and no keychain list; + // override global revocation check setting for this evaluation + allPolicies = NULL; // use only mPolicies + } + else if(!(revocationPolicySpecified(mPolicies))) { + /* + * None specified in mPolicies; see if any specified via SPI. + */ + allPolicies = addSpecifiedRevocationPolicies(numSpecAdded, context.allocator); + if(allPolicies == NULL) { + /* + * None there; try preferences. + */ + allPolicies = Trust::addPreferenceRevocationPolicies(numPrefAdded, + context.allocator); + } + + } + if(allPolicies == NULL) { + allPolicies = CFMutableArrayRef(CFArrayRef(mPolicies)); + } + orderRevocationPolicies(allPolicies); + CFToVector policies(allPolicies); + if (policies.empty()) + MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS); + context.setPolicies(policies, policies); + + // anchor certificates (if caller provides them, or if cert requires EV) + CFCopyRef anchors(mAllowedAnchors); + CFToVector roots(anchors); + if (!anchors) { + // no anchor certificates were provided; + // built-in anchors will be trusted unless explicitly disabled. + mUsingTrustSettings = (mAnchorPolicy < useAnchorsOnly); + secdebug("userTrust", "Trust::evaluate() %s", + (mUsingTrustSettings) ? "using UserTrust" : "has no trusted anchors!"); + } + else { + // anchor certificates were provided; + // built-in anchors will NOT also be trusted unless explicitly enabled. + mUsingTrustSettings = (mAnchorPolicy == useAnchorsAndBuiltIns); + secdebug("userTrust", "Trust::evaluate() using %s %s anchors", + (mUsingTrustSettings) ? "UserTrust AND" : "only", + (isEVCandidate) ? "EV" : "caller"); + context.anchors(roots, roots); + } + + // dlDbList (keychain list) + vector dlDbList; + { + StLock _(SecTrustKeychainsGetMutex()); + StorageManager::KeychainList& list = searchLibs(); + for (StorageManager::KeychainList::const_iterator it = list.begin(); + it != list.end(); it++) + { + try + { + // For the purpose of looking up intermediate certificates to establish trust, + // do not include the network-based LDAP or DotMac pseudo-keychains. (The only + // time the network should be consulted for certificates is if there is an AIA + // extension with a specific URL, which will be handled by the TP code.) + CSSM_DL_DB_HANDLE dldbHandle = (*it)->database()->handle(); + if (dldbHandle.DLHandle) { + CSSM_GUID guid = {}; + CSSM_RETURN crtn = CSSM_GetModuleGUIDFromHandle(dldbHandle.DLHandle, &guid); + if (crtn == CSSM_OK) { + if ((memcmp(&guid, &gGuidAppleLDAPDL, sizeof(CSSM_GUID))==0) || + (memcmp(&guid, &gGuidAppleDotMacDL, sizeof(CSSM_GUID))==0)) { + continue; // don't add to dlDbList + } + } + } + // This DB is OK to search for intermediate certificates. + dlDbList.push_back(dldbHandle); + } + catch (...) + { + } + } + if(mUsingTrustSettings) { + /* Append system anchors for use with Trust Settings */ + try { + dlDbList.push_back(trustKeychains().rootStoreHandle()); + actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS; + } + catch (...) { + // no root store or system keychain; don't use trust settings but continue + mUsingTrustSettings = false; + } + try { + dlDbList.push_back(trustKeychains().systemKcHandle()); + } + catch(...) { + /* Oh well, at least we got the root store DB */ + } + } + context.setDlDbList(dlDbList.size(), &dlDbList[0]); + } + + // verification time + char timeString[15]; + if (mVerifyTime) { + CssmUniformDate(static_cast(mVerifyTime)).convertTo( + timeString, sizeof(timeString)); + context.time(timeString); + } + + // to avoid keychain open/close thrashing, hold a copy of the search list + StorageManager::KeychainList *holdSearchList = NULL; + if (searchLibs().size() > 0) { + holdSearchList = new StorageManager::KeychainList; + globals().storageManager.getSearchList(*holdSearchList); + } + + // Go TP! + try { + mTP->certGroupVerify(subjectCertGroup, context, &mTpResult); + mTpReturn = noErr; + } catch (CommonError &err) { + mTpReturn = err.osStatus(); + secdebug("trusteval", "certGroupVerify exception: %d", (int)mTpReturn); + } + mResult = diagnoseOutcome(); + + // see if we can use the evidence + if (mTpResult.count() > 0 + && mTpResult[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER + && mTpResult[0].as()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION + && mTpResult.count() == 3 + && mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP + && mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) { + evaluateUserTrust(*mTpResult[1].as(), + mTpResult[2].as(), anchors); + } else { + // unexpected evidence information. Can't use it + secdebug("trusteval", "unexpected evidence ignored"); + } + + /* do post-processing for the evaluated certificate chain */ + CFArrayRef fullChain = makeCFArray(convert, mCertChain); + CFDictionaryRef etResult = extendedTrustResults(fullChain, mResult, mTpReturn, isEVCandidate); + mExtendedResult = etResult; // assignment to CFRef type is an implicit retain + if (etResult) { + CFRelease(etResult); + } + if (fullChain) { + CFRelease(fullChain); + } + + /* Clean up Policies we created implicitly */ + if(numSpecAdded) { + freeSpecifiedRevocationPolicies(allPolicies, numSpecAdded, context.allocator); + } + if(numPrefAdded) { + Trust::freePreferenceRevocationPolicies(allPolicies, numPrefAdded, context.allocator); + } + + if (holdSearchList) { + delete holdSearchList; + holdSearchList = NULL; + } + } // end evaluation block with mutex; releases all temporary allocations in this scope + + + if (isEVCandidate && mResult == kSecTrustResultRecoverableTrustFailure && + isRevocationServerMetaError(mTpReturn)) { + // re-do the evaluation, this time disabling EV + evaluate(true); + } +} + +// CSSM_RETURN values that map to kSecTrustResultRecoverableTrustFailure. +static const CSSM_RETURN recoverableErrors[] = +{ + CSSMERR_TP_INVALID_ANCHOR_CERT, + CSSMERR_TP_NOT_TRUSTED, + CSSMERR_TP_VERIFICATION_FAILURE, + CSSMERR_TP_VERIFY_ACTION_FAILED, + CSSMERR_TP_INVALID_CERTIFICATE, + CSSMERR_TP_INVALID_REQUEST_INPUTS, + CSSMERR_TP_CERT_EXPIRED, + CSSMERR_TP_CERT_NOT_VALID_YET, + CSSMERR_TP_CERTIFICATE_CANT_OPERATE, + CSSMERR_TP_INVALID_CERT_AUTHORITY, + CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK, + CSSMERR_APPLETP_HOSTNAME_MISMATCH, + CSSMERR_TP_VERIFY_ACTION_FAILED, + CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND, + CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS, + CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE, + CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH, + CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS, + CSSMERR_APPLETP_CS_BAD_PATH_LENGTH, + CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE, + CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE, + CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT, + CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH, + CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN, + CSSMERR_APPLETP_CRL_NOT_FOUND, + CSSMERR_APPLETP_CRL_SERVER_DOWN, + CSSMERR_APPLETP_CRL_NOT_VALID_YET, + CSSMERR_APPLETP_OCSP_UNAVAILABLE, + CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK, + CSSMERR_APPLETP_NETWORK_FAILURE, + CSSMERR_APPLETP_OCSP_RESP_TRY_LATER, +}; +#define NUM_RECOVERABLE_ERRORS (sizeof(recoverableErrors) / sizeof(CSSM_RETURN)) + +// +// Classify the TP outcome in terms of a SecTrustResultType +// +SecTrustResultType Trust::diagnoseOutcome() +{ + StLock_(mMutex); + + uint32 chainLength = 0; + if (mTpResult.count() == 3 && + mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP && + mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) + { + const CertGroup &chain = *mTpResult[1].as(); + chainLength = chain.count(); + } + + switch (mTpReturn) { + case noErr: // peachy + if (mUsingTrustSettings) + { + if (chainLength) + { + const CSSM_TP_APPLE_EVIDENCE_INFO *infoList = mTpResult[2].as(); + const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[chainLength-1]); + const CSSM_TP_APPLE_CERT_STATUS resultCertStatus = info.status(); + bool hasUserDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) && + (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER)); + bool hasAdminDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) && + (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN)); + if (hasUserDomainTrust || hasAdminDomainTrust) + { + return kSecTrustResultProceed; // explicitly allowed + } + } + } + return kSecTrustResultUnspecified; // cert evaluates OK + case CSSMERR_TP_INVALID_CERTIFICATE: // bad certificate + return kSecTrustResultFatalTrustFailure; + case CSSMERR_APPLETP_TRUST_SETTING_DENY: // authoritative denial + return kSecTrustResultDeny; + default: + break; + } + + // a known list of returns maps to kSecTrustResultRecoverableTrustFailure + const CSSM_RETURN *errp=recoverableErrors; + for(unsigned dex=0; dex anchors) +{ + StLock_(mMutex); + // extract cert chain as Certificate objects + mCertChain.resize(chain.count()); + for (uint32 n = 0; n < mCertChain.size(); n++) { + const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[n]); + if (info.recordId()) { + Keychain keychain = keychainByDLDb(info.DlDbHandle); + DbUniqueRecord uniqueId(keychain->database()->newDbUniqueRecord()); + secdebug("trusteval", "evidence %lu from keychain \"%s\"", (unsigned long)n, keychain->name()); + *static_cast(uniqueId) = info.UniqueRecord; + uniqueId->activate(); // transfers ownership + Item ii = keychain->item(CSSM_DL_DB_RECORD_X509_CERTIFICATE, uniqueId); + Certificate* cert = dynamic_cast(ii.get()); + if (cert == NULL) { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + mCertChain[n] = cert; + } else if (info.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS)) { + secdebug("trusteval", "evidence %lu from input cert %lu", (unsigned long)n, (unsigned long)info.index()); + assert(info.index() < uint32(CFArrayGetCount(mCerts))); + SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(mCerts, + info.index())); + mCertChain[n] = Certificate::required(cert); + } else if (info.status(CSSM_CERT_STATUS_IS_IN_ANCHORS)) { + secdebug("trusteval", "evidence %lu from anchor cert %lu", (unsigned long)n, (unsigned long)info.index()); + assert(info.index() < uint32(CFArrayGetCount(anchors))); + SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(anchors, + info.index())); + mCertChain[n] = Certificate::required(cert); + } else { + // unknown source; make a new Certificate for it + secdebug("trusteval", "evidence %lu from unknown source", (unsigned long)n); + mCertChain[n] = + new Certificate(chain.blobCerts()[n], + CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER); + } + } + + // now walk the chain, leaf-to-root, checking for user settings + TrustStore &store = gStore(); + SecPointer policy = + Policy::required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies, 0))); + for (mResultIndex = 0; + mResult == kSecTrustResultUnspecified && mResultIndex < mCertChain.size(); + mResultIndex++) { + if (!mCertChain[mResultIndex]) { + assert(false); + continue; + } + mResult = store.find(mCertChain[mResultIndex], policy, searchLibs()); + secdebug("trusteval", "trustResult=%lu from cert %lu", mResult, (unsigned long)mResultIndex); + } +} + + +// +// Release TP evidence information. +// This information is severely under-defined by CSSM, so we proceed +// as follows: +// (a) If the evidence matches an Apple-defined pattern, use specific +// knowledge of that format. +// (b) Otherwise, assume that the void * are flat blocks of memory. +// +void Trust::releaseTPEvidence(TPVerifyResult &result, Allocator &allocator) +{ + if (result.count() > 0) { // something to do + if (result[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER) { + // Apple defined evidence form -- use intimate knowledge + if (result[0].as()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION + && result.count() == 3 + && result[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP + && result[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) { + // proper format + CertGroup& certs = *result[1].as(); + CSSM_TP_APPLE_EVIDENCE_INFO *evidence = result[2].as(); + uint32 count = certs.count(); + allocator.free(result[0].data()); // just a struct + certs.destroy(allocator); // certgroup contents + allocator.free(result[1].data()); // the CertGroup itself + for (uint32 n = 0; n < count; n++) + allocator.free(evidence[n].StatusCodes); + allocator.free(result[2].data()); // array of (flat) info structs + } else { + secdebug("trusteval", "unrecognized Apple TP evidence format"); + // drop it -- better leak than kill + } + } else { + // unknown format -- blindly assume flat blobs + secdebug("trusteval", "destroying unknown TP evidence format"); + for (uint32 n = 0; n < result.count(); n++) + { + allocator.free(result[n].data()); + } + } + + allocator.free (result.Evidence); + } +} + + +// +// Clear evaluation results unless state is initial (invalid) +// +void Trust::clearResults() +{ + StLock_(mMutex); + if (mResult != kSecTrustResultInvalid) { + releaseTPEvidence(mTpResult, mTP.allocator()); + mResult = kSecTrustResultInvalid; + } +} + + +// +// Build evidence information +// +void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain) +{ + StLock_(mMutex); + if (mResult == kSecTrustResultInvalid) + MacOSError::throwMe(errSecTrustNotAvailable); + certChain = mEvidenceReturned = + makeCFArray(convert, mCertChain); + if(mTpResult.count() >= 3) { + statusChain = mTpResult[2].as(); + } + else { + statusChain = NULL; + } +} + + +// +// Return extended result dictionary +// +void Trust::extendedResult(CFDictionaryRef &result) +{ + if (mResult == kSecTrustResultInvalid) + MacOSError::throwMe(errSecTrustNotAvailable); + if (mExtendedResult) + CFRetain(mExtendedResult); // retain before handing out to caller + result = mExtendedResult; +} + + +// +// Return properties array (a CFDictionaryRef for each certificate in chain) +// +CFArrayRef Trust::properties() +{ + // Builds and returns an array which the caller must release. + StLock_(mMutex); + CFMutableArrayRef properties = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks); + if (mResult == kSecTrustResultInvalid) // chain not built or evaluated + return properties; + + // Walk the chain from leaf to anchor, building properties dictionaries + for (uint32 idx=0; idx < mCertChain.size(); idx++) { + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (dict) { + CFStringRef title = NULL; + mCertChain[idx]->inferLabel(false, &title); + if (title) { + CFDictionarySetValue(dict, (const void *)kSecPropertyTypeTitle, (const void *)title); + CFRelease(title); + } + if (idx == 0 && mTpReturn != noErr) { + CFStringRef error = SecCopyErrorMessageString(mTpReturn, NULL); + if (error) { + CFDictionarySetValue(dict, (const void *)kSecPropertyTypeError, (const void *)error); + CFRelease(error); + } + } + CFArrayAppendValue(properties, (const void *)dict); + CFRelease(dict); + } + } + + return properties; +} + + + +//* =========================================================================== +//* We need a way to compare two CSSM_DL_DB_HANDLEs WITHOUT using a operator +//* overload +//* =========================================================================== +bool Compare_CSSM_DL_DB_HANDLE(const CSSM_DL_DB_HANDLE &h1, const CSSM_DL_DB_HANDLE &h2) +{ + return (h1.DLHandle == h2.DLHandle && h1.DBHandle == h2.DBHandle); +} + + + +// +// Given a DL_DB_HANDLE, locate the Keychain object (from the search list) +// +Keychain Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE &handle) +{ + StLock_(mMutex); + StorageManager::KeychainList& list = searchLibs(); + for (StorageManager::KeychainList::const_iterator it = list.begin(); + it != list.end(); it++) + { + try + { + + if (Compare_CSSM_DL_DB_HANDLE((*it)->database()->handle(), handle)) + return *it; + } + catch (...) + { + } + } + if(mUsingTrustSettings) { + try { + if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().rootStoreHandle(), handle)) { + return trustKeychains().rootStore(); + } + if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().systemKcHandle(), handle)) { + return trustKeychains().systemKc(); + } + } + catch(...) { + /* one of those is missing; proceed */ + } + } + + // could not find in search list - internal error + + // we now throw an error here rather than assert and silently fail. That way our application won't crash... + MacOSError::throwMe(errSecInternal); +}