X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp diff --git a/Security/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp b/Security/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp deleted file mode 100644 index b1f0f931..00000000 --- a/Security/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp +++ /dev/null @@ -1,2583 +0,0 @@ -/* - * Copyright (c) 2000-2013 Apple Inc. All Rights Reserved. - * - * The contents of this file constitute Original Code as defined in and are - * subject to the Apple Public Source License Version 1.2 (the 'License'). - * You may not use this file except in compliance with the License. Please obtain - * a copy of the License at http://www.apple.com/publicsource and read it before - * using this file. - * - * This 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. - */ - - -/* - * TPCertInfo.cpp - TP's private certificate info classes - */ - -#include "TPCertInfo.h" -#include "tpdebugging.h" -#include "tpTime.h" -#include "certGroupUtils.h" -#include "TPDatabase.h" -#include "TPNetwork.h" -#include -#include -#include -#include -#include /* for memcmp */ -#include /* for Mutex */ -#include -#include -#include -#include -#include -#include -#include - -#define tpTimeDbg(args...) secdebug("tpTime", ## args) -#define tpCertInfoDbg(args...) secdebug("tpCert", ## args) - -static const TPClItemCalls tpCertClCalls = -{ - CSSM_CL_CertGetFirstCachedFieldValue, - CSSM_CL_CertAbortQuery, - CSSM_CL_CertCache, - CSSM_CL_CertAbortCache, - CSSM_CL_CertVerify, - &CSSMOID_X509V1ValidityNotBefore, - &CSSMOID_X509V1ValidityNotAfter, - CSSMERR_TP_INVALID_CERT_POINTER, - CSSMERR_TP_CERT_EXPIRED, - CSSMERR_TP_CERT_NOT_VALID_YET -}; - -TPClItemInfo::TPClItemInfo( - CSSM_CL_HANDLE clHand, - CSSM_CSP_HANDLE cspHand, - const TPClItemCalls &clCalls, - const CSSM_DATA *itemData, - TPItemCopy copyItemData, - const char *verifyTime) // may be NULL - : - mClHand(clHand), - mCspHand(cspHand), - mClCalls(clCalls), - mWeOwnTheData(false), - mCacheHand(0), - mIssuerName(NULL), - mSubjectKeyID(NULL), - mAuthorityKeyID(NULL), - mItemData(NULL), - mSigAlg(CSSM_ALGID_NONE), - mNotBefore(NULL), - mNotAfter(NULL), - mIsExpired(false), - mIsNotValidYet(false), - mIndex(0) -{ - try { - CSSM_RETURN crtn = cacheItem(itemData, copyItemData); - if(crtn) { - CssmError::throwMe(crtn); - } - - /* - * Fetch standard fields... - * Issuer name assumes same OID for Certs and CRLs! - */ - crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName); - if(crtn) { - CssmError::throwMe(crtn); - } - - /* - * Signing algorithm, infer from TBS algId - * Note this assumes that the OID for fetching this field is the - * same for CRLs and Certs. - */ - CSSM_DATA_PTR algField; - crtn = fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS, &algField); - if(crtn) { - releaseResources(); - CssmError::throwMe(crtn); - } - if(algField->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) { - tpErrorLog("TPClItemInfo: bad CSSM_X509_ALGORITHM_IDENTIFIER\n"); - CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); - } - CSSM_X509_ALGORITHM_IDENTIFIER *algId = - (CSSM_X509_ALGORITHM_IDENTIFIER *)algField->Data; - bool algFound = cssmOidToAlg(&algId->algorithm, &mSigAlg); - if(!algFound) { - tpErrorLog("TPClItemInfo: unknown signature algorithm\n"); - CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); - } - if(mSigAlg == CSSM_ALGID_ECDSA_SPECIFIED) { - /* Further processing needed to get digest algorithm */ - if(decodeECDSA_SigAlgParams(&algId->parameters, &mSigAlg)) { - tpErrorLog("TPClItemInfo: incomplete/unknown ECDSA signature algorithm\n"); - CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); - } - } - freeField(&CSSMOID_X509V1SignatureAlgorithmTBS, algField); - - /* Attempt to fetch authority key id and subject key id, - * ignore error if they do not exist. - */ - fetchField(&CSSMOID_SubjectKeyIdentifier, &mSubjectKeyID); - fetchField(&CSSMOID_AuthorityKeyIdentifier, &mAuthorityKeyID); - - fetchNotBeforeAfter(); - calculateCurrent(verifyTime); - } - catch(...) { - releaseResources(); - throw; - } -} - -TPClItemInfo::~TPClItemInfo() -{ - tpCertInfoDbg("TPClItemInfo destruct this %p", this); - releaseResources(); -} - -void TPClItemInfo::releaseResources() -{ - if(mWeOwnTheData && (mItemData != NULL)) { - tpFreeCssmData(Allocator::standard(), mItemData, CSSM_TRUE); - mWeOwnTheData = false; - mItemData = NULL; - } - if(mIssuerName) { - freeField(&CSSMOID_X509V1IssuerName, mIssuerName); - mIssuerName = NULL; - } - if(mSubjectKeyID) { - freeField(&CSSMOID_SubjectKeyIdentifier, mSubjectKeyID); - mSubjectKeyID = NULL; - } - if(mAuthorityKeyID) { - freeField(&CSSMOID_AuthorityKeyIdentifier, mAuthorityKeyID); - mAuthorityKeyID = NULL; - } - if(mCacheHand != 0) { - mClCalls.abortCache(mClHand, mCacheHand); - mCacheHand = 0; - } - if(mNotBefore) { - CFRelease(mNotBefore); - mNotBefore = NULL; - } - if(mNotAfter) { - CFRelease(mNotAfter); - mNotAfter = NULL; - } -} - -/* fetch arbitrary field from cached cert */ -CSSM_RETURN TPClItemInfo::fetchField( - const CSSM_OID *fieldOid, - CSSM_DATA_PTR *fieldData) // mallocd by CL and RETURNED -{ - CSSM_RETURN crtn; - - uint32 NumberOfFields = 0; - CSSM_HANDLE resultHand = 0; - *fieldData = NULL; - - assert(mClCalls.getField != NULL); - assert(mCacheHand != 0); - crtn = mClCalls.getField( - mClHand, - mCacheHand, - fieldOid, - &resultHand, - &NumberOfFields, - fieldData); - if(crtn) { - return crtn; - } - if(NumberOfFields != 1) { - tpCertInfoDbg("TPClItemInfo::fetchField: numFields %d, expected 1\n", - (int)NumberOfFields); - } - mClCalls.abortQuery(mClHand, resultHand); - return CSSM_OK; -} - -/* free arbitrary field obtained from fetchField() */ -CSSM_RETURN TPClItemInfo::freeField( - const CSSM_OID *fieldOid, - CSSM_DATA_PTR fieldData) -{ - return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData); - -} - -/* - * Verify with an issuer cert - works on certs and CRLs. - * Issuer/subject name match already performed by caller. - * Optional paramCert is used to provide parameters when issuer - * has a partial public key. - */ -CSSM_RETURN TPClItemInfo::verifyWithIssuer( - TPCertInfo *issuerCert, - TPCertInfo *paramCert /* = NULL */) const -{ - CSSM_RETURN crtn; - - assert(mClHand != 0); - assert(issuerCert->isIssuerOf(*this)); - assert(mCspHand != 0); - - /* - * Special case: detect partial public key right now; don't even - * bother trying the cert verify in that case. - */ - if(issuerCert->hasPartialKey() && (paramCert == NULL)) { - /* caller deals with this later */ - tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE"); - return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE; - } - - CSSM_CC_HANDLE ccHand; - crtn = CSSM_CSP_CreateSignatureContext(mCspHand, - mSigAlg, - NULL, // Access Creds - issuerCert->pubKey(), - &ccHand); - if(crtn != CSSM_OK) { - tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n"); - CssmError::throwMe(crtn); - } - if(paramCert != NULL) { - assert(issuerCert->hasPartialKey()); - - /* add in parameter-bearing key */ - CSSM_CONTEXT_ATTRIBUTE newAttr; - - newAttr.AttributeType = CSSM_ATTRIBUTE_PARAM_KEY; - newAttr.AttributeLength = sizeof(CSSM_KEY); - newAttr.Attribute.Key = paramCert->pubKey(); - crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr); - if(crtn) { - tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n"); - CssmError::throwMe(crtn); - } - } - crtn = mClCalls.itemVerify(mClHand, - ccHand, - mItemData, - NULL, // issuer cert - NULL, // VerifyScope - 0); // ScopeSize - - switch(crtn) { - case CSSM_OK: // success - case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles - tpVfyDebug("verifyWithIssuer GOOD"); - break; - default: - /* all others appear here as general cert verify error */ - crtn = CSSMERR_TP_VERIFICATION_FAILURE; - tpVfyDebug("verifyWithIssuer BAD"); - break; - } - CSSM_DeleteContext(ccHand); - return crtn; -} - -CSSM_RETURN TPClItemInfo::cacheItem( - const CSSM_DATA *itemData, - TPItemCopy copyItemData) -{ - switch(copyItemData) { - case TIC_NoCopy: - mItemData = const_cast(itemData); - break; - case TIC_CopyData: - mItemData = tpMallocCopyCssmData(Allocator::standard(), itemData); - mWeOwnTheData = true; - break; - default: - assert(0); - CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); - } - - /* cache the cert/CRL in the CL */ - return mClCalls.cacheItem(mClHand, mItemData, &mCacheHand); -} - -/* - * Calculate not before/after times as struct tm. Only throws on - * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.). - * - * Only differences between Cert and CRL flavors of this are the - * OIDs used to fetch the appropriate before/after times, both of - * which are expressed as CSSM_X509_TIME structs for both Certs - * and CRLS. - */ -void TPClItemInfo::fetchNotBeforeAfter() -{ - CSSM_DATA_PTR notBeforeField = NULL; - CSSM_DATA_PTR notAfterField = NULL; - CSSM_RETURN crtn = CSSM_OK; - CSSM_X509_TIME *xTime; - - assert(cacheHand() != CSSM_INVALID_HANDLE); - crtn = fetchField(mClCalls.notBeforeOid, ¬BeforeField); - if(crtn) { - tpErrorLog("fetchNotBeforeAfter: GetField error\n"); - CssmError::throwMe(mClCalls.invalidItemRtn); - } - - /* subsequent errors to errOut */ - xTime = (CSSM_X509_TIME *)notBeforeField->Data; - if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotBefore)) { - tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n"); - crtn = mClCalls.invalidItemRtn; - goto errOut; - } - - crtn = fetchField(mClCalls.notAfterOid, ¬AfterField); - if(crtn) { - /* - * Tolerate a missing NextUpdate in CRL only - */ - if(mClCalls.notAfterOid == &CSSMOID_X509V1ValidityNotAfter) { - tpErrorLog("fetchNotBeforeAfter: GetField error\n"); - crtn = mClCalls.invalidItemRtn; - goto errOut; - } - else { - /* - * Fake NextUpdate to be "at the end of time" - */ - timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME, - strlen(CSSM_APPLE_CRL_END_OF_TIME), - &mNotAfter); - } - } - else { - xTime = (CSSM_X509_TIME *)notAfterField->Data; - if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotAfter)) { - tpErrorLog("fetchNotBeforeAfter: malformed notAfter time\n"); - crtn = mClCalls.invalidItemRtn; - goto errOut; - } - } - crtn = CSSM_OK; -errOut: - if(notAfterField) { - freeField(mClCalls.notAfterOid, notAfterField); - } - if(notBeforeField) { - freeField(mClCalls.notBeforeOid, notBeforeField); - } - if(crtn != CSSM_OK) { - CssmError::throwMe(crtn); - } -} - -/* - * Verify validity (not before/after) by comparing the reference - * time (verifyString if present, or "now" if NULL) to the - * not before/after fields fetched from the item at construction. - * - * Called implicitly at construction; can be called again any time - * to re-establish validity (e.g. after fetching an item from a cache). - * - * We use some stdlib time calls over in tpTime.c; the stdlib function - * gmtime() is not thread-safe, so we do the protection here. Note that - * this makes *our* calls to gmtime() thread-safe, but if the app has - * other threads which are also calling gmtime, we're out of luck. - */ -ModuleNexus tpTimeLock; - -CSSM_RETURN TPClItemInfo::calculateCurrent( - const char *verifyString) -{ - CFDateRef refTime = NULL; - - if(verifyString != NULL) { - /* caller specifies verification time base */ - if(timeStringToCfDate(verifyString, (unsigned)strlen(verifyString), &refTime)) { - tpErrorLog("calculateCurrent: timeStringToCfDate error\n"); - return CSSMERR_TP_INVALID_TIMESTRING; - } - } - else { - /* time base = right now */ - refTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); - } - if(compareTimes(refTime, mNotBefore) < 0) { - mIsNotValidYet = true; - tpTimeDbg("\nTP_CERT_NOT_VALID_YET: now %g notBefore %g", - CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore)); - CFRelease(refTime); - return mClCalls.notValidYetRtn; - } - else { - mIsNotValidYet = false; - } - - if(compareTimes(refTime, mNotAfter) > 0) { - mIsExpired = true; - tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g", - CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore)); - CFRelease(refTime); - return mClCalls.expiredRtn; - } - else { - mIsExpired = false; - CFRelease(refTime); - return CSSM_OK; - } -} - - -/* - * No default constructor - this is the only way. - * This caches the cert and fetches subjectName, issuerName, and - * mPublicKey to ensure the incoming certData is well-constructed. - */ -TPCertInfo::TPCertInfo( - CSSM_CL_HANDLE clHand, - CSSM_CSP_HANDLE cspHand, - const CSSM_DATA *certData, - TPItemCopy copyCertData, // true: we copy, we free - // false - caller owns - const char *verifyTime) // may be NULL - : - TPClItemInfo(clHand, cspHand, tpCertClCalls, certData, - copyCertData, verifyTime), - mSubjectName(NULL), - mPublicKeyData(NULL), - mPublicKey(NULL), - mIsAnchor(false), - mIsFromInputCerts(false), - mIsFromNet(false), - mNumStatusCodes(0), - mStatusCodes(NULL), - mUniqueRecord(NULL), - mUsed(false), - mIsLeaf(false), - mIsRoot(TRS_Unknown), - mRevCheckGood(false), - mRevCheckComplete(false), - mTrustSettingsEvaluated(false), - mTrustSettingsDomain(kSecTrustSettingsDomainSystem), - mTrustSettingsResult(kSecTrustSettingsResultInvalid), - mTrustSettingsFoundAnyEntry(false), - mTrustSettingsFoundMatchingEntry(false), - mAllowedErrs(NULL), - mNumAllowedErrs(0), - mIgnoredError(false), - mTrustSettingsKeyUsage(0), - mCertHashStr(NULL) -{ - CSSM_RETURN crtn; - - tpCertInfoDbg("TPCertInfo construct this %p", this); - mDlDbHandle.DLHandle = 0; - mDlDbHandle.DBHandle = 0; - - /* fetch subject name */ - crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName); - if(crtn) { - /* bad cert */ - releaseResources(); - CssmError::throwMe(crtn); - } - - /* this cert's public key */ - crtn = fetchField(&CSSMOID_CSSMKeyStruct, &mPublicKeyData); - if(crtn || (mPublicKeyData->Length != sizeof(CSSM_KEY))) { - /* bad cert */ - releaseResources(); - CssmError::throwMe(crtn); - } - mPublicKey = (CSSM_KEY_PTR)mPublicKeyData->Data; - - /* calculate other commonly used fields */ - if(tpCompareCssmData(mSubjectName, issuerName())) { - /* - * Per Radar 3374978, perform complete signature verification - * lazily - just check subject/issuer match here. - */ - tpAnchorDebug("TPCertInfo potential anchor"); - mIsRoot = TRS_NamesMatch; - } - else { - mIsRoot = TRS_NotRoot; - } -} - -/* frees mSubjectName, mIssuerName, mCacheHand via mClHand */ -TPCertInfo::~TPCertInfo() -{ - tpCertInfoDbg("TPCertInfo destruct this %p", this); - releaseResources(); -} - -void TPCertInfo::releaseResources() -{ - if(mSubjectName) { - freeField(&CSSMOID_X509V1SubjectName, mSubjectName); - mSubjectName = NULL; - } - if(mPublicKeyData) { - freeField(&CSSMOID_CSSMKeyStruct, mPublicKeyData); - mPublicKey = NULL; - mPublicKeyData = NULL; - } - if(mStatusCodes) { - free(mStatusCodes); - mStatusCodes = NULL; - } - if(mAllowedErrs) { - free(mAllowedErrs); - } - if(mCertHashStr) { - CFRelease(mCertHashStr); - } - TPClItemInfo::releaseResources(); -} - -const CSSM_DATA *TPCertInfo::subjectName() -{ - assert(mSubjectName != NULL); - return mSubjectName; -} - -/* - * Perform semi-lazy evaluation of "rootness". Subject and issuer names - * compared at constructor. - * If avoidVerify is true, we won't do the signature verify: caller - * just wants to know if the subject and issuer names match. - */ -bool TPCertInfo::isSelfSigned(bool avoidVerify) -{ - switch(mIsRoot) { - case TRS_NotRoot: // known not to be root - return false; - case TRS_IsRoot: - return true; - case TRS_NamesMatch: - if(avoidVerify) { - return true; - } - /* else drop through and verify */ - case TRS_Unknown: // actually shouldn't happen, but to be safe... - default: - /* do the signature verify */ - if(verifyWithIssuer(this) == CSSM_OK) { - tpAnchorDebug("isSelfSigned anchor verified"); - mIsRoot = TRS_IsRoot; - return true; - } - else { - tpAnchorDebug("isSelfSigned anchor vfy FAIL"); - mIsRoot = TRS_NotRoot; - return false; - } - } -} - -/* - * Am I the issuer of the specified subject item? Returns true if so. - * Works for subject certs as well as CRLs. - */ -bool TPCertInfo::isIssuerOf( - const TPClItemInfo &subject) -{ - assert(mSubjectName != NULL); - assert(subject.issuerName() != NULL); - if(tpCompareCssmData(mSubjectName, subject.issuerName())) { - return true; - } - else { - return false; - } -} - -/* - * Does my subjectKeyID match the authorityKeyID of the specified subject? - * Returns true if so (and if both fields are available). - */ -bool TPCertInfo::isAuthorityKeyOf( - const TPClItemInfo &subject) -{ - const CSSM_DATA *subjectKeyID = this->subjectKeyID(); - const CSSM_DATA *authorityKeyID = subject.authorityKeyID(); - if(!subjectKeyID || !authorityKeyID) { - tpDebug("isAuthorityKeyOf FALSE (one or both key ids missing)"); - return false; - } - CSSM_X509_EXTENSION *ske = (CSSM_X509_EXTENSION *)subjectKeyID->Data; - CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authorityKeyID->Data; - if( !ske || ske->format != CSSM_X509_DATAFORMAT_PARSED || - !ake || ake->format != CSSM_X509_DATAFORMAT_PARSED || - !ske->value.parsedValue || !ake->value.parsedValue) { - tpDebug("isAuthorityKeyOf FALSE (no parsed value present)"); - return false; - } - - const CE_SubjectKeyID *skid = (CE_SubjectKeyID *)ske->value.parsedValue; - const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue; - - if(!akid->keyIdentifierPresent) { - tpDebug("isAuthorityKeyOf FALSE (no key identifier present)"); - return false; - } - if(tpCompareCssmData(skid, &akid->keyIdentifier)) { - #ifndef NDEBUG - tpDebug("isAuthorityKeyOf TRUE (len:s=%lu/a=%lu, %08lX../%08lX..)", - skid->Length, - akid->keyIdentifier.Length, - (skid->Data) ? *((unsigned long *)skid->Data) : 0L, - (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L); - #endif - return true; - } - else { - #ifndef NDEBUG - tpDebug("isAuthorityKeyOf FALSE (len:s=%lu/a=%lu, %08lX../%08lX..)", - skid->Length, - akid->keyIdentifier.Length, - (skid->Data) ? *((unsigned long *)skid->Data) : 0L, - (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L); - #endif - return false; - } -} - -bool TPCertInfo::addStatusCode(CSSM_RETURN code) -{ - mNumStatusCodes++; - mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes, - mNumStatusCodes * sizeof(CSSM_RETURN)); - mStatusCodes[mNumStatusCodes - 1] = code; - return isStatusFatal(code); -} - -bool TPCertInfo::hasStatusCode(CSSM_RETURN code) -{ - for(unsigned dex=0; dexKeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) { - return true; - } - else { - return false; - } -} - -/* - * - */ -bool TPCertInfo::shouldReject() -{ - static unsigned char _UTN_UF_H_ISSUER_BYTES[154] = { - 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, - 0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, - 0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, - 0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, - 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, - 0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, - 0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, - 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, - 0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, - 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, - 0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45 - }; - CSSM_DATA _UTN_UF_H_ISSUER = { sizeof(_UTN_UF_H_ISSUER_BYTES), _UTN_UF_H_ISSUER_BYTES }; - - static CSSM_DATA _UTN_UF_H_SERIALS[] = { - { 17, (uint8*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, // amo - { 17, (uint8*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, // gt - { 17, (uint8*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, // llc - { 17, (uint8*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, // lsc - { 17, (uint8*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, // lyc - { 16, (uint8*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" }, // lyc1 - { 16, (uint8*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" }, // lyc2 - { 16, (uint8*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" }, // mgc - { 17, (uint8*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, // wgc - { 0, NULL } - }; - - const CSSM_DATA *issuer=issuerName(); - if(!issuer || !(tpCompareCssmData(issuer, &_UTN_UF_H_ISSUER))) - return false; - - CSSM_DATA *serialNumber=NULL; - CSSM_RETURN crtn = fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber); - if(crtn || !serialNumber) - return false; - - CSSM_DATA *p=_UTN_UF_H_SERIALS; - bool matched=false; - while(p->Length) { - if(tpCompareCssmData(serialNumber, p)) { - matched=true; - addStatusCode(CSSMERR_TP_CERT_REVOKED); - break; - } - ++p; - } - freeField(&CSSMOID_X509V1SerialNumber, serialNumber); - return matched; -} - -/* - * Evaluate trust settings; returns true in *foundMatchingEntry if positive - * match found - i.e., cert chain construction is done. - */ -OSStatus TPCertInfo::evaluateTrustSettings( - const CSSM_OID &policyOid, - const char *policyString, // optional - uint32 policyStringLen, - SecTrustSettingsKeyUsage keyUse, // required - bool *foundMatchingEntry, // RETURNED - bool *foundAnyEntry) // RETURNED -{ - /* - * We might have to force a re-evaluation if the requested key usage - * is not a subset of what we already checked for (and cached). - */ - if(mTrustSettingsEvaluated) { - bool doFlush = false; - if(mTrustSettingsKeyUsage != kSecTrustSettingsKeyUseAny) { - if(keyUse == kSecTrustSettingsKeyUseAny) { - /* now want "any", checked something else before */ - doFlush = true; - } - else if((keyUse & mTrustSettingsKeyUsage) != keyUse) { - /* want bits that we didn't ask for before */ - doFlush = true; - } - } - if(doFlush) { - tpTrustSettingsDbg("evaluateTrustSettings: flushing cached trust for " - "%p due to keyUse 0x%x", this, (int)keyUse); - mTrustSettingsEvaluated = false; - mTrustSettingsFoundAnyEntry = false; - mTrustSettingsResult = kSecTrustSettingsResultInvalid; - mTrustSettingsFoundMatchingEntry = false; - if(mAllowedErrs != NULL) { - free(mAllowedErrs); - } - mNumAllowedErrs = 0; - } - /* else we can safely use the cached values */ - } - if(!mTrustSettingsEvaluated) { - - if(mCertHashStr == NULL) { - const CSSM_DATA *certData = itemData(); - mCertHashStr = SecTrustSettingsCertHashStrFromData(certData->Data, - certData->Length); - } - - OSStatus ortn = SecTrustSettingsEvaluateCert(mCertHashStr, - &policyOid, - policyString, - policyStringLen, - keyUse, - /* - * This is the purpose of the avoidVerify option, right here. - * If this is a root cert and it has trust settings, we avoid - * the signature verify. If it turns out there are no trust - * settings and this is a root, we'll verify the signature - * elsewhere (e.g. post_trust_setting: in buildCertGroup()). - */ - isSelfSigned(true), - &mTrustSettingsDomain, - &mAllowedErrs, - &mNumAllowedErrs, - &mTrustSettingsResult, - &mTrustSettingsFoundMatchingEntry, - &mTrustSettingsFoundAnyEntry); - if(ortn) { - tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!"); - return ortn; - } - mTrustSettingsEvaluated = true; - mTrustSettingsKeyUsage = keyUse; - #ifndef NDEBUG - if(mTrustSettingsFoundMatchingEntry) { - tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d", - this, (int)mTrustSettingsResult); - } - #endif - /* one more thing... */ - if(shouldReject()) { - return CSSMERR_TP_INVALID_CERTIFICATE; - } - } - *foundMatchingEntry = mTrustSettingsFoundMatchingEntry; - *foundAnyEntry = mTrustSettingsFoundAnyEntry; - - return errSecSuccess; -} - -/* true means "verification terminated due to user trust setting" */ -bool TPCertInfo::trustSettingsFound() -{ - switch(mTrustSettingsResult) { - case kSecTrustSettingsResultUnspecified: /* entry but not definitive */ - case kSecTrustSettingsResultInvalid: /* no entry */ - return false; - default: - return true; - } -} - -/* - * Determine if this has an empty SubjectName field. Returns true if so. - */ -bool TPCertInfo::hasEmptySubjectName() -{ - /* - * A "pure" empty subject is two bytes (0x30 00) - constructed sequence, - * short form length, length 0. We'll be robust and tolerate a missing - * field, as well as a possible BER-encoded subject with some extra cruft. - */ - if((mSubjectName == NULL) || (mSubjectName->Length <= 4)) { - return true; - } - else { - return false; - } -} - -/* - * Free mUniqueRecord if it exists. - * This is *not* done in our destructor because this record sometimes - * has to persist in the form of a CSSM evidence chain. - */ -void TPCertInfo::freeUniqueRecord() -{ - if(mUniqueRecord == NULL) { - return; - } - tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord); - CSSM_DL_FreeUniqueRecord(mDlDbHandle, mUniqueRecord); -} - -/*** - *** TPCertGroup class - ***/ - -/* build empty group */ -TPCertGroup::TPCertGroup( - Allocator &alloc, - TPGroupOwner whoOwns) : - mAlloc(alloc), - mCertInfo(NULL), - mNumCerts(0), - mSizeofCertInfo(0), - mWhoOwns(whoOwns) -{ - tpCertInfoDbg("TPCertGroup simple construct this %p", this); - /* nothing for now */ -} - -/* - * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting - * TPCertInfos are more or less in the same order as the incoming - * certs, though incoming certs are discarded if they don't parse. - * No verification of any sort is performed. - */ -TPCertGroup::TPCertGroup( - const CSSM_CERTGROUP &CertGroupFrag, - CSSM_CL_HANDLE clHand, - CSSM_CSP_HANDLE cspHand, - Allocator &alloc, - const char *verifyTime, // may be NULL - bool firstCertMustBeValid, - TPGroupOwner whoOwns) : - mAlloc(alloc), - mCertInfo(NULL), - mNumCerts(0), - mSizeofCertInfo(0), - mWhoOwns(whoOwns) -{ - tpCertInfoDbg("TPCertGroup hard construct this %p", this); - - /* verify input args */ - if(cspHand == CSSM_INVALID_HANDLE) { - CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE); - } - if(clHand == CSSM_INVALID_HANDLE) { - CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE); - } - if(firstCertMustBeValid) { - if( (CertGroupFrag.NumCerts == 0) || - (CertGroupFrag.GroupList.CertList[0].Data == NULL) || - (CertGroupFrag.GroupList.CertList[0].Length == 0)) { - CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE); - } - } - if(CertGroupFrag.CertGroupType != CSSM_CERTGROUP_DATA) { - CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP); - } - switch(CertGroupFrag.CertType) { - case CSSM_CERT_X_509v1: - case CSSM_CERT_X_509v2: - case CSSM_CERT_X_509v3: - break; - default: - CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); - } - switch(CertGroupFrag.CertEncoding) { - case CSSM_CERT_ENCODING_BER: - case CSSM_CERT_ENCODING_DER: - break; - default: - CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); - } - - /* - * Add remaining input certs to mCertInfo. - */ - TPCertInfo *certInfo = NULL; - for(unsigned certDex=0; certDexindex(certDex); - appendCert(certInfo); - } -} - -/* - * Deletes contents of mCertInfo[] if appropriate. - */ -TPCertGroup::~TPCertGroup() -{ - if(mWhoOwns == TGO_Group) { - unsigned i; - for(i=0; i here, but - * gdb is so lame that it doesn't even let one examine the contents - * of an array<> (or just about anything else in the STL). I prefer - * debuggability over saving a few lines of trivial code. - */ -void TPCertGroup::appendCert( - TPCertInfo *certInfo) // appends to end of mCertInfo -{ - if(mNumCerts == mSizeofCertInfo) { - if(mSizeofCertInfo == 0) { - /* appending to empty array */ - mSizeofCertInfo = 1; - } - else { - mSizeofCertInfo *= 2; - } - mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo, - mSizeofCertInfo * sizeof(TPCertInfo *)); - } - mCertInfo[mNumCerts++] = certInfo; -} - -TPCertInfo *TPCertGroup::certAtIndex( - unsigned index) -{ - if(index > (mNumCerts - 1)) { - CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); - } - return mCertInfo[index]; -} - -TPCertInfo *TPCertGroup::removeCertAtIndex( - unsigned index) // doesn't delete the cert, just - // removes it from out list -{ - if(index > (mNumCerts - 1)) { - CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); - } - TPCertInfo *rtn = mCertInfo[index]; - - /* removed requested element and compact remaining array */ - unsigned i; - for(i=index; i<(mNumCerts - 1); i++) { - mCertInfo[i] = mCertInfo[i+1]; - } - mNumCerts--; - return rtn; -} - -TPCertInfo *TPCertGroup::firstCert() -{ - if(mNumCerts == 0) { - /* the caller really should not do this... */ - CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); - } - else { - return mCertInfo[0]; - } -} - -TPCertInfo *TPCertGroup::lastCert() -{ - if(mNumCerts == 0) { - return NULL; - } - else { - return mCertInfo[mNumCerts - 1]; - } -} - -/* build a CSSM_CERTGROUP corresponding with our mCertInfo */ -CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup() -{ - CSSM_CERTGROUP_PTR cgrp = - (CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP)); - cgrp->NumCerts = mNumCerts; - cgrp->CertGroupType = CSSM_CERTGROUP_DATA; - cgrp->CertType = CSSM_CERT_X_509v3; - cgrp->CertEncoding = CSSM_CERT_ENCODING_DER; - if(mNumCerts == 0) { - /* legal */ - cgrp->GroupList.CertList = NULL; - return cgrp; - } - cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts, - sizeof(CSSM_DATA)); - for(unsigned i=0; iitemData(), - &cgrp->GroupList.CertList[i]); - } - return cgrp; -} - -/* build a CSSM_TP_APPLE_EVIDENCE_INFO array */ -CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo() -{ - CSSM_TP_APPLE_EVIDENCE_INFO *infoArray; - - infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)mAlloc.calloc(mNumCerts, - sizeof(CSSM_TP_APPLE_EVIDENCE_INFO)); - for(unsigned i=0; iisExpired()) { - evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED; - } - if(certInfo->isNotValidYet()) { - evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET; - } - if(certInfo->isAnchor()) { - tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS"); - evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS; - } - if(certInfo->dlDbHandle().DLHandle == 0) { - if(certInfo->isFromNet()) { - evInfo->StatusBits |= CSSM_CERT_STATUS_IS_FROM_NET; - } - else if(certInfo->isFromInputCerts()) { - evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS; - } - } - /* If trust settings apply to a root, skip verifying the signature */ - bool avoidVerify = false; - switch(certInfo->trustSettingsResult()) { - case kSecTrustSettingsResultTrustRoot: - case kSecTrustSettingsResultTrustAsRoot: - /* these two can be disambiguated by IS_ROOT */ - evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST; - avoidVerify = true; - break; - case kSecTrustSettingsResultDeny: - evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY; - avoidVerify = true; - break; - case kSecTrustSettingsResultUnspecified: - case kSecTrustSettingsResultInvalid: - default: - break; - } - if(certInfo->isSelfSigned(avoidVerify)) { - evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT; - } - if(certInfo->ignoredError()) { - evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR; - } - unsigned numCodes = certInfo->numStatusCodes(); - if(numCodes) { - evInfo->NumStatusCodes = numCodes; - evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes, - sizeof(CSSM_RETURN)); - for(unsigned j=0; jStatusCodes[j] = (certInfo->statusCodes())[j]; - } - } - if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST | - CSSM_CERT_STATUS_TRUST_SETTINGS_DENY | - CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) { - /* Something noteworthy happened involving TrustSettings */ - uint32 whichDomain = 0; - switch(certInfo->trustSettingsDomain()) { - 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; - } - evInfo->Index = certInfo->index(); - evInfo->DlDbHandle = certInfo->dlDbHandle(); - evInfo->UniqueRecord = certInfo->uniqueRecord(); - } - return infoArray; -} - -/* Given a status for basic construction of a cert group and a status - * of (optional) policy verification, plus the implicit notBefore/notAfter - * status in the certs, calculate a global return code. This just - * encapsulates a policy for CertGroupConstruct and CertGroupVerify. - */ -CSSM_RETURN TPCertGroup::getReturnCode( - CSSM_RETURN constructStatus, - CSSM_RETURN policyStatus, - CSSM_APPLE_TP_ACTION_FLAGS actionFlags) -{ - if(constructStatus) { - /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */ - return constructStatus; - } - - bool expired = false; - bool postdated = false; - bool allowExpiredRoot = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) ? - true : false; - bool allowExpired = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED) ? true : false; - bool allowPostdated = allowExpired; // flag overrides any temporal invalidity - bool requireRevPerCert = (actionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT) ? - true : false; - - /* check for expired, not valid yet */ - for(unsigned i=0; iisExpired() && - !(allowExpiredRoot && ci->isSelfSigned(true)) && // allowed globally - ci->isStatusFatal(CSSMERR_TP_CERT_EXPIRED)) { // allowed for this cert - expired = true; - } - if(ci->isNotValidYet() && - ci->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET)) { - postdated = true; - } - } - if(expired && !allowExpired) { - return CSSMERR_TP_CERT_EXPIRED; - } - if(postdated && !allowPostdated) { - return CSSMERR_TP_CERT_NOT_VALID_YET; - } - - /* Check for missing revocation check */ - if(requireRevPerCert) { - for(unsigned i=0; iisSelfSigned(true)) { - /* revocation check meaningless for a root cert */ - tpDebug("getReturnCode: ignoring revocation for self-signed cert %d", i); - continue; - } - if(!ci->revokeCheckGood() && - ci->isStatusFatal(CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK)) { - tpDebug("getReturnCode: FATAL: revocation check incomplete for cert %d", i); - return CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK; - } - #ifndef NDEBUG - else { - tpDebug("getReturnCode: revocation check %s for cert %d", - (ci->revokeCheckGood()) ? "GOOD" : "OK", i); - } - #endif - } - } - return policyStatus; -} - -/* set all TPCertInfo.mUsed flags false */ -void TPCertGroup::setAllUnused() -{ - for(unsigned dex=0; dexused(false); - } -} - -/* - * See if the specified error status is allowed (return true) or - * fatal (return false) per each cert's mAllowedErrs[]. Returns - * true if any cert returns false for its isStatusFatal() call. - * The list of errors which can apply to cert-chain-wide allowedErrors - * is right here; if the incoming error is not in that list, we - * return false. If the incoming error code is CSSM_OK we return - * true as a convenience for our callers. - */ -bool TPCertGroup::isAllowedError( - CSSM_RETURN code) -{ - switch(code) { - case CSSM_OK: - return true; - case CSSMERR_TP_NOT_TRUSTED: - case CSSMERR_TP_INVALID_ANCHOR_CERT: - case CSSMERR_TP_VERIFY_ACTION_FAILED: - case CSSMERR_TP_INVALID_CERT_AUTHORITY: - case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH: - case CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH: - /* continue processing these candidates */ - break; - default: - /* not a candidate for cert-chain-wide allowedErrors */ - return false; - } - - for(unsigned dex=0; dexisStatusFatal(code)) { - tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u", - dex); - return true; - } - } - - /* every cert thought this was fatal; it is. */ - return false; -} - -/* - * Determine if we already have the specified cert in this group. - */ -bool TPCertGroup::isInGroup(TPCertInfo &certInfo) -{ - for(unsigned dex=0; dexitemData())) { - return true; - } - } - return false; -} - -/* - * Encode issuing certs in this group as a PEM-encoded data blob. - * Caller must free. - */ -void TPCertGroup::encodeIssuers(CSSM_DATA &issuers) -{ - /* FIXME: probably want to rewrite this using pemEncode() from libsecurity_cdsa_utils, - * since use of Sec* APIs from this layer violates the API reentrancy contract. - */ - issuers.Data = NULL; - issuers.Length = 0; - CFMutableArrayRef certArray = CFArrayCreateMutable(kCFAllocatorDefault, - 0, &kCFTypeArrayCallBacks); - if(!certArray) { - return; - } - for(unsigned certDex=0; certDex 1) { - continue; /* don't need the leaf */ - } - CSSM_DATA *cssmData = (CSSM_DATA*)((certInfo) ? certInfo->itemData() : NULL); - if(!cssmData || !cssmData->Data || !cssmData->Length) { - continue; - } - CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, - (const UInt8 *)cssmData->Data, cssmData->Length, - kCFAllocatorNull); - if(!dataRef) { - continue; - } - SecCertificateRef certRef = SecCertificateCreateWithData(kCFAllocatorDefault, - dataRef); - if(!certRef) { - CFRelease(dataRef); - continue; - } - CFArrayAppendValue(certArray, certRef); - CFRelease(certRef); - CFRelease(dataRef); - } - CFDataRef exportedPEMData = NULL; - OSStatus status = SecItemExport(certArray, - kSecFormatPEMSequence, - kSecItemPemArmour, - NULL, - &exportedPEMData); - CFRelease(certArray); - - if(!status) { - uint8 *dataPtr = (uint8*)CFDataGetBytePtr(exportedPEMData); - size_t dataLen = CFDataGetLength(exportedPEMData); - issuers.Data = (uint8*)malloc(dataLen); - memmove(issuers.Data, dataPtr, dataLen); - issuers.Length = dataLen; - CFRelease(exportedPEMData); - } -} - -/* - * Search unused incoming certs to find an issuer of specified cert or CRL. - * WARNING this assumes a valid "used" state for all certs in this group. - * If partialIssuerKey is true on return, caller must re-verify signature - * of subject later when sufficient info is available. - */ -TPCertInfo *TPCertGroup::findIssuerForCertOrCrl( - const TPClItemInfo &subject, - bool &partialIssuerKey) -{ - partialIssuerKey = false; - TPCertInfo *expiredIssuer = NULL; - TPCertInfo *unmatchedKeyIDIssuer = NULL; - - for(unsigned certDex=0; certDexused()) { - continue; - } - - /* subject/issuer names match? */ - if(certInfo->isIssuerOf(subject)) { - /* yep, do a sig verify */ - tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig"); - CSSM_RETURN crtn = subject.verifyWithIssuer(certInfo); - switch(crtn) { - case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: - /* issuer OK, check sig later */ - partialIssuerKey = true; - /* and fall thru */ - case CSSM_OK: - /* - * Temporal validity check: if we're not already holding an expired - * issuer, and this one's invalid, hold it and keep going. - */ - if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { - if(certInfo->isExpired() || certInfo->isNotValidYet()) { - tpDebug("findIssuerForCertOrCrl: holding expired cert %p", - certInfo); - expiredIssuer = certInfo; - break; - } - } - /* Authority key identifier check: if we can't match subject key id, - * hold onto this cert and keep going. - */ - if(unmatchedKeyIDIssuer == NULL) { - if(!certInfo->isAuthorityKeyOf(subject)) { - tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p", - certInfo); - unmatchedKeyIDIssuer = certInfo; - break; - } - } - /* YES */ - certInfo->used(true); - return certInfo; - default: - /* just skip this one and keep looking */ - tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG"); - break; - } - } /* names match */ - } - if(unmatchedKeyIDIssuer != NULL) { - /* OK, we'll use this one (preferred over an expired issuer) */ - tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer); - unmatchedKeyIDIssuer->used(true); - return unmatchedKeyIDIssuer; - } - if(expiredIssuer != NULL) { - /* OK, we'll use this one */ - tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer); - expiredIssuer->used(true); - return expiredIssuer; - } - - /* not found */ - return NULL; -} - -/* - * Construct ordered, verified cert chain from a variety of inputs. - * Time validity does not affect the function return or any status, - * we always try to find a valid cert to replace an expired or - * not-yet-valid cert if we can. Final temporal validity of each - * cert must be checked by caller (it's stored in each TPCertInfo - * we add to ourself during construction). - * - * Only possible error returns are: - * CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial - * public key, rendering full verification impossible. - * CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial - * public key and which failed to perform subsequent signature - * verification. - * - * Other interesting status is returned via the verifiedToRoot and - * verifiedToAnchor flags. - * - * NOTE: is it the caller's responsibility to call setAllUnused() for both - * incoming cert groups (inCertGroup and gatheredCerts). We don't do that - * here because we may call ourself recursively. - */ -CSSM_RETURN TPCertGroup::buildCertGroup( - const TPClItemInfo &subjectItem, // Cert or CRL - TPCertGroup *inCertGroup, // optional - const CSSM_DL_DB_LIST *dbList, // optional - CSSM_CL_HANDLE clHand, - CSSM_CSP_HANDLE cspHand, - const char *verifyTime, // optional, for establishing - // validity of new TPCertInfos - /* trusted anchors, optional */ - /* FIXME - maybe this should be a TPCertGroup */ - uint32 numAnchorCerts, - const CSSM_DATA *anchorCerts, - - /* - * Certs to be freed by caller (i.e., TPCertInfo which we allocate - * as a result of using a cert from anchorCerts or dbList) are added - * to this group. - */ - TPCertGroup &certsToBeFreed, - - /* - * Other certificates gathered during the course of this operation, - * currently consisting of certs fetched from DBs and from the net. - * This is not used when called by AppleTPSession::CertGroupConstructPriv; - * it's an optimization for the case when we're building a cert group - * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from - * the net which are needed to verify both the subject cert and a CRL. - * We don't modify this TPCertGroup, we only use certs from it. - */ - TPCertGroup *gatheredCerts, - - /* - * Indicates that subjectItem is a cert in this cert group. - * If true, that cert will be tested for "root-ness", including - * -- subject/issuer compare - * -- signature self-verify - * -- anchor compare - */ - CSSM_BOOL subjectIsInGroup, - - /* - * CSSM_TP_ACTION_FETCH_CERT_FROM_NET, - * CSSM_TP_ACTION_TRUST_SETTING, - * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting - */ - CSSM_APPLE_TP_ACTION_FLAGS actionFlags, - - /* CSSM_TP_ACTION_TRUST_SETTING parameters */ - const CSSM_OID *policyOid, - const char *policyStr, - uint32 policyStrLen, - SecTrustSettingsKeyUsage leafKeyUse, // usage of *first* cert in chain - - /* returned */ - CSSM_BOOL &verifiedToRoot, // end of chain self-verifies - CSSM_BOOL &verifiedToAnchor, // end of chain in anchors - CSSM_BOOL &verifiedViaTrustSettings) // chain ends per User Trust setting -{ - const TPClItemInfo *thisSubject = &subjectItem; - CSSM_RETURN crtn = CSSM_OK; - TPCertInfo *issuerCert = NULL; - unsigned certDex; - TPCertInfo *anchorInfo = NULL; - bool foundPartialIssuer = false; - bool attemptNetworkFetch = false; - CSSM_BOOL firstSubjectIsInGroup = subjectIsInGroup; - TPCertInfo *endCert; - - tpVfyDebug("buildCertGroup top"); - - /* possible expired root which we'll only use if we can't find - * a better one */ - TPCertInfo *expiredRoot = NULL; - - /* and the general case of an expired or not yet valid cert */ - TPCertInfo *expiredIssuer = NULL; - - /* and the case of an issuer without a matching subject key id */ - TPCertInfo *unmatchedKeyIDIssuer = NULL; - - /* and the case of a root that isn't trusted or an anchor */ - TPCertInfo *untrustedRoot = NULL; - - verifiedToRoot = CSSM_FALSE; - verifiedToAnchor = CSSM_FALSE; - verifiedViaTrustSettings = CSSM_FALSE; - - /*** main loop to seach inCertGroup and dbList *** - * - * Exit loop on: - * -- find a root cert in the chain (self-signed) - * -- find a non-root cert which is also in the anchors list - * -- find a cert which is trusted per Trust Settings (if enabled) - * -- memory error - * -- or no more certs to add to chain. - */ - for(;;) { - /* - * Top of loop: thisSubject is the item we're trying to verify. - */ - - /* is thisSubject a root cert or listed in user trust list? */ - if(subjectIsInGroup) { - TPCertInfo *subjCert = lastCert(); - assert(subjCert != NULL); - - if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) { - assert(policyOid != NULL); - - /* - * Figure out key usage. If this is a leaf cert, the caller - actually - * the per-policy code - inferred the usage. Else it could be for - * verifying a cert or a CRL. - * - * We want to avoid multiple calls to the effective portion of - * evaluateTrustSettings(), but a CA cert could be usable for only - * signing certs and not CRLs. Thus we're evaluating a CA cert, - * try to evaluate for signing certs *and* CRLs in case we come - * this way again later when performing CRL verification. If that - * fails, then retry with just cert signing. - */ - SecTrustSettingsKeyUsage localKeyUse; - bool doRetry = false; - if(subjCert == firstCert()) { - /* leaf - use caller's spec */ - localKeyUse = leafKeyUse; - /* FIXME - add in CRL if this is cert checking? */ - } - else { - localKeyUse = kSecTrustSettingsKeyUseSignCert | kSecTrustSettingsKeyUseSignRevocation; - /* and if necessary */ - doRetry = true; - } - /* this lets us avoid searching for the same thing twice when there - * is in fact no entry for it */ - bool foundEntry = false; - bool trustSettingsFound = false; - OSStatus ortn = subjCert->evaluateTrustSettings(*policyOid, - policyStr, policyStrLen, localKeyUse, &trustSettingsFound, &foundEntry); - if(ortn) { - /* this is only a dire error */ - crtn = ortn; - goto final_out; - } - if(!trustSettingsFound && foundEntry && doRetry) { - tpTrustSettingsDbg("buildCertGroup: retrying evaluateTrustSettings with Cert only"); - ortn = subjCert->evaluateTrustSettings(*policyOid, - policyStr, policyStrLen, kSecTrustSettingsKeyUseSignCert, - &trustSettingsFound, &foundEntry); - if(ortn) { - crtn = ortn; - goto final_out; - } - } - if(trustSettingsFound) { - switch(subjCert->trustSettingsResult()) { - case kSecTrustSettingsResultInvalid: - /* should not happen... */ - assert(0); - crtn = CSSMERR_TP_INTERNAL_ERROR; - break; - case kSecTrustSettingsResultTrustRoot: - case kSecTrustSettingsResultTrustAsRoot: - tpTrustSettingsDbg("Trust[As]Root found"); - crtn = CSSM_OK; - break; - case kSecTrustSettingsResultDeny: - tpTrustSettingsDbg("TrustResultDeny found"); - crtn = CSSMERR_APPLETP_TRUST_SETTING_DENY; - break; - case kSecTrustSettingsResultUnspecified: - /* special case here: this means "keep going, we don't trust or - * distrust this cert". Typically used to express allowed errors - * only. - */ - tpTrustSettingsDbg("TrustResultUnspecified found"); - goto post_trust_setting; - default: - tpTrustSettingsDbg("Unknown TrustResult (%d)", - (int)subjCert->trustSettingsResult()); - crtn = CSSMERR_TP_INTERNAL_ERROR; - break; - } - /* cleanup partial key processing */ - verifiedViaTrustSettings = CSSM_TRUE; - goto final_out; - } - } /* CSSM_TP_ACTION_TRUST_SETTING */ - -post_trust_setting: - /* - * If this cert is in the provided anchors list, - * we can stop building the chain at this point. - * - * If this cert is a leaf, the chain ends in an anchor, but if it's - * also temporally invalid, we can't do anything further. However, - * if it's not a leaf, then we need to roll back the chain to a - * point just before this cert, so Case 1 will subsequently find - * the anchor (and handle the anchor correctly if it's expired.) - */ - if(numAnchorCerts && anchorCerts) { - bool foundAnchor = false; - for(certDex=0; certDexitemData(), &anchorCerts[certDex])) { - foundAnchor = true; - /* if it's not the leaf, remove it from the outgoing cert group. */ - if(!firstSubjectIsInGroup || (mNumCerts > 1)) { - if(mNumCerts) { - /* roll back to previous cert */ - mNumCerts--; - } - if(mNumCerts == 0) { - /* roll back to caller's initial condition */ - thisSubject = &subjectItem; - } - else { - thisSubject = lastCert(); - } - tpAnchorDebug("buildCertGroup: CA cert in input AND anchors"); - } /* not leaf */ - else { - if(subjCert->isExpired() || subjCert->isNotValidYet()) { - crtn = CSSM_CERT_STATUS_EXPIRED; - } else { - crtn = CSSM_OK; - } - subjCert->isAnchor(true); - verifiedToAnchor = CSSM_TRUE; - tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors"); - } /* leaf */ - if(subjCert->isSelfSigned()) { - verifiedToRoot = CSSM_TRUE; - } - break; /* out of anchor-checking loop */ - } - } - if(foundAnchor) { - break; /* out of main loop */ - } - } - - if(subjCert->isSelfSigned()) { - /* - * Special case if this root is temporally invalid (and it's not - * the leaf): remove it from the outgoing cert group, save it, - * and proceed, looking another (good) root in anchors. - * There's no way we'll find another good one in this loop. - */ - if((subjCert->isExpired() || subjCert->isNotValidYet()) && - (!firstSubjectIsInGroup || (mNumCerts > 1))) { - verifiedToRoot = CSSM_TRUE; - tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert); - expiredRoot = subjCert; - if(mNumCerts) { - /* roll back to previous cert */ - mNumCerts--; - } - if(mNumCerts == 0) { - /* roll back to caller's initial condition */ - thisSubject = &subjectItem; - } - else { - thisSubject = lastCert(); - } - break; /* out of main loop */ - } - /* - * If any root is considered an anchor, we don't need to look - * for a better chain that ends in an anchor or trusted root. - */ - if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) { - verifiedToRoot = CSSM_TRUE; - break; /*out of main loop */ - } - /* - * The root we have is neither trusted nor an anchor. Continue in - * the loop to look for a better chain if this is not a leaf. - */ - if(!firstSubjectIsInGroup || (mNumCerts > 1)) { - tpDebug("buildCertGroup: UNTRUSTED ROOT %p, looking for better chain", subjCert); - untrustedRoot = subjCert; - if(mNumCerts) { - /* roll back to previous cert */ - mNumCerts--; - } - if(mNumCerts == 0) { - /* roll back to caller's initial condition */ - thisSubject = &subjectItem; - } - else { - thisSubject = lastCert(); - } - } - else { - /* the leaf is a root */ - break; /* out of main loop */ - } - } /* root */ - } /* subjectIsInGroup */ - - /* - * Search unused incoming certs to find an issuer. - * Both cert groups are optional. - * We'll add issuer to outCertGroup below. - * If we find a cert that's expired or not yet valid, we hold on to it - * and look for a better one. If we don't find it here we drop back to the - * expired one at the end of the loop. If that expired cert is a root - * cert, we'll use the expiredRoot mechanism (see above) to roll back and - * see if we can find a good root in the incoming anchors. - */ - if(inCertGroup != NULL) { - bool partial = false; - issuerCert = inCertGroup->findIssuerForCertOrCrl(*thisSubject, - partial); - if(issuerCert) { - issuerCert->isFromInputCerts(true); - if(partial) { - /* deal with this later */ - foundPartialIssuer = true; - tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup"); - } - else { - tpDebug("buildCertGroup: Cert FOUND in inCertGroup"); - } - } - } - - if(issuerCert != NULL) { - bool stashedIssuer = false; - /* Check whether candidate issuer is the same as unstrustedRoot */ - if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) { - /* already stashed */ - stashedIssuer = true; - } - /* Check whether candidate issuer is expired or not yet valid */ - if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { - if(expiredIssuer == NULL) { - tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert); - expiredIssuer = issuerCert; - stashedIssuer = true; - } - /* else we already have an expired issuer candidate */ - } - else { - /* unconditionally done with possible expiredIssuer */ - #ifndef NDEBUG - if(expiredIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer); - } - #endif - expiredIssuer = NULL; - } - /* Check whether candidate issuer failed to match authority key id in thisSubject */ - if(!issuerCert->isAuthorityKeyOf(*thisSubject)) { - if(unmatchedKeyIDIssuer == NULL) { - tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert); - unmatchedKeyIDIssuer = issuerCert; - stashedIssuer = true; - } - /* else we already have an unmatched key id issuer candidate */ - } - else { - /* unconditionally done with possible unmatchedKeyIDIssuer */ - #ifndef NDEBUG - if(unmatchedKeyIDIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer); - } - #endif - unmatchedKeyIDIssuer = NULL; - } - if(stashedIssuer) { - issuerCert = NULL; /* keep looking */ - } - } - - if((issuerCert == NULL) && (gatheredCerts != NULL)) { - bool partial = false; - issuerCert = gatheredCerts->findIssuerForCertOrCrl(*thisSubject, - partial); - if(issuerCert) { - if(partial) { - /* deal with this later */ - foundPartialIssuer = true; - tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts"); - } - else { - tpDebug("buildCertGroup: Cert FOUND in gatheredCerts"); - } - } - } - - if(issuerCert != NULL) { - bool stashedIssuer = false; - /* Check whether candidate issuer is the same as untrustedRoot */ - if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) { - /* already stashed */ - stashedIssuer = true; - } - /* Check whether candidate issuer is expired or not yet valid */ - if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { - if(expiredIssuer == NULL) { - tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert); - expiredIssuer = issuerCert; - stashedIssuer = true; - } - /* else we already have an expired issuer candidate */ - } - else { - /* unconditionally done with possible expiredIssuer */ - #ifndef NDEBUG - if(expiredIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer); - } - #endif - expiredIssuer = NULL; - } - /* Check whether candidate issuer failed to match authority key id in thisSubject */ - if(!issuerCert->isAuthorityKeyOf(*thisSubject)) { - if(unmatchedKeyIDIssuer == NULL) { - tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert); - unmatchedKeyIDIssuer = issuerCert; - stashedIssuer = true; - } - /* else we already have an unmatched key id issuer candidate */ - } - else { - /* unconditionally done with possible unmatchedKeyIdIssuer */ - #ifndef NDEBUG - if(unmatchedKeyIDIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer); - } - #endif - unmatchedKeyIDIssuer = NULL; - } - if(stashedIssuer) { - issuerCert = NULL; /* keep looking */ - } - } - - /* - * If we found a candidate issuer in input or gathered certs, check whether it - * might be a cross-signed intermediate that can be replaced with an anchor. - */ - if(issuerCert != NULL && !issuerCert->isSelfSigned() && (untrustedRoot == NULL)) { - bool partial = false; - TPCertInfo *possibleAnchorCert = NULL; - try { - possibleAnchorCert = tpDbFindIssuerCert(mAlloc, - clHand, - cspHand, - thisSubject, - dbList, - verifyTime, - partial, - untrustedRoot); - } - catch (...) {} - - if(possibleAnchorCert != NULL) { - if(possibleAnchorCert->isSelfSigned()) { - /* - * We found a better replacement issuer, so use it. - * note that we don't need to free the old issuerCert first as it - * comes from inCertGroup or gatheredCerts (not from dbList). - * However, code from this point on cannot assume the same thing. - */ - tpDebug("buildCertGroup: replacement anchor for issuer FOUND in dbList"); - /* mark non-root candidate as unused since we won't use it */ - issuerCert->used(false); - issuerCert = possibleAnchorCert; - - /* Caller must free, since this cert came from a DLDB */ - certsToBeFreed.appendCert(issuerCert); - if(partial) { - /* deal with this later */ - foundPartialIssuer = true; - } - - /* unconditionally done with possible expiredIssuer */ - #ifndef NDEBUG - if(expiredIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer); - } - #endif - expiredIssuer = NULL; - /* unconditionally done with possible unmatchedKeyIDIssuer */ - #ifndef NDEBUG - if(unmatchedKeyIDIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer); - } - #endif - unmatchedKeyIDIssuer = NULL; - } - else { - possibleAnchorCert->freeUniqueRecord(); - delete possibleAnchorCert; - possibleAnchorCert = NULL; - } - } - } - - if((issuerCert == NULL) && (dbList != NULL)) { - /* Issuer not in incoming cert group or gathered certs. Search DBList. */ - bool partial = false; - try { - issuerCert = tpDbFindIssuerCert(mAlloc, - clHand, - cspHand, - thisSubject, - dbList, - verifyTime, - partial, - untrustedRoot); - } - catch (...) {} - - if(issuerCert) { - /* unconditionally done with possible expiredIssuer */ - #ifndef NDEBUG - if(expiredIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING expired cert %p (4)", expiredIssuer); - } - #endif - expiredIssuer = NULL; - /* unconditionally done with possible unmatchedKeyIDIssuer */ - #ifndef NDEBUG - if(unmatchedKeyIDIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (4)", unmatchedKeyIDIssuer); - } - #endif - unmatchedKeyIDIssuer = NULL; - - /* - * Handle Radar 4566041, endless loop of cross-signed certs. - * This can only happen when fetching certs from a DLDB or - * from the net; we prevent that from happening when the certs - * are in inCertGroup or gatheredCerts by keeping track of those - * certs' mUsed state. - */ - if(isInGroup(*issuerCert)) { - tpDebug("buildCertGroup: Multiple instances of cert"); - delete issuerCert; - issuerCert = NULL; - } - else { - /* caller must free */ - certsToBeFreed.appendCert(issuerCert); - if(partial) { - /* deal with this later */ - foundPartialIssuer = true; - tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList"); - } - else { - tpDebug("buildCertGroup: Cert FOUND in dbList"); - } - } - } - } /* searching DLDB list */ - - /* - * Note: we don't handle an expired cert returned from tpDbFindIssuerCert() - * in any special way like we do with findIssuerForCertOrCrl(). - * tpDbFindIssuerCert() does its best to give us a temporally valid cert; if - * it returns an expired cert (or, if findIssuerForCertOrCrl() gave us an - * expired cert and tpDbFindIssuerCert() could not do any better), that's all - * we have to work with at this point. We'll go back to the top of the loop - * and apply trust settings if enabled; if an expired cert is trusted per - * Trust Settings, we're done. (Note that anchors are fetched from a DLDB - * when Trust Settings are enabled, so even if two roots with the same key - * and subject name are in DLDBs, and one of them is expired, we'll have the - * good one at this time because of tpDbFindIssuerCert()'s ability to find - * the best cert.) - * - * If Trust Settings are not enabled, and we have an expired root at this - * point, the expiredRoot mechanism is used to roll back and search for - * an anchor that verifies the last good cert. - */ - if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and - * we don't have a good one */ - (untrustedRoot != NULL)) { /* but we have an untrusted root available*/ - /* - * We couldn't find a better issuer, so end loop. In Case 1, we'll look for - * an alternate anchor issuer. If we can't find an anchor, we'll end up using - * the untrusted root. - */ - break; /* from main loop */ - } - - if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and - * we don't have a good one */ - (unmatchedKeyIDIssuer != NULL)) { /* but we have an unmatched keyID candidate */ - /* - * OK, we'll take the unmatched key id issuer. - * Note we don't have to free unmatchedKeyIDIssuer if we found a good one since - * unmatchedKeyIDIssuer can only come from inCertGroup or gatheredCerts (not from - * dbList). - */ - tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer); - issuerCert = unmatchedKeyIDIssuer; - unmatchedKeyIDIssuer = NULL; - } - if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and - * we don't have a good one */ - (expiredIssuer != NULL)) { /* but we have an expired candidate */ - /* - * OK, we'll take the expired issuer. - * Note we don't have to free expiredIssuer if we found a good one since - * expiredIssuer can only come from inCertGroup or gatheredCerts (not from - * dbList). - */ - tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer); - issuerCert = expiredIssuer; - expiredIssuer = NULL; - } - if(issuerCert == NULL) { - /* end of search, broken chain */ - break; - } - - /* - * One way or the other, we've found a cert which verifies subjectCert. - * Add the issuer to outCertGroup and make it the new thisSubject for - * the next pass. - */ - appendCert(issuerCert); - thisSubject = issuerCert; - subjectIsInGroup = CSSM_TRUE; - issuerCert = NULL; - /* - * We've found a (potentially) better chain, so discard the untrusted root. - * Note we don't have to free untrustedRoot because it either: - * -came from inCertGroup or gatherCerts; or - * -came from the dbList and was already added to certToBeFreed. - */ - untrustedRoot = NULL; - } /* main loop */ - - /* - * This can be NULL if we're evaluating a CRL (and we haven't - * gotten very far). - */ - endCert = lastCert(); - - /* - * This, on the other hand, is always valid. It could be a CRL. - */ - assert(thisSubject != NULL); - - if( (actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) && - ( (endCert && endCert->isSelfSigned()) || expiredRoot) ) { - /* - * Caller will be satisfied with this; skip further anchor processing. - */ - tpAnchorDebug("buildCertGroup: found IMPLICIT anchor"); - goto post_anchor; - } - if(numAnchorCerts == 0) { - /* we're probably done */ - goto post_anchor; - } - assert(anchorCerts != NULL); - - /*** anchor cert handling ***/ - - /* - * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs. - */ - expiredIssuer = NULL; - if(!(endCert && endCert->isSelfSigned())) { - for(certDex=0; certDexisIssuerOf(*thisSubject)) { - /* not this anchor */ - tpAnchorDebug("buildCertGroup anchor not issuer"); - delete anchorInfo; - anchorInfo = NULL; - continue; - } - - crtn = thisSubject->verifyWithIssuer(anchorInfo); - - if(crtn == CSSM_OK) { - if(anchorInfo->isExpired() || anchorInfo->isNotValidYet()) { - if(expiredIssuer == NULL) { - /* - * Hang on to this one; keep looking for a better one. - */ - tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo); - expiredIssuer = anchorInfo; - /* flag this condition for the switch below */ - crtn = CSSM_CERT_STATUS_EXPIRED; - expiredIssuer->isAnchor(true); - assert(!anchorInfo->isFromInputCerts()); - expiredIssuer->index(certDex); - certsToBeFreed.appendCert(expiredIssuer); - } - /* else we already have an expired candidate anchor */ - } - else { - /* - * Done with possible expiredIssuer. We don't delete it, since we already added - * it to certsToBeFreed, above. - */ - if(expiredIssuer != NULL) { - tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer); - expiredIssuer = NULL; - } - } - } - - switch(crtn) { - case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: - /* - * A bit of a corner case. Found an issuer in AnchorCerts, but - * we can't do a signature verify since the issuer has a partial - * public key. Proceed but return - * CSSMERR_TP_CERTIFICATE_CANT_OPERATE. - */ - if(anchorInfo->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { - foundPartialIssuer = true; - crtn = CSSMERR_TP_CERTIFICATE_CANT_OPERATE; - } - else { - /* ignore */ - crtn = CSSM_OK; - } - /* drop thru */ - case CSSM_OK: - /* A fully successful return. */ - verifiedToAnchor = CSSM_TRUE; - if(anchorInfo->isSelfSigned()) { - verifiedToRoot = CSSM_TRUE; - } - - /* - * Add this anchor cert to the output group - * and to certsToBeFreed. - */ - appendCert(anchorInfo); - anchorInfo->isAnchor(true); - assert(!anchorInfo->isFromInputCerts()); - anchorInfo->index(certDex); - certsToBeFreed.appendCert(anchorInfo); - tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); - tpAnchorDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); - /* one more thing: partial public key processing needed? */ - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return crtn; - } - - default: - /* continue to next anchor */ - if(crtn != CSSM_CERT_STATUS_EXPIRED) { - /* Expired means we're saving it in expiredIssuer */ - tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG"); - delete anchorInfo; - } - anchorInfo = NULL; - break; - } - } /* for each anchor */ - } /* thisSubject not a root cert */ - - /* - * Case 2: Check whether endCert is present in anchor certs. - * - * Also used to validate an expiredRoot that we pulled off the chain in - * hopes of finding something better (which, if we're here, we haven't done). - * - * Note that the main loop above did the actual root self-verify test. - */ - if(endCert || expiredRoot) { - - TPCertInfo *theRoot; - if(expiredRoot) { - /* this is NOT in our outgoing cert group (yet) */ - theRoot = expiredRoot; - } - else { - theRoot = endCert; - } - /* see if that root cert is identical to one of the anchor certs */ - for(certDex=0; certDexitemData(), &anchorCerts[certDex])) { - /* one fully successful return */ - tpAnchorDebug("buildCertGroup: end cert in input AND anchors"); - verifiedToAnchor = CSSM_TRUE; - theRoot->isAnchor(true); - if(!theRoot->isFromInputCerts()) { - /* Don't override index into input certs */ - theRoot->index(certDex); - } - if(expiredRoot) { - /* verified to anchor but caller will see - * CSSMERR_TP_CERT_EXPIRED */ - appendCert(expiredRoot); - } - /* one more thing: partial public key processing needed? */ - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return CSSM_OK; - } - } - } - tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors"); - - if(!expiredRoot && endCert->isSelfSigned()) { - /* verified to a root cert which is not an anchor */ - /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */ - /* one more thing: partial public key processing needed? */ - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return CSSM_OK; - } - } - /* else try finding a good anchor */ - } - - /* regardless of anchor search status... */ - crtn = CSSM_OK; - if(!verifiedToAnchor && (expiredIssuer != NULL)) { - /* expiredIssuer here is always an anchor */ - tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer); - appendCert(expiredIssuer); - verifiedToAnchor = CSSM_TRUE; - if(expiredIssuer->isSelfSigned()) { - verifiedToRoot = CSSM_TRUE; - } - /* no matter what, we don't want these */ - expiredRoot = NULL; - untrustedRoot = NULL; - } -post_anchor: - if(untrustedRoot) { - /* - * Special case: untrustedRoot found, but no luck resolving the problem with - * anchors. Go ahead and (re-)append the untrusted root and return - */ - tpDebug("buildCertGroup: accepted UNTRUSTED root"); - appendCert(untrustedRoot); - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return CSSM_OK; - } - } - if(expiredRoot) { - /* - * One remaining special case: expiredRoot found in input certs, but - * no luck resolving the problem with the anchors. Go ahead and (re-)append - * the expired root and return. - */ - tpDebug("buildCertGroup: accepting EXPIRED root"); - appendCert(expiredRoot); - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return CSSM_OK; - } - } - - /* If we get here, determine if fetching the issuer from the network - * should be attempted: - */ - attemptNetworkFetch = (actionFlags & CSSM_TP_ACTION_FETCH_CERT_FROM_NET); - if( (!dbList || (dbList->NumHandles == 0)) && - (!anchorCerts || (numAnchorCerts == 0)) ) { - /* DB list is empty *and* anchors are empty; there is no point in going - * out to the network, since we cannot build a chain to a trusted root. - * (This can occur when the caller wants to evaluate a single certificate - * without trying to build the chain, e.g. to check its key usage.) - */ - attemptNetworkFetch = false; - } - - /* - * If we haven't verified to a root, and net fetch of certs is enabled, - * try to get the issuer of the last cert in the chain from the net. - * If that succeeds, then call ourself recursively to perform the - * whole search again (including comparing to or verifying against - * anchor certs). - */ - if(!verifiedToRoot && !verifiedToAnchor && - (endCert != NULL) && attemptNetworkFetch) { - TPCertInfo *issuer = NULL; - CSSM_RETURN cr = tpFetchIssuerFromNet(*endCert, - clHand, - cspHand, - verifyTime, - issuer); - switch(cr) { - case CSSMERR_TP_CERTGROUP_INCOMPLETE: - /* no issuerAltName, no reason to log this */ - break; - default: - /* gross error */ - endCert->addStatusCode(cr); - break; - case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: - /* use this one but re-verify later */ - foundPartialIssuer = true; - /* and drop thru */ - case CSSM_OK: - if (!issuer) - break; - tpDebug("buildCertGroup: Cert FOUND from Net; recursing"); - - if(isInGroup(*issuer)) { - tpDebug("buildCertGroup: Multiple instances of cert from net"); - delete issuer; - issuer = NULL; - crtn = CSSMERR_TP_CERTGROUP_INCOMPLETE; - break; - } - - /* add this fetched cert to constructed group */ - appendCert(issuer); - issuer->isFromNet(true); - certsToBeFreed.appendCert(issuer); - - /* and go again */ - cr = buildCertGroup(*issuer, - inCertGroup, - dbList, - clHand, - cspHand, - verifyTime, - numAnchorCerts, - anchorCerts, - certsToBeFreed, - gatheredCerts, - CSSM_TRUE, // subjectIsInGroup - actionFlags, - policyOid, - policyStr, - policyStrLen, - leafKeyUse, // actually don't care since the leaf will not - // be evaluated - verifiedToRoot, - verifiedToAnchor, - verifiedViaTrustSettings); - if(cr) { - return cr; - } - - /* one more thing: partial public key processing needed? */ - if(foundPartialIssuer) { - return verifyWithPartialKeys(subjectItem); - } - else { - return CSSM_OK; - } - } - } -final_out: - /* regardless of outcome, check for partial keys to log per-cert status */ - CSSM_RETURN partRtn = CSSM_OK; - if(foundPartialIssuer) { - partRtn = verifyWithPartialKeys(subjectItem); - } - if(crtn) { - return crtn; - } - else { - return partRtn; - } -} - -/* - * Called from buildCertGroup as final processing of a constructed - * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been - * detected. Perform partial public key processing. - * - * We don't have to verify every element, just the ones whose - * issuers have partial public keys. - * - * Returns: - * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert - * with a partial public key which can't be completed. - * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with - * a (supposedly) completed partial key - */ -CSSM_RETURN TPCertGroup::verifyWithPartialKeys( - const TPClItemInfo &subjectItem) // Cert or CRL -{ - TPCertInfo *lastFullKeyCert = NULL; - tpDebug("verifyWithPartialKeys top"); - - /* start from the end - it's easier */ - for(int dex=mNumCerts-1; dex >= 0; dex--) { - TPCertInfo *thisCert = mCertInfo[dex]; - - /* - * If this is the start of the cert chain, and it's not being - * used to verify subjectItem, then we're done. - */ - if(dex == 0) { - if((void *)thisCert == (void *)&subjectItem) { - tpDebug("verifyWithPartialKeys: success at leaf cert"); - return CSSM_OK; - } - } - if(!thisCert->hasPartialKey()) { - /* - * Good to know. Record this and move on. - */ - lastFullKeyCert = thisCert; - tpDebug("full key cert found at index %d", dex); - continue; - } - if(lastFullKeyCert == NULL) { - /* - * No full keys between here and the end! - */ - tpDebug("UNCOMPLETABLE cert at index %d", dex); - if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { - return CSSMERR_TP_CERTIFICATE_CANT_OPERATE; - } - else { - break; - } - } - - /* do the verify - of next cert in chain or of subjectItem */ - const TPClItemInfo *subject; - if(dex == 0) { - subject = &subjectItem; - tpDebug("...verifying subject item with partial cert 0"); - } - else { - subject = mCertInfo[dex - 1]; - tpDebug("...verifying with partial cert %d", dex); - } - CSSM_RETURN crtn = subject->verifyWithIssuer(thisCert, - lastFullKeyCert); - if(crtn) { - tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex); - if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { - return CSSMERR_TP_INVALID_CERT_AUTHORITY; - } - else { - break; - } - } - } - - /* we just verified subjectItem - right? */ - assert((void *)mCertInfo[0] != (void *)&subjectItem); - tpDebug("verifyWithPartialKeys: success at subjectItem"); - return CSSM_OK; -} - -/* - * Free records obtained from DBs. Called when these records are not going to - * be passed to caller of CertGroupConstruct or CertGroupVerify. - */ -void TPCertGroup::freeDbRecords() -{ - for(unsigned dex=0; dexfreeUniqueRecord(); - } -}