#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>
#include <security_utilities/threading.h>
#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) :
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);
}
crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
if(crtn) {
CssmError::throwMe(crtn);
}
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) {
if(decodeECDSA_SigAlgParams(&algId->parameters, &mSigAlg)) {
tpErrorLog("TPClItemInfo: incomplete/unknown ECDSA signature algorithm\n");
CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
}
}
freeField(&CSSMOID_X509V1SignatureAlgorithmTBS, algField);
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;
}
}
CSSM_RETURN TPClItemInfo::fetchField(
const CSSM_OID *fieldOid,
CSSM_DATA_PTR *fieldData) {
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;
}
CSSM_RETURN TPClItemInfo::freeField(
const CSSM_OID *fieldOid,
CSSM_DATA_PTR fieldData)
{
return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
}
CSSM_RETURN TPClItemInfo::verifyWithIssuer(
TPCertInfo *issuerCert,
TPCertInfo *paramCert ) const
{
CSSM_RETURN crtn;
assert(mClHand != 0);
assert(issuerCert->isIssuerOf(*this));
assert(mCspHand != 0);
if(issuerCert->hasPartialKey() && (paramCert == NULL)) {
tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE");
return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
}
CSSM_CC_HANDLE ccHand;
crtn = CSSM_CSP_CreateSignatureContext(mCspHand,
mSigAlg,
NULL, issuerCert->pubKey(),
&ccHand);
if(crtn != CSSM_OK) {
tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
CssmError::throwMe(crtn);
}
if(paramCert != NULL) {
assert(issuerCert->hasPartialKey());
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, NULL, 0);
switch(crtn) {
case CSSM_OK: case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: tpVfyDebug("verifyWithIssuer GOOD");
break;
default:
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);
}
return mClCalls.cacheItem(mClHand, mItemData, &mCacheHand);
}
void TPClItemInfo::fetchNotBeforeAfter()
{
CSSM_DATA_PTR notBeforeField = NULL;
CSSM_DATA_PTR notAfterField = NULL;
CSSM_RETURN crtn = CSSM_OK;
CSSM_X509_TIME *xTime;
assert(cacheHand() != CSSM_INVALID_HANDLE);
crtn = fetchField(mClCalls.notBeforeOid, ¬BeforeField);
if(crtn) {
tpErrorLog("fetchNotBeforeAfter: GetField error\n");
CssmError::throwMe(mClCalls.invalidItemRtn);
}
xTime = (CSSM_X509_TIME *)notBeforeField->Data;
if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotBefore)) {
tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n");
crtn = mClCalls.invalidItemRtn;
goto errOut;
}
crtn = fetchField(mClCalls.notAfterOid, ¬AfterField);
if(crtn) {
if(mClCalls.notAfterOid == &CSSMOID_X509V1ValidityNotAfter) {
tpErrorLog("fetchNotBeforeAfter: GetField error\n");
crtn = mClCalls.invalidItemRtn;
goto errOut;
}
else {
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);
}
}
ModuleNexus<Mutex> tpTimeLock;
CSSM_RETURN TPClItemInfo::calculateCurrent(
const char *verifyString)
{
CFDateRef refTime = NULL;
if(verifyString != NULL) {
if(timeStringToCfDate(verifyString, (unsigned)strlen(verifyString), &refTime)) {
tpErrorLog("calculateCurrent: timeStringToCfDate error\n");
return CSSMERR_TP_INVALID_TIMESTRING;
}
}
else {
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;
}
}
TPCertInfo::TPCertInfo(
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA *certData,
TPItemCopy copyCertData, const char *verifyTime) :
TPClItemInfo(clHand, cspHand, tpCertClCalls, certData,
copyCertData, verifyTime),
mSubjectName(NULL),
mPublicKeyData(NULL),
mPublicKey(NULL),
mIsAnchor(false),
mIsFromInputCerts(false),
mIsFromNet(false),
mNumStatusCodes(0),
mStatusCodes(NULL),
mCrlReason(0),
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;
crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
if(crtn) {
releaseResources();
CssmError::throwMe(crtn);
}
crtn = fetchField(&CSSMOID_CSSMKeyStruct, &mPublicKeyData);
if(crtn || (mPublicKeyData->Length != sizeof(CSSM_KEY))) {
releaseResources();
CssmError::throwMe(crtn);
}
mPublicKey = (CSSM_KEY_PTR)mPublicKeyData->Data;
if(tpCompareCssmData(mSubjectName, issuerName())) {
tpAnchorDebug("TPCertInfo potential anchor");
mIsRoot = TRS_NamesMatch;
}
else {
mIsRoot = TRS_NotRoot;
}
}
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;
}
bool TPCertInfo::isSelfSigned(bool avoidVerify)
{
switch(mIsRoot) {
case TRS_NotRoot: return false;
case TRS_IsRoot:
return true;
case TRS_NamesMatch:
if(avoidVerify) {
return true;
}
case TRS_Unknown: default:
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;
}
}
}
bool TPCertInfo::isIssuerOf(
const TPClItemInfo &subject)
{
assert(mSubjectName != NULL);
assert(subject.issuerName() != NULL);
if(tpCompareCssmData(mSubjectName, subject.issuerName())) {
return true;
}
else {
return false;
}
}
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;
}
bool TPCertInfo::hasPartialKey()
{
if(mPublicKey->KeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) {
return true;
}
else {
return false;
}
}
bool TPCertInfo::shouldReject()
{
static unsigned char _UTN_UF_H_ISSUER_BYTES[154] = {
0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55,
0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b,
0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45,
0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e,
0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f,
0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16,
0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53,
0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
};
CSSM_DATA _UTN_UF_H_ISSUER = { sizeof(_UTN_UF_H_ISSUER_BYTES), _UTN_UF_H_ISSUER_BYTES };
static CSSM_DATA _UTN_UF_H_SERIALS[] = {
{ 17, (uint8*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, { 17, (uint8*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, { 17, (uint8*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, { 17, (uint8*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, { 17, (uint8*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, { 16, (uint8*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" }, { 16, (uint8*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" }, { 16, (uint8*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" }, { 17, (uint8*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, { 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);
mCrlReason=2;
break;
}
++p;
}
freeField(&CSSMOID_X509V1SerialNumber, serialNumber);
return matched;
}
OSStatus TPCertInfo::evaluateTrustSettings(
const CSSM_OID &policyOid,
const char *policyString, uint32 policyStringLen,
SecTrustSettingsKeyUsage keyUse, bool *foundMatchingEntry, bool *foundAnyEntry) {
if(mTrustSettingsEvaluated) {
bool doFlush = false;
if(mTrustSettingsKeyUsage != kSecTrustSettingsKeyUseAny) {
if(keyUse == kSecTrustSettingsKeyUseAny) {
doFlush = true;
}
else if((keyUse & mTrustSettingsKeyUsage) != keyUse) {
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;
}
}
if(!mTrustSettingsEvaluated) {
if(mCertHashStr == NULL) {
const CSSM_DATA *certData = itemData();
mCertHashStr = SecTrustSettingsCertHashStrFromData(certData->Data,
certData->Length);
}
OSStatus ortn = SecTrustSettingsEvaluateCert(mCertHashStr,
&policyOid,
policyString,
policyStringLen,
keyUse,
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
if(shouldReject()) {
return CSSMERR_TP_INVALID_CERTIFICATE;
}
}
*foundMatchingEntry = mTrustSettingsFoundMatchingEntry;
*foundAnyEntry = mTrustSettingsFoundAnyEntry;
return errSecSuccess;
}
bool TPCertInfo::trustSettingsFound()
{
switch(mTrustSettingsResult) {
case kSecTrustSettingsResultUnspecified:
case kSecTrustSettingsResultInvalid:
return false;
default:
return true;
}
}
bool TPCertInfo::hasEmptySubjectName()
{
if((mSubjectName == NULL) || (mSubjectName->Length <= 4)) {
return true;
}
else {
return false;
}
}
void TPCertInfo::freeUniqueRecord()
{
if(mUniqueRecord == NULL) {
return;
}
tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord);
CSSM_DL_FreeUniqueRecord(mDlDbHandle, mUniqueRecord);
}
TPCertGroup::TPCertGroup(
Allocator &alloc,
TPGroupOwner whoOwns) :
mAlloc(alloc),
mCertInfo(NULL),
mNumCerts(0),
mSizeofCertInfo(0),
mWhoOwns(whoOwns)
{
tpCertInfoDbg("TPCertGroup simple construct this %p", this);
}
TPCertGroup::TPCertGroup(
const CSSM_CERTGROUP &CertGroupFrag,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
Allocator &alloc,
const char *verifyTime, bool firstCertMustBeValid,
TPGroupOwner whoOwns) :
mAlloc(alloc),
mCertInfo(NULL),
mNumCerts(0),
mSizeofCertInfo(0),
mWhoOwns(whoOwns)
{
tpCertInfoDbg("TPCertGroup hard construct this %p", this);
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);
}
TPCertInfo *certInfo = NULL;
for(unsigned certDex=0; certDex<CertGroupFrag.NumCerts; certDex++) {
try {
certInfo = new TPCertInfo(clHand,
cspHand,
&CertGroupFrag.GroupList.CertList[certDex],
TIC_NoCopy, verifyTime);
}
catch (...) {
if((certDex == 0) && firstCertMustBeValid) {
CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
}
continue;
}
certInfo->index(certDex);
appendCert(certInfo);
}
}
TPCertGroup::~TPCertGroup()
{
if(mWhoOwns == TGO_Group) {
unsigned i;
for(i=0; i<mNumCerts; i++) {
delete mCertInfo[i];
}
}
mAlloc.free(mCertInfo);
}
void TPCertGroup::appendCert(
TPCertInfo *certInfo) {
if(mNumCerts == mSizeofCertInfo) {
if(mSizeofCertInfo == 0) {
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) {
if(index > (mNumCerts - 1)) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
TPCertInfo *rtn = mCertInfo[index];
unsigned i;
for(i=index; i<(mNumCerts - 1); i++) {
mCertInfo[i] = mCertInfo[i+1];
}
mNumCerts--;
return rtn;
}
TPCertInfo *TPCertGroup::firstCert()
{
if(mNumCerts == 0) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
else {
return mCertInfo[0];
}
}
TPCertInfo *TPCertGroup::lastCert()
{
if(mNumCerts == 0) {
return NULL;
}
else {
return mCertInfo[mNumCerts - 1];
}
}
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) {
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;
}
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];
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;
}
}
bool avoidVerify = false;
switch(certInfo->trustSettingsResult()) {
case kSecTrustSettingsResultTrustRoot:
case kSecTrustSettingsResultTrustAsRoot:
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();
evInfo->NumStatusCodes = numCodes;
evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes+1,
sizeof(CSSM_RETURN));
for(unsigned j=0; j<numCodes; j++) {
evInfo->StatusCodes[j] = (certInfo->statusCodes())[j];
}
evInfo->StatusCodes[numCodes] = (CSSM_RETURN)certInfo->crlReason();
if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST |
CSSM_CERT_STATUS_TRUST_SETTINGS_DENY |
CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) {
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;
}
CSSM_RETURN TPCertGroup::getReturnCode(
CSSM_RETURN constructStatus,
CSSM_RETURN policyStatus,
CSSM_APPLE_TP_ACTION_FLAGS actionFlags)
{
if(constructStatus) {
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; bool requireRevPerCert = (actionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT) ?
true : false;
for(unsigned i=0; i<mNumCerts; i++) {
TPCertInfo *ci = mCertInfo[i];
if(ci->isExpired() &&
!(allowExpiredRoot && ci->isSelfSigned(true)) && ci->isStatusFatal(CSSMERR_TP_CERT_EXPIRED)) { 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;
}
if(requireRevPerCert) {
for(unsigned i=0; i<mNumCerts; i++) {
TPCertInfo *ci = mCertInfo[i];
if(ci->isSelfSigned(true)) {
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;
}
void TPCertGroup::setAllUnused()
{
for(unsigned dex=0; dex<mNumCerts; dex++) {
mCertInfo[dex]->used(false);
}
}
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:
break;
default:
return false;
}
for(unsigned dex=0; dex<mNumCerts; dex++) {
if(!mCertInfo[dex]->isStatusFatal(code)) {
tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u",
dex);
return true;
}
}
return false;
}
bool TPCertGroup::isInGroup(TPCertInfo &certInfo)
{
for(unsigned dex=0; dex<mNumCerts; dex++) {
if(tpCompareCssmData(certInfo.itemData(), mCertInfo[dex]->itemData())) {
return true;
}
}
return false;
}
void TPCertGroup::encodeIssuers(CSSM_DATA &issuers)
{
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;
}
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);
}
}
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);
if(certInfo->used()) {
continue;
}
if(certInfo->isIssuerOf(subject)) {
tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig");
CSSM_RETURN crtn = subject.verifyWithIssuer(certInfo);
switch(crtn) {
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
partialIssuerKey = true;
case CSSM_OK:
if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
if(certInfo->isExpired() || certInfo->isNotValidYet()) {
tpDebug("findIssuerForCertOrCrl: holding expired cert %p",
certInfo);
expiredIssuer = certInfo;
break;
}
}
if(unmatchedKeyIDIssuer == NULL) {
if(!certInfo->isAuthorityKeyOf(subject)) {
tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p",
certInfo);
unmatchedKeyIDIssuer = certInfo;
break;
}
}
certInfo->used(true);
return certInfo;
default:
tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
break;
}
}
}
if(unmatchedKeyIDIssuer != NULL) {
tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer);
unmatchedKeyIDIssuer->used(true);
return unmatchedKeyIDIssuer;
}
if(expiredIssuer != NULL) {
tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer);
expiredIssuer->used(true);
return expiredIssuer;
}
return NULL;
}
CSSM_RETURN TPCertGroup::buildCertGroup(
const TPClItemInfo &subjectItem, TPCertGroup *inCertGroup, const CSSM_DL_DB_LIST *dbList, CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const char *verifyTime,
uint32 numAnchorCerts,
const CSSM_DATA *anchorCerts,
TPCertGroup &certsToBeFreed,
TPCertGroup *gatheredCerts,
CSSM_BOOL subjectIsInGroup,
CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
const CSSM_OID *policyOid,
const char *policyStr,
uint32 policyStrLen,
SecTrustSettingsKeyUsage leafKeyUse,
CSSM_BOOL &verifiedToRoot, CSSM_BOOL &verifiedToAnchor, CSSM_BOOL &verifiedViaTrustSettings) {
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");
TPCertInfo *expiredRoot = NULL;
TPCertInfo *expiredIssuer = NULL;
TPCertInfo *unmatchedKeyIDIssuer = NULL;
TPCertInfo *untrustedRoot = NULL;
verifiedToRoot = CSSM_FALSE;
verifiedToAnchor = CSSM_FALSE;
verifiedViaTrustSettings = CSSM_FALSE;
for(;;) {
if(subjectIsInGroup) {
TPCertInfo *subjCert = lastCert();
assert(subjCert != NULL);
if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) {
assert(policyOid != NULL);
SecTrustSettingsKeyUsage localKeyUse;
bool doRetry = false;
if(subjCert == firstCert()) {
localKeyUse = leafKeyUse;
}
else {
localKeyUse = kSecTrustSettingsKeyUseSignCert | kSecTrustSettingsKeyUseSignRevocation;
doRetry = true;
}
bool foundEntry = false;
bool trustSettingsFound = false;
OSStatus ortn = subjCert->evaluateTrustSettings(*policyOid,
policyStr, policyStrLen, localKeyUse, &trustSettingsFound, &foundEntry);
if(ortn) {
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:
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:
tpTrustSettingsDbg("TrustResultUnspecified found");
goto post_trust_setting;
default:
tpTrustSettingsDbg("Unknown TrustResult (%d)",
(int)subjCert->trustSettingsResult());
crtn = CSSMERR_TP_INTERNAL_ERROR;
break;
}
verifiedViaTrustSettings = CSSM_TRUE;
goto final_out;
}
}
post_trust_setting:
if(numAnchorCerts && anchorCerts) {
bool foundAnchor = false;
for(certDex=0; certDex<numAnchorCerts; certDex++) {
if(tp_CompareCerts(subjCert->itemData(), &anchorCerts[certDex])) {
foundAnchor = true;
if(!firstSubjectIsInGroup || (mNumCerts > 1)) {
if(mNumCerts) {
mNumCerts--;
}
if(mNumCerts == 0) {
thisSubject = &subjectItem;
}
else {
thisSubject = lastCert();
}
tpAnchorDebug("buildCertGroup: CA cert in input AND anchors");
}
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");
}
if(subjCert->isSelfSigned()) {
verifiedToRoot = CSSM_TRUE;
}
break;
}
}
if(foundAnchor) {
break;
}
}
if(subjCert->isSelfSigned()) {
if((subjCert->isExpired() || subjCert->isNotValidYet()) &&
(!firstSubjectIsInGroup || (mNumCerts > 1))) {
verifiedToRoot = CSSM_TRUE;
tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert);
expiredRoot = subjCert;
if(mNumCerts) {
mNumCerts--;
}
if(mNumCerts == 0) {
thisSubject = &subjectItem;
}
else {
thisSubject = lastCert();
}
break;
}
if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) {
verifiedToRoot = CSSM_TRUE;
break;
}
if(!firstSubjectIsInGroup || (mNumCerts > 1)) {
tpDebug("buildCertGroup: UNTRUSTED ROOT %p, looking for better chain", subjCert);
untrustedRoot = subjCert;
if(mNumCerts) {
mNumCerts--;
}
if(mNumCerts == 0) {
thisSubject = &subjectItem;
}
else {
thisSubject = lastCert();
}
}
else {
break;
}
}
}
if(inCertGroup != NULL) {
bool partial = false;
issuerCert = inCertGroup->findIssuerForCertOrCrl(*thisSubject,
partial);
if(issuerCert) {
issuerCert->isFromInputCerts(true);
if(partial) {
foundPartialIssuer = true;
tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
}
else {
tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
}
}
}
if(issuerCert != NULL) {
bool stashedIssuer = false;
if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) {
stashedIssuer = true;
}
if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
if(expiredIssuer == NULL) {
tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert);
expiredIssuer = issuerCert;
stashedIssuer = true;
}
}
else {
#ifndef NDEBUG
if(expiredIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer);
}
#endif
expiredIssuer = NULL;
}
if(!issuerCert->isAuthorityKeyOf(*thisSubject)) {
if(unmatchedKeyIDIssuer == NULL) {
tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert);
unmatchedKeyIDIssuer = issuerCert;
stashedIssuer = true;
}
}
else {
#ifndef NDEBUG
if(unmatchedKeyIDIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer);
}
#endif
unmatchedKeyIDIssuer = NULL;
}
if(stashedIssuer) {
issuerCert = NULL;
}
}
if((issuerCert == NULL) && (gatheredCerts != NULL)) {
bool partial = false;
issuerCert = gatheredCerts->findIssuerForCertOrCrl(*thisSubject,
partial);
if(issuerCert) {
if(partial) {
foundPartialIssuer = true;
tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
}
else {
tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
}
}
}
if(issuerCert != NULL) {
bool stashedIssuer = false;
if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) {
stashedIssuer = true;
}
if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
if(expiredIssuer == NULL) {
tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert);
expiredIssuer = issuerCert;
stashedIssuer = true;
}
}
else {
#ifndef NDEBUG
if(expiredIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer);
}
#endif
expiredIssuer = NULL;
}
if(!issuerCert->isAuthorityKeyOf(*thisSubject)) {
if(unmatchedKeyIDIssuer == NULL) {
tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert);
unmatchedKeyIDIssuer = issuerCert;
stashedIssuer = true;
}
}
else {
#ifndef NDEBUG
if(unmatchedKeyIDIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer);
}
#endif
unmatchedKeyIDIssuer = NULL;
}
if(stashedIssuer) {
issuerCert = NULL;
}
}
if(issuerCert != NULL && !issuerCert->isSelfSigned() && (untrustedRoot == NULL)) {
bool partial = false;
TPCertInfo *possibleAnchorCert = NULL;
try {
possibleAnchorCert = tpDbFindIssuerCert(mAlloc,
clHand,
cspHand,
thisSubject,
dbList,
verifyTime,
partial,
untrustedRoot);
}
catch (...) {}
if(possibleAnchorCert != NULL) {
if(possibleAnchorCert->isSelfSigned()) {
tpDebug("buildCertGroup: replacement anchor for issuer FOUND in dbList");
issuerCert->used(false);
issuerCert = possibleAnchorCert;
certsToBeFreed.appendCert(issuerCert);
if(partial) {
foundPartialIssuer = true;
}
#ifndef NDEBUG
if(expiredIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer);
}
#endif
expiredIssuer = NULL;
#ifndef NDEBUG
if(unmatchedKeyIDIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer);
}
#endif
unmatchedKeyIDIssuer = NULL;
}
else {
possibleAnchorCert->freeUniqueRecord();
delete possibleAnchorCert;
possibleAnchorCert = NULL;
}
}
}
if((issuerCert == NULL) && (dbList != NULL)) {
bool partial = false;
try {
issuerCert = tpDbFindIssuerCert(mAlloc,
clHand,
cspHand,
thisSubject,
dbList,
verifyTime,
partial,
untrustedRoot);
}
catch (...) {}
if(issuerCert) {
#ifndef NDEBUG
if(expiredIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING expired cert %p (4)", expiredIssuer);
}
#endif
expiredIssuer = NULL;
#ifndef NDEBUG
if(unmatchedKeyIDIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (4)", unmatchedKeyIDIssuer);
}
#endif
unmatchedKeyIDIssuer = NULL;
if(isInGroup(*issuerCert)) {
tpDebug("buildCertGroup: Multiple instances of cert");
delete issuerCert;
issuerCert = NULL;
}
else {
certsToBeFreed.appendCert(issuerCert);
if(partial) {
foundPartialIssuer = true;
tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
}
else {
tpDebug("buildCertGroup: Cert FOUND in dbList");
}
}
}
}
if((issuerCert == NULL) &&
(untrustedRoot != NULL)) {
break;
}
if((issuerCert == NULL) &&
(unmatchedKeyIDIssuer != NULL)) {
tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer);
issuerCert = unmatchedKeyIDIssuer;
unmatchedKeyIDIssuer = NULL;
}
if((issuerCert == NULL) &&
(expiredIssuer != NULL)) {
tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer);
issuerCert = expiredIssuer;
expiredIssuer = NULL;
}
if(issuerCert == NULL) {
break;
}
appendCert(issuerCert);
thisSubject = issuerCert;
subjectIsInGroup = CSSM_TRUE;
issuerCert = NULL;
untrustedRoot = NULL;
}
endCert = lastCert();
assert(thisSubject != NULL);
if( (actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) &&
( (endCert && endCert->isSelfSigned()) || expiredRoot) ) {
tpAnchorDebug("buildCertGroup: found IMPLICIT anchor");
goto post_anchor;
}
if(numAnchorCerts == 0) {
goto post_anchor;
}
assert(anchorCerts != NULL);
expiredIssuer = NULL;
if(!(endCert && endCert->isSelfSigned())) {
for(certDex=0; certDex<numAnchorCerts; certDex++) {
try {
anchorInfo = new TPCertInfo(clHand,
cspHand,
&anchorCerts[certDex],
TIC_NoCopy,
verifyTime);
}
catch(...) {
anchorInfo = NULL;
continue;
}
if(!anchorInfo->isIssuerOf(*thisSubject)) {
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) {
tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo);
expiredIssuer = anchorInfo;
crtn = CSSM_CERT_STATUS_EXPIRED;
expiredIssuer->isAnchor(true);
assert(!anchorInfo->isFromInputCerts());
expiredIssuer->index(certDex);
certsToBeFreed.appendCert(expiredIssuer);
}
}
else {
if(expiredIssuer != NULL) {
tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer);
expiredIssuer = NULL;
}
}
}
switch(crtn) {
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
if(anchorInfo->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
foundPartialIssuer = true;
crtn = CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
}
else {
crtn = CSSM_OK;
}
case CSSM_OK:
verifiedToAnchor = CSSM_TRUE;
if(anchorInfo->isSelfSigned()) {
verifiedToRoot = CSSM_TRUE;
}
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");
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return crtn;
}
default:
if(crtn != CSSM_CERT_STATUS_EXPIRED) {
tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG");
delete anchorInfo;
}
anchorInfo = NULL;
break;
}
}
}
if(endCert || expiredRoot) {
TPCertInfo *theRoot;
if(expiredRoot) {
theRoot = expiredRoot;
}
else {
theRoot = endCert;
}
for(certDex=0; certDex<numAnchorCerts; certDex++) {
if(tp_CompareCerts(theRoot->itemData(), &anchorCerts[certDex])) {
tpAnchorDebug("buildCertGroup: end cert in input AND anchors");
verifiedToAnchor = CSSM_TRUE;
theRoot->isAnchor(true);
if(!theRoot->isFromInputCerts()) {
theRoot->index(certDex);
}
if(expiredRoot) {
appendCert(expiredRoot);
}
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return CSSM_OK;
}
}
}
tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors");
if(!expiredRoot && endCert->isSelfSigned()) {
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return CSSM_OK;
}
}
}
crtn = CSSM_OK;
if(!verifiedToAnchor && (expiredIssuer != NULL)) {
tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer);
appendCert(expiredIssuer);
verifiedToAnchor = CSSM_TRUE;
if(expiredIssuer->isSelfSigned()) {
verifiedToRoot = CSSM_TRUE;
}
expiredRoot = NULL;
untrustedRoot = NULL;
}
post_anchor:
if(untrustedRoot) {
tpDebug("buildCertGroup: accepted UNTRUSTED root");
appendCert(untrustedRoot);
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return CSSM_OK;
}
}
if(expiredRoot) {
tpDebug("buildCertGroup: accepting EXPIRED root");
appendCert(expiredRoot);
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return CSSM_OK;
}
}
attemptNetworkFetch = (actionFlags & CSSM_TP_ACTION_FETCH_CERT_FROM_NET);
if( (!dbList || (dbList->NumHandles == 0)) &&
(!anchorCerts || (numAnchorCerts == 0)) ) {
attemptNetworkFetch = false;
}
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:
break;
default:
endCert->addStatusCode(cr);
break;
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
foundPartialIssuer = true;
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;
}
appendCert(issuer);
issuer->isFromNet(true);
certsToBeFreed.appendCert(issuer);
cr = buildCertGroup(*issuer,
inCertGroup,
dbList,
clHand,
cspHand,
verifyTime,
numAnchorCerts,
anchorCerts,
certsToBeFreed,
gatheredCerts,
CSSM_TRUE, actionFlags,
policyOid,
policyStr,
policyStrLen,
leafKeyUse, verifiedToRoot,
verifiedToAnchor,
verifiedViaTrustSettings);
if(cr) {
return cr;
}
if(foundPartialIssuer) {
return verifyWithPartialKeys(subjectItem);
}
else {
return CSSM_OK;
}
}
}
final_out:
CSSM_RETURN partRtn = CSSM_OK;
if(foundPartialIssuer) {
partRtn = verifyWithPartialKeys(subjectItem);
}
if(crtn) {
return crtn;
}
else {
return partRtn;
}
}
CSSM_RETURN TPCertGroup::verifyWithPartialKeys(
const TPClItemInfo &subjectItem) {
TPCertInfo *lastFullKeyCert = NULL;
tpDebug("verifyWithPartialKeys top");
for(int dex=mNumCerts-1; dex >= 0; dex--) {
TPCertInfo *thisCert = mCertInfo[dex];
if(dex == 0) {
if((void *)thisCert == (void *)&subjectItem) {
tpDebug("verifyWithPartialKeys: success at leaf cert");
return CSSM_OK;
}
}
if(!thisCert->hasPartialKey()) {
lastFullKeyCert = thisCert;
tpDebug("full key cert found at index %d", dex);
continue;
}
if(lastFullKeyCert == NULL) {
tpDebug("UNCOMPLETABLE cert at index %d", dex);
if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
return CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
}
else {
break;
}
}
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;
}
}
}
assert((void *)mCertInfo[0] != (void *)&subjectItem);
tpDebug("verifyWithPartialKeys: success at subjectItem");
return CSSM_OK;
}
void TPCertGroup::freeDbRecords()
{
for(unsigned dex=0; dex<mNumCerts; dex++) {
TPCertInfo *certInfo = mCertInfo[dex];
certInfo->freeUniqueRecord();
}
}