#include "tpCrlVerify.h"
#include "TPCertInfo.h"
#include "TPCrlInfo.h"
#include "tpOcspVerify.h"
#include "tpdebugging.h"
#include "TPNetwork.h"
#include "TPDatabase.h"
#include <CommonCrypto/CommonDigest.h>
#include <Security/oidscert.h>
#include <security_ocspd/ocspdClient.h>
#include <security_utilities/globalizer.h>
#include <security_utilities/threading.h>
#include <security_cdsa_utilities/cssmerrors.h>
#include <sys/stat.h>
CSSM_RETURN tpRevocationPolicyVerify(
TPVerifyContext &tpVerifyContext,
TPCertGroup &certGroup)
{
switch(tpVerifyContext.policy) {
case kRevokeNone:
return CSSM_OK;
case kRevokeCrlBasic:
return tpVerifyCertGroupWithCrls(tpVerifyContext, certGroup);
case kRevokeOcsp:
return tpVerifyCertGroupWithOCSP(tpVerifyContext, certGroup);
default:
assert(0);
return CSSMERR_TP_INTERNAL_ERROR;
}
}
class TPCRLCache : private TPCrlGroup
{
public:
TPCRLCache();
~TPCRLCache() { }
TPCrlInfo *search(
TPCertInfo &cert,
TPVerifyContext &vfyCtx);
void add(
TPCrlInfo &crl);
void remove(
TPCrlInfo &crl);
void release(
TPCrlInfo &crl);
private:
Mutex mLock;
};
TPCRLCache::TPCRLCache()
: TPCrlGroup(Allocator::standard(), TGO_Group)
{
}
TPCrlInfo *TPCRLCache::search(
TPCertInfo &cert,
TPVerifyContext &vfyCtx)
{
StLock<Mutex> _(mLock);
TPCrlInfo *crl = findCrlForCert(cert);
if(crl) {
crl->calculateCurrent(vfyCtx.verifyTime);
crl->mRefCount++;
tpCrlDebug("TPCRLCache hit");
}
else {
tpCrlDebug("TPCRLCache miss");
}
return crl;
}
void TPCRLCache::add(
TPCrlInfo &crl)
{
StLock<Mutex> _(mLock);
tpCrlDebug("TPCRLCache add");
crl.mRefCount++;
appendCrl(crl);
}
void TPCRLCache::release(
TPCrlInfo &crl)
{
StLock<Mutex> _(mLock);
assert(crl.mRefCount > 0);
crl.mRefCount--;
if(crl.mRefCount == 0) {
tpCrlDebug("TPCRLCache release; deleting");
removeCrl(crl);
delete &crl;
}
else {
tpCrlDebug("TPCRLCache release; in use");
}
}
static ModuleNexus<TPCRLCache> tpGlobalCrlCache;
static CSSM_RETURN tpFindCrlForCert(
TPCertInfo &subject,
TPCrlInfo *&foundCrl, TPVerifyContext &vfyCtx)
{
tpCrlDebug("tpFindCrlForCert top");
TPCrlInfo *crl = NULL;
foundCrl = NULL;
CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0;
if(vfyCtx.crlOpts) {
crlOptFlags = vfyCtx.crlOpts->CrlFlags;
}
if(vfyCtx.inputCrls != NULL) {
crl = vfyCtx.inputCrls->findCrlForCert(subject);
if(crl && (crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK)) {
foundCrl = crl;
crl->mFromWhere = CFW_InGroup;
tpCrlDebug(" ...CRL found in CrlGroup");
return CSSM_OK;
}
}
crl = tpGlobalCrlCache().search(subject, vfyCtx);
if(crl) {
tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext");
if(crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK) {
foundCrl = crl;
crl->mFromWhere = CFW_LocalCache;
tpCrlDebug(" ...CRL found in local cache");
return CSSM_OK;
}
else {
tpGlobalCrlCache().release(*crl);
}
}
crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject);
if(crl) {
foundCrl = crl;
crl->mFromWhere = CFW_DlDb;
tpCrlDebug(" ...CRL found in DlDb");
return CSSM_OK;
}
CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
crl = NULL;
if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) {
crtn = tpFetchCrlFromNet(subject, vfyCtx, crl);
}
if(crtn) {
tpCrlDebug(" ...tpFindCrlForCert: CRL not found");
if(subject.addStatusCode(crtn)) {
return crtn;
}
else {
return CSSM_OK;
}
}
assert(crl != NULL);
tpGlobalCrlCache().add(*crl);
crl->mFromWhere = CFW_Net;
tpCrlDebug(" ...CRL found from net");
foundCrl = crl;
return CSSM_OK;
}
static void tpDisposeCrl(
TPCrlInfo &crl,
TPVerifyContext &vfyCtx)
{
switch(crl.mFromWhere) {
case CFW_Nowhere:
default:
assert(0);
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
case CFW_InGroup:
return;
case CFW_DlDb:
delete &crl;
return;
case CFW_LocalCache: case CFW_Net: tpGlobalCrlCache().release(crl);
return;
}
}
static bool tpCertHasCrlDistPt(
TPCertInfo &cert)
{
CSSM_DATA_PTR fieldValue;
CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue);
if(crtn) {
return false;
}
else {
cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
return true;
}
}
CSSM_RETURN tpGetCrlStatusForCert(
TPCertInfo &subject,
const CSSM_DATA &issuers)
{
CSSM_DATA *serialNumber=NULL;
CSSM_RETURN crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber);
if(crtn || !serialNumber) {
return CSSMERR_TP_INTERNAL_ERROR;
}
crtn = ocspdCRLStatus(*serialNumber, issuers, subject.issuerName(), NULL);
subject.freeField(&CSSMOID_X509V1SerialNumber, serialNumber);
return crtn;
}
CSSM_RETURN tpVerifyCertGroupWithCrls(
TPVerifyContext &vfyCtx,
TPCertGroup &certGroup) {
CSSM_RETURN crtn;
CSSM_RETURN ourRtn = CSSM_OK;
assert(vfyCtx.clHand != 0);
assert(vfyCtx.policy == kRevokeCrlBasic);
tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts());
CSSM_DATA issuers = { 0, NULL };
CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0;
if(vfyCtx.crlOpts != NULL) {
optFlags = vfyCtx.crlOpts->CrlFlags;
}
TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller);
try {
unsigned certDex;
TPCrlInfo *crl = NULL;
certGroup.encodeIssuers(issuers);
for(certDex=0; certDex<certGroup.numCerts(); certDex++) {
TPCertInfo *cert = certGroup.certAtIndex(certDex);
tpCrlDebug("...verifying %s cert %u",
cert->isAnchor() ? "anchor " : "", cert->index());
if(cert->isSelfSigned() || cert->trustSettingsFound()) {
continue;
}
if(cert->revokeCheckComplete()) {
tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping",
cert->index());
continue;
}
crl = NULL;
do {
crtn = tpGetCrlStatusForCert(*cert, issuers);
tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn);
if(crtn == CSSM_OK) {
tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n",
cert->index());
cert->revokeCheckGood(true);
if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
cert->revokeCheckComplete(true);
}
break;
}
if(crtn == CSSMERR_TP_CERT_REVOKED) {
tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n",
cert->index());
cert->addStatusCode(crtn);
break;
}
if(crtn == CSSMERR_APPLETP_NETWORK_FAILURE) {
if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) &&
tpCertHasCrlDistPt(*cert)) {
tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
cert->index());
break;
}
tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index());
crtn = CSSM_OK;
break;
}
crtn = tpFindCrlForCert(*cert, crl, vfyCtx);
if(crtn) {
if(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT) {
tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort",
cert->index());
break;
}
if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) &&
tpCertHasCrlDistPt(*cert)) {
tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
cert->index());
break;
}
tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index());
crtn = CSSM_OK;
assert(crl == NULL);
break;
}
assert(crl != NULL);
foundCrls.appendCrl(*crl);
crtn = crl->isCertRevoked(*cert, vfyCtx.verifyTime);
if(crtn) {
break;
}
tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index());
cert->revokeCheckGood(true);
if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
cert->revokeCheckComplete(true);
}
} while(0);
if(crtn) {
tpCrlDebug(" ...cert at index %u FAILED crl vfy",
cert->index());
if(ourRtn == CSSM_OK) {
ourRtn = crtn;
}
}
}
}
catch(const CssmError &cerr) {
if(ourRtn == CSSM_OK) {
ourRtn = cerr.error;
}
}
for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) {
TPCrlInfo *crl = foundCrls.crlAtIndex(dex);
assert(crl != NULL);
tpDisposeCrl(*crl, vfyCtx);
}
if(issuers.Data) {
free(issuers.Data);
}
return ourRtn;
}