]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_apple_x509_tp/lib/TPCertInfo.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / TPCertInfo.cpp
diff --git a/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp b/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp
deleted file mode 100644 (file)
index 2b12425..0000000
+++ /dev/null
@@ -1,2445 +0,0 @@
-/*
- * Copyright (c) 2000-2011 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 <Security/cssmapi.h>
-#include <Security/x509defs.h>
-#include <Security/oidscert.h>
-#include <Security/oidsalg.h>
-#include <string.h>                                            /* for memcmp */
-#include <security_utilities/threading.h> /* for Mutex */
-#include <security_utilities/globalizer.h>
-#include <security_utilities/debugging.h>
-#include <security_cdsa_utilities/cssmerrors.h>
-#include <Security/cssmapple.h>
-#include <Security/SecCertificate.h>
-#include <Security/SecImportExport.h>
-#include <Security/SecTrustSettingsPriv.h>
-
-#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<CSSM_DATA *>(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, &notBeforeField);
-       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, &notAfterField);
-       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<Mutex> 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; dex<mNumStatusCodes; dex++) {
-               if(mStatusCodes[dex] == code) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool TPCertInfo::isStatusFatal(CSSM_RETURN code)
-{
-       for(unsigned dex=0; dex<mNumAllowedErrs; dex++) {
-               if(mAllowedErrs[dex] == code) {
-                       tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code);
-                       mIgnoredError = true;
-                       return false;
-               }
-       }
-       return true;
-}
-
-/*
- * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
- * key.
- */
-bool TPCertInfo::hasPartialKey()
-{
-       if(mPublicKey->KeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) {
-               return true;
-       }
-       else {
-               return false;
-       }
-}
-
-/*
- * <rdar://9145531>
- */
-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; certDex<CertGroupFrag.NumCerts; certDex++) {
-               try {
-                       certInfo = new TPCertInfo(clHand,
-                               cspHand,
-                               &CertGroupFrag.GroupList.CertList[certDex],
-                               TIC_NoCopy,                     // caller owns
-                               verifyTime);
-               }
-               catch (...) {
-                       if((certDex == 0) && firstCertMustBeValid) {
-                               CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
-                       }
-                       /* else just ignore this cert */
-                       continue;
-               }
-               certInfo->index(certDex);
-               appendCert(certInfo);
-       }
-}
-
-/*
- * Deletes contents of mCertInfo[] if appropriate.
- */
-TPCertGroup::~TPCertGroup()
-{
-       if(mWhoOwns == TGO_Group) {
-               unsigned i;
-               for(i=0; i<mNumCerts; i++) {
-                       delete mCertInfo[i];
-               }
-       }
-       mAlloc.free(mCertInfo);
-}
-
-/* add/remove/access TPTCertInfo's. */
-/*
- * NOTE: I am aware that most folks would just use an array<> 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; i<mNumCerts; i++) {
-               tpCopyCssmData(mAlloc, mCertInfo[i]->itemData(),
-                       &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; i<mNumCerts; i++) {
-               TPCertInfo *certInfo = mCertInfo[i];
-               CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[i];
-
-               /* first the booleans */
-               if(certInfo->isExpired()) {
-                       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; j<numCodes; j++) {
-                               evInfo->StatusCodes[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; i<mNumCerts; i++) {
-               TPCertInfo *ci = mCertInfo[i];
-               /*
-                * Note avoidVerify = true for isSelfSigned(); if it were appropriate to
-                * verify the signature, that would have happened in
-                * buildCssmEvidenceInfo() at the latest.
-                */
-               if(ci->isExpired() &&
-                  !(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; i<mNumCerts; i++) {
-                       TPCertInfo *ci = mCertInfo[i];
-                       if(ci->isSelfSigned(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; dex<mNumCerts; dex++) {
-               mCertInfo[dex]->used(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; dex<mNumCerts; dex++) {
-               if(!mCertInfo[dex]->isStatusFatal(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; dex<mNumCerts; dex++) {
-               if(tpCompareCssmData(certInfo.itemData(), mCertInfo[dex]->itemData())) {
-                       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<mNumCerts; certDex++) {
-               TPCertInfo *certInfo = certAtIndex(certDex);
-               if(!certDex && mNumCerts > 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; certDex<mNumCerts; certDex++) {
-               TPCertInfo *certInfo = certAtIndex(certDex);
-
-               /* has this one already been used in this search? */
-               if(certInfo->used()) {
-                       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;
-
-       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(subjCert->isSelfSigned()) {
-                               /* We're at the end of the chain. */
-                               verifiedToRoot = CSSM_TRUE;
-
-                               /*
-                                * 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))) {
-                                       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 */
-                       }       /* root */
-
-                       /*
-                        * If this non-root 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 foundNonRootAnchor = false;
-                               for(certDex=0; certDex<numAnchorCerts; certDex++) {
-                                       if(tp_CompareCerts(subjCert->itemData(), &anchorCerts[certDex])) {
-                                               foundNonRootAnchor = 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 */
-                                               break;  /* out of anchor-checking loop */
-                                       }
-                               }
-                               if(foundNonRootAnchor) {
-                                       break; /* out of main loop */
-                               }
-                       } /* non-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 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 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((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);
-                       }
-                       catch (...) {}
-
-                       if(issuerCert) {
-                               /* 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;
-
-
-                               /*
-                                * 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 */
-                  (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;
-       }       /* 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; certDex<numAnchorCerts; certDex++) {
-
-                       try {
-                               anchorInfo = new TPCertInfo(clHand,
-                                       cspHand,
-                                       &anchorCerts[certDex],
-                                       TIC_NoCopy,
-                                       verifyTime);
-                       }
-                       catch(...) {
-                               /* bad anchor cert - ignore it */
-                               anchorInfo = NULL;
-                               continue;
-                       }
-
-                       /*
-                        * We must subsequently delete anchorInfo one way or the other.
-                        * If we add it to tpCertGroup, we also add it to certsToBeFreed.
-                        * Otherwise we delete it.
-                        */
-                       if(!anchorInfo->isIssuerOf(*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; certDex<numAnchorCerts; certDex++) {
-                       if(tp_CompareCerts(theRoot->itemData(), &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 this one */
-               expiredRoot = NULL;
-       }
-post_anchor:
-       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: <rdar://6113890&7419584&7422356>
-        */
-       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; dex<mNumCerts; dex++) {
-               TPCertInfo *certInfo = mCertInfo[dex];
-               certInfo->freeUniqueRecord();
-       }
-}