#include "TPCrlInfo.h"
#include "tpdebugging.h"
#include "certGroupUtils.h"
#include "tpCrlVerify.h"
#include "tpPolicies.h"
#include "tpTime.h"
#include <Security/cssmapi.h>
#include <Security/x509defs.h>
#include <Security/oidscert.h>
#include <Security/oidscrl.h>
#include <security_cdsa_utilities/cssmerrors.h>
#include <string.h>
#include <Security/cssmapple.h>
static CSSM_RETURN tpGetFirstCachedFieldValue (CSSM_CL_HANDLE CLHandle,
CSSM_HANDLE CrlHandle,
const CSSM_OID *CrlField,
CSSM_HANDLE_PTR ResultsHandle,
uint32 *NumberOfMatchedFields,
CSSM_DATA_PTR *Value)
{
return CSSM_CL_CrlGetFirstCachedFieldValue(CLHandle,
CrlHandle,
NULL, CrlField,
ResultsHandle,
NumberOfMatchedFields,
Value);
}
static const TPClItemCalls tpCrlClCalls =
{
tpGetFirstCachedFieldValue,
CSSM_CL_CrlAbortQuery,
CSSM_CL_CrlCache,
CSSM_CL_CrlAbortCache,
CSSM_CL_CrlVerify,
&CSSMOID_X509V1CRLThisUpdate,
&CSSMOID_X509V1CRLNextUpdate,
CSSMERR_TP_INVALID_CRL_POINTER,
CSSMERR_APPLETP_CRL_EXPIRED,
CSSMERR_APPLETP_CRL_NOT_VALID_YET
};
TPCrlInfo::TPCrlInfo(
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA *crlData,
TPItemCopy copyCrlData, const char *verifyTime)
: TPClItemInfo(clHand, cspHand, tpCrlClCalls, crlData,
copyCrlData, verifyTime),
mRefCount(0),
mFromWhere(CFW_Nowhere),
mX509Crl(NULL),
mCrlFieldToFree(NULL),
mVerifyState(CVS_Unknown),
mVerifyError(CSSMERR_TP_INTERNAL_ERROR)
{
CSSM_RETURN crtn;
mUri.Data = NULL;
mUri.Length = 0;
crtn = fetchField(&CSSMOID_X509V2CRLSignedCrlCStruct, &mCrlFieldToFree);
if(crtn) {
releaseResources();
CssmError::throwMe(crtn);
}
if(mCrlFieldToFree->Length != sizeof(CSSM_X509_SIGNED_CRL)) {
tpErrorLog("fetchField(SignedCrlCStruct) length error\n");
releaseResources();
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
mX509Crl = (CSSM_X509_SIGNED_CRL *)mCrlFieldToFree->Data;
}
TPCrlInfo::~TPCrlInfo()
{
releaseResources();
}
void TPCrlInfo::releaseResources()
{
if(mCrlFieldToFree) {
freeField(&CSSMOID_X509V2CRLSignedCrlCStruct, mCrlFieldToFree);
mCrlFieldToFree = NULL;
}
if(mUri.Data) {
Allocator::standard().free(mUri.Data);
mUri.Data = NULL;
mUri.Length = 0;
}
TPClItemInfo::releaseResources();
}
void TPCrlInfo::uri(const CSSM_DATA &uri)
{
tpCopyCssmData(Allocator::standard(), &uri, &mUri);
}
static const CSSM_OID *const TPGoodCrlExtens[] =
{
&CSSMOID_CrlNumber,
&CSSMOID_CrlReason,
&CSSMOID_CertIssuer,
&CSSMOID_IssuingDistributionPoint,
&CSSMOID_HoldInstructionCode,
&CSSMOID_InvalidityDate,
&CSSMOID_AuthorityKeyIdentifier,
&CSSMOID_SubjectAltName,
&CSSMOID_IssuerAltName
};
#define NUM_KNOWN_EXTENS (sizeof(TPGoodCrlExtens) / sizeof(CSSM_OID_PTR))
CSSM_RETURN TPCrlInfo::parseExtensions(
TPVerifyContext &vfyCtx,
bool isPerEntry,
uint32 entryIndex, const CSSM_X509_EXTENSIONS &extens,
TPCertInfo *forCert, bool &isIndirectCrl) {
isIndirectCrl = false;
for(uint32 dex=0; dex<extens.numberOfExtensions; dex++) {
CSSM_X509_EXTENSION_PTR exten = &extens.extensions[dex];
if(exten->critical) {
unsigned i;
for(i=0; i<NUM_KNOWN_EXTENS; i++) {
if(tpCompareOids(&exten->extnId, TPGoodCrlExtens[i])) {
break;
}
}
if(i == NUM_KNOWN_EXTENS) {
tpCrlDebug("parseExtensions: Unknown Critical Extension\n");
return CSSMERR_APPLETP_UNKNOWN_CRL_EXTEN;
}
}
if(tpCompareOids(&exten->extnId,
&CSSMOID_IssuingDistributionPoint)) {
assert(exten->format == CSSM_X509_DATAFORMAT_PARSED);
CE_IssuingDistributionPoint *idp =
(CE_IssuingDistributionPoint *)
exten->value.parsedValue;
if(idp->indirectCrlPresent && idp->indirectCrl) {
isIndirectCrl = true;
}
if(forCert != NULL) {
bool isUserCert;
if(forCert->isLeaf() &&
!(vfyCtx.actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
isUserCert = true;
}
else {
isUserCert = false;
}
if((idp->onlyUserCertsPresent) && (idp->onlyUserCerts)) {
if(!isUserCert) {
tpCrlDebug("parseExtensions: onlyUserCerts, "
"!leaf\n");
return CSSMERR_APPLETP_IDP_FAIL;
}
}
if((idp->onlyCACertsPresent) && (idp->onlyCACerts)) {
if(isUserCert) {
tpCrlDebug("parseExtensions: onlyCACerts, leaf\n");
return CSSMERR_APPLETP_IDP_FAIL;
}
}
if(idp->distPointName) {
CSSM_DATA_PTR certDistPoints;
CSSM_RETURN crtn = forCert->fetchField(&CSSMOID_CrlDistributionPoints, &certDistPoints);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CL_NO_FIELD_VALUES:
return CSSM_OK;
default:
return crtn;
}
if (certDistPoints->Length != sizeof(CSSM_X509_EXTENSION)) {
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)certDistPoints->Data;
if (cssmExt == NULL) {
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CE_CRLDistPointsSyntax *dps = (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
if (dps == NULL) {
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
return CSSMERR_TP_UNKNOWN_FORMAT;
}
if (!dps->numDistPoints) {
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
return CSSM_OK;
}
CSSM_BOOL sameType = CSSM_FALSE;
CSSM_BOOL found = CSSM_FALSE;
for (unsigned dex=0; dex<dps->numDistPoints; dex++) {
CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
if (dp->distPointName == NULL) {
continue;
}
if (idp->distPointName->nameType != dp->distPointName->nameType) {
continue;
}
sameType = CSSM_TRUE;
switch (dp->distPointName->nameType) {
case CE_CDNT_NameRelativeToCrlIssuer: {
if (true) {
found = CSSM_TRUE;
tpErrorLog("parseExtensions: "
"CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
break;
}
CSSM_X509_RDN_PTR idpName = idp->distPointName->dpn.rdn;
CSSM_X509_RDN_PTR certName = dp->distPointName->dpn.rdn;
if (idpName == NULL || certName == NULL || idpName->numberOfPairs != certName->numberOfPairs) {
continue;
}
unsigned nDex;
for (nDex=0; nDex<idpName->numberOfPairs; nDex++) {
CSSM_X509_TYPE_VALUE_PAIR_PTR iPair = idpName->AttributeTypeAndValue;
CSSM_X509_TYPE_VALUE_PAIR_PTR cPair = certName->AttributeTypeAndValue;
if (!tpCompareCssmData(&iPair->type, &cPair->type) ||
!tpCompareCssmData(&iPair->value, &cPair->value)) {
break;
}
}
if (nDex==idpName->numberOfPairs) {
found = CSSM_TRUE;
}
}
case CE_CDNT_FullName: {
CE_GeneralNames *idpNames = idp->distPointName->dpn.fullName;
CE_GeneralNames *certNames = dp->distPointName->dpn.fullName;
if (idpNames == NULL || certNames == NULL || idpNames->numNames != certNames->numNames) {
continue;
}
unsigned nDex;
for (nDex=0; nDex<idpNames->numNames; nDex++) {
CE_GeneralName *idpName = &idpNames->generalName[nDex];
CE_GeneralName *certName = &certNames->generalName[nDex];
if ((idpName->nameType != certName->nameType) ||
(!tpCompareCssmData(&idpName->name, &certName->name))) {
break;
}
}
if (nDex==idpNames->numNames) {
found = CSSM_TRUE;
}
break;
}
default: {
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
return CSSMERR_TP_UNKNOWN_FORMAT;
}
}
if (found) {
break;
}
}
forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
if(sameType && !found) {
return CSSMERR_APPLETP_IDP_FAIL;
}
}
}
}
}
return CSSM_OK;
}
CSSM_RETURN TPCrlInfo::verifyWithContext(
TPVerifyContext &tpVerifyContext,
TPCertInfo *forCert, bool doCrlVerify)
{
if(isExpired()) {
return CSSMERR_APPLETP_CRL_EXPIRED;
}
if(isNotValidYet()) {
return CSSMERR_APPLETP_CRL_NOT_VALID_YET;
}
switch(mVerifyState) {
case CVS_Good:
return CSSM_OK;
case CVS_Bad:
return mVerifyError;
case CVS_Unknown:
break;
default:
tpErrorLog("verifyWithContext: bad verifyState\n");
return CSSMERR_TP_INTERNAL_ERROR;
}
CSSM_RETURN crtn;
bool isIndirectCrl;
crtn = parseExtensions(tpVerifyContext,
false,
0,
mX509Crl->tbsCertList.extensions,
forCert,
isIndirectCrl);
if(crtn) {
mVerifyState = CVS_Bad;
if(!forCert || forCert->addStatusCode(crtn)) {
return crtn;
}
}
CSSM_X509_REVOKED_CERT_LIST_PTR revoked =
mX509Crl->tbsCertList.revokedCertificates;
if(revoked != NULL) {
for(uint32 dex=0; dex<revoked->numberOfRevokedCertEntries; dex++) {
bool dummyIsIndirect; crtn = parseExtensions(tpVerifyContext,
true,
dex,
revoked->revokedCertEntry[dex].extensions,
forCert,
dummyIsIndirect);
if(crtn) {
if(!forCert || forCert->addStatusCode(crtn)) {
mVerifyState = CVS_Bad;
return crtn;
}
}
}
}
CSSM_BOOL verifiedToRoot;
CSSM_BOOL verifiedToAnchor;
CSSM_BOOL verifiedViaTrustSetting;
TPCertGroup outCertGroup(tpVerifyContext.alloc,
TGO_Caller);
TPCertGroup certsToBeFreed(tpVerifyContext.alloc, TGO_Group);
if(tpVerifyContext.signerCerts) {
tpVerifyContext.signerCerts->setAllUnused();
}
crtn = outCertGroup.buildCertGroup(
*this, tpVerifyContext.signerCerts, tpVerifyContext.dbList, tpVerifyContext.clHand,
tpVerifyContext.cspHand,
tpVerifyContext.verifyTime,
tpVerifyContext.numAnchorCerts,
tpVerifyContext.anchorCerts,
certsToBeFreed,
&tpVerifyContext.gatheredCerts,
CSSM_FALSE, tpVerifyContext.actionFlags,
tpVerifyContext.policyOid,
tpVerifyContext.policyStr,
tpVerifyContext.policyStrLen,
kSecTrustSettingsKeyUseSignRevocation,
verifiedToRoot,
verifiedToAnchor,
verifiedViaTrustSetting);
if(crtn) {
tpCrlDebug("TPCrlInfo::verifyWithContext buildCertGroup failure "
"index %u", index());
if(!forCert || forCert->addStatusCode(crtn)) {
goto errOut;
}
}
if (verifiedToRoot && (tpVerifyContext.actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS))
verifiedToAnchor = CSSM_TRUE;
if(!verifiedToAnchor && !verifiedViaTrustSetting) {
if(verifiedToRoot) {
tpCrlDebug("TPCrlInfo::verifyWithContext root, no anchor, "
"index %u", index());
crtn = CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT;
}
else {
tpCrlDebug("TPCrlInfo::verifyWithContext no root, no anchor, "
"index %u", index());
crtn = CSSMERR_APPLETP_CRL_NOT_TRUSTED;
}
if(!forCert || forCert->addStatusCode(crtn)) {
mVerifyState = CVS_Bad;
goto errOut;
}
}
outCertGroup.certAtIndex(0)->isLeaf(true);
crtn = tp_policyVerify(kCrlPolicy,
tpVerifyContext.alloc,
tpVerifyContext.clHand,
tpVerifyContext.cspHand,
&outCertGroup,
verifiedToRoot,
verifiedViaTrustSetting,
tpVerifyContext.actionFlags | CSSM_TP_ACTION_LEAF_IS_CA,
NULL, NULL); if(crtn) {
tpCrlDebug(" ...verifyWithContext policy FAILURE CRL %u",
index());
if(!forCert || forCert->addStatusCode(CSSMERR_APPLETP_CRL_POLICY_FAIL)) {
mVerifyState = CVS_Bad;
goto errOut;
}
}
if(isIndirectCrl || doCrlVerify) {
tpCrlDebug("verifyWithContext recursing to "
"tpVerifyCertGroupWithCrls");
crtn = tpVerifyCertGroupWithCrls(tpVerifyContext,
outCertGroup);
if(crtn) {
tpCrlDebug(" ...verifyWithContext CRL reverify FAILURE CRL %u",
index());
if(!forCert || forCert->addStatusCode(crtn)) {
mVerifyState = CVS_Bad;
goto errOut;
}
}
}
tpCrlDebug(" ...verifyWithContext CRL %u SUCCESS", index());
mVerifyState = CVS_Good;
errOut:
certsToBeFreed.freeDbRecords();
return crtn;
}
CSSM_RETURN TPCrlInfo::verifyWithContextNow(
TPVerifyContext &tpVerifyContext,
TPCertInfo *forCert, bool doCrlVerify)
{
CSSM_TIMESTRING ctxTime = tpVerifyContext.verifyTime;
CSSM_RETURN crtn = verifyWithContext(tpVerifyContext, forCert, doCrlVerify);
tpVerifyContext.verifyTime = ctxTime;
return crtn;
}
bool TPCrlInfo::hasSameIssuer(
const TPCertInfo &subject)
{
assert(subject.issuerName() != NULL);
if(tpCompareCssmData(issuerName(), subject.issuerName())) {
return true;
}
else {
return false;
}
}
CSSM_RETURN TPCrlInfo::isCertRevoked(
TPCertInfo &subjectCert,
CSSM_TIMESTRING verifyTime)
{
assert(mVerifyState == CVS_Good);
CSSM_X509_TBS_CERTLIST_PTR tbs = &mX509Crl->tbsCertList;
if((tbs->revokedCertificates == NULL) ||
(tbs->revokedCertificates->numberOfRevokedCertEntries == 0)) {
tpCrlDebug(" isCertRevoked: empty CRL at index %u", index());
return CSSM_OK;
}
CSSM_DATA_PTR subjSerial = NULL;
CSSM_RETURN crtn;
crtn = subjectCert.fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
if(crtn) {
tpErrorLog("TPCrlInfo:isCertRevoked: error fetching serial number\n");
if(subjectCert.addStatusCode(crtn)) {
return crtn;
}
else {
return CSSM_OK;
}
}
uint32 numEntries = tbs->revokedCertificates->numberOfRevokedCertEntries;
CSSM_X509_REVOKED_CERT_ENTRY_PTR entries =
tbs->revokedCertificates->revokedCertEntry;
crtn = CSSM_OK;
CFDateRef cfRevokedTime = NULL;
CFDateRef cfVerifyTime = NULL;
for(uint32 dex=0; dex<numEntries; dex++) {
CSSM_X509_REVOKED_CERT_ENTRY_PTR entry = &entries[dex];
if(tpCompareCssmData(subjSerial, &entry->certificateSerialNumber)) {
CSSM_X509_TIME_PTR xTime = &entry->revocationDate;
int rtn;
rtn = timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length,
&cfRevokedTime);
if(rtn) {
tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n");
}
else {
if(verifyTime != NULL) {
rtn = timeStringToCfDate((char *)verifyTime, (unsigned)strlen(verifyTime),
&cfVerifyTime);
}
else {
cfVerifyTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
}
if((rtn == 0) && cfVerifyTime != NULL) {
CFComparisonResult res = CFDateCompare(cfVerifyTime, cfRevokedTime, NULL);
if(res == kCFCompareLessThan) {
tpCrlDebug(" isCertRevoked: cert %u NOT YET REVOKED by CRL %u",
subjectCert.index(), index());
break;
}
}
}
crtn = CSSMERR_TP_CERT_REVOKED;
subjectCert.crlReason(1);
tpCrlDebug(" isCertRevoked: cert %u REVOKED by CRL %u",
subjectCert.index(), index());
break;
}
}
subjectCert.freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
if(crtn && !subjectCert.addStatusCode(crtn)) {
return CSSM_OK;
}
if(cfRevokedTime) {
CFRelease(cfRevokedTime);
}
if(cfVerifyTime) {
CFRelease(cfVerifyTime);
}
return crtn;
}
TPCrlGroup::TPCrlGroup(
Allocator &alloc,
TPGroupOwner whoOwns) :
mAlloc(alloc),
mCrlInfo(NULL),
mNumCrls(0),
mSizeofCrlInfo(0),
mWhoOwns(whoOwns)
{
}
TPCrlGroup::TPCrlGroup(
const CSSM_CRLGROUP *cssmCrlGroup, CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
Allocator &alloc,
const char *verifyTime, TPGroupOwner whoOwns) :
mAlloc(alloc),
mCrlInfo(NULL),
mNumCrls(0),
mSizeofCrlInfo(0),
mWhoOwns(whoOwns)
{
if((cssmCrlGroup == NULL) || (cssmCrlGroup->NumberOfCrls == 0)) {
return;
}
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(cssmCrlGroup->CrlGroupType != CSSM_CRLGROUP_DATA) {
CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
}
switch(cssmCrlGroup->CrlType) {
case CSSM_CRL_TYPE_X_509v1:
case CSSM_CRL_TYPE_X_509v2:
break;
default:
CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
}
switch(cssmCrlGroup->CrlEncoding) {
case CSSM_CRL_ENCODING_BER:
case CSSM_CRL_ENCODING_DER:
break;
default:
CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
}
TPCrlInfo *crlInfo = NULL;
for(unsigned crlDex=0; crlDex<cssmCrlGroup->NumberOfCrls; crlDex++) {
try {
crlInfo = new TPCrlInfo(clHand,
cspHand,
&cssmCrlGroup->GroupCrlList.CrlList[crlDex],
TIC_NoCopy, verifyTime);
}
catch (...) {
continue;
}
crlInfo->index(crlDex);
appendCrl(*crlInfo);
}
}
TPCrlGroup::~TPCrlGroup()
{
if(mWhoOwns == TGO_Group) {
unsigned i;
for(i=0; i<mNumCrls; i++) {
delete mCrlInfo[i];
}
}
mAlloc.free(mCrlInfo);
}
void TPCrlGroup::appendCrl(
TPCrlInfo &crlInfo)
{
if(mNumCrls == mSizeofCrlInfo) {
if(mSizeofCrlInfo == 0) {
mSizeofCrlInfo = 1;
}
else {
mSizeofCrlInfo *= 2;
}
mCrlInfo = (TPCrlInfo **)mAlloc.realloc(mCrlInfo,
mSizeofCrlInfo * sizeof(TPCrlInfo *));
}
mCrlInfo[mNumCrls++] = &crlInfo;
}
TPCrlInfo *TPCrlGroup::crlAtIndex(
unsigned index)
{
if(index > (mNumCrls - 1)) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
return mCrlInfo[index];
}
TPCrlInfo &TPCrlGroup::removeCrlAtIndex(
unsigned index) {
if(index > (mNumCrls - 1)) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
TPCrlInfo &rtn = *mCrlInfo[index];
unsigned i;
for(i=index; i<(mNumCrls - 1); i++) {
mCrlInfo[i] = mCrlInfo[i+1];
}
mNumCrls--;
return rtn;
}
void TPCrlGroup::removeCrl(
TPCrlInfo &crlInfo)
{
for(unsigned dex=0; dex<mNumCrls; dex++) {
if(mCrlInfo[dex] == &crlInfo) {
removeCrlAtIndex(dex);
return;
}
}
tpErrorLog("TPCrlGroup::removeCrl: CRL NOT FOUND\n");
assert(0);
}
TPCrlInfo *TPCrlGroup::firstCrl()
{
if(mNumCrls == 0) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
else {
return mCrlInfo[0];
}
}
TPCrlInfo *TPCrlGroup::lastCrl()
{
if(mNumCrls == 0) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
else {
return mCrlInfo[mNumCrls - 1];
}
}
TPCrlInfo *TPCrlGroup::findCrlForCert(
TPCertInfo &subject)
{
for(unsigned dex=0; dex<mNumCrls; dex++) {
TPCrlInfo *crl = mCrlInfo[dex];
if(crl->hasSameIssuer(subject)) {
return crl;
}
}
return NULL;
}