#include "TPNetwork.h"
#include "tpdebugging.h"
#include "tpTime.h"
#include <Security/cssmtype.h>
#include <Security/cssmapple.h>
#include <Security/oidscert.h>
#include <security_utilities/logging.h>
#include <security_ocspd/ocspdClient.h>
typedef enum {
LT_Crl = 1,
LT_Cert
} LF_Type;
static CSSM_RETURN tpFetchViaNet(
const CSSM_DATA &url,
LF_Type lfType,
CSSM_TIMESTRING verifyTime, Allocator &alloc,
CSSM_DATA &rtnBlob) {
if(lfType == LT_Crl) {
return ocspdCRLFetch(alloc, url,
true, true, verifyTime, rtnBlob);
}
else {
return ocspdCertFetch(alloc, url, rtnBlob);
}
}
static CSSM_RETURN tpCrlViaNet(
const CSSM_DATA &url,
TPVerifyContext &vfyCtx,
TPCertInfo &forCert, TPCrlInfo *&rtnCrl)
{
TPCrlInfo *crl = NULL;
CSSM_DATA crlData;
CSSM_RETURN crtn;
Allocator &alloc = Allocator::standard();
char cssmTime[CSSM_TIME_STRLEN+1];
rtnCrl = NULL;
{
StLock<Mutex> _(tpTimeLock());
timeAtNowPlus(0, TIME_CSSM, cssmTime);
}
crtn = tpFetchViaNet(url, LT_Crl, cssmTime, alloc, crlData);
if(crtn) {
return crtn;
}
try {
crl = new TPCrlInfo(vfyCtx.clHand,
vfyCtx.cspHand,
&crlData,
TIC_CopyData,
NULL); }
catch(...) {
alloc.free(crlData.Data);
tpDebug(" bad CRL; flushing from cache and retrying");
ocspdCRLFlush(url);
crtn = tpFetchViaNet(url, LT_Crl, cssmTime, alloc, crlData);
if(crtn == CSSM_OK) {
try {
crl = new TPCrlInfo(vfyCtx.clHand,
vfyCtx.cspHand,
&crlData,
TIC_CopyData,
NULL);
tpDebug(" RECOVERY: good CRL obtained from net");
}
catch(...) {
alloc.free(crlData.Data);
tpDebug(" bad CRL; recovery FAILED (1)");
return CSSMERR_APPLETP_CRL_NOT_FOUND;
}
}
else {
tpDebug(" bad CRL; recovery FAILED (2)");
return CSSMERR_APPLETP_CRL_NOT_FOUND;
}
}
alloc.free(crlData.Data);
crtn = crl->verifyWithContextNow(vfyCtx, &forCert);
if(crtn == CSSM_OK) {
crl->uri(url);
}
else {
delete crl;
crl = NULL;
}
rtnCrl = crl;
return crtn;
}
static CSSM_RETURN tpIssuerCertViaNet(
const CSSM_DATA &url,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const char *verifyTime,
TPCertInfo &subject,
TPCertInfo *&rtnCert)
{
TPCertInfo *issuer = NULL;
CSSM_DATA certData;
CSSM_RETURN crtn;
Allocator &alloc = Allocator::standard();
crtn = tpFetchViaNet(url, LT_Cert, NULL, alloc, certData);
if(crtn) {
tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
}
try {
issuer = new TPCertInfo(clHand,
cspHand,
&certData,
TIC_CopyData,
verifyTime);
}
catch(...) {
tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
alloc.free(certData.Data);
rtnCert = NULL;
return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
}
alloc.free(certData.Data);
if(!issuer->isIssuerOf(subject)) {
tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
}
else {
crtn = subject.verifyWithIssuer(issuer);
if(crtn) {
tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
"fetch\n");
crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
}
}
if(crtn) {
assert(issuer != NULL);
delete issuer;
issuer = NULL;
}
rtnCert = issuer;
return crtn;
}
static CSSM_RETURN tpFetchViaGeneralNames(
const CE_GeneralNames *names,
TPCertInfo &forCert,
TPVerifyContext *verifyContext, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, const char *verifyTime,
TPCertInfo **certInfo,
TPCrlInfo **crlInfo)
{
assert(certInfo || crlInfo);
assert(!certInfo || !crlInfo);
CSSM_RETURN crtn;
for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) {
CE_GeneralName *name = &names->generalName[nameDex];
switch(name->nameType) {
case GNT_URI:
if(name->name.Length < 5) {
continue;
}
if(strncmp((char *)name->name.Data, "ldap:", 5) &&
strncmp((char *)name->name.Data, "http:", 5) &&
strncmp((char *)name->name.Data, "https:", 6)) {
continue;
}
if(certInfo) {
tpDebug(" fetching cert via net");
crtn = tpIssuerCertViaNet(name->name,
clHand,
cspHand,
verifyTime,
forCert,
*certInfo);
}
else {
tpDebug(" fetching CRL via net");
assert(verifyContext != NULL);
crtn = tpCrlViaNet(name->name,
*verifyContext,
forCert,
*crlInfo);
}
switch(crtn) {
case CSSM_OK:
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: return crtn;
default:
break;
}
break;
default:
tpCrlDebug(" tpFetchCrlFromNet: unknown"
"nameType (%u)", (unsigned)name->nameType);
break;
}
}
if(certInfo) {
return CSSMERR_TP_CERTGROUP_INCOMPLETE;
}
else {
return CSSMERR_APPLETP_CRL_NOT_FOUND;
}
}
CSSM_RETURN tpFetchCrlFromNet(
TPCertInfo &cert,
TPVerifyContext &vfyCtx,
TPCrlInfo *&crl) {
CSSM_DATA_PTR fieldValue;
CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
&fieldValue);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CL_NO_FIELD_VALUES:
return CSSMERR_APPLETP_CRL_NOT_FOUND;
default:
return crtn;
}
if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
CE_CRLDistPointsSyntax *dps =
(CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
TPCrlInfo *rtnCrl = NULL;
crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
if(dp->distPointName == NULL) {
continue;
}
switch(dp->distPointName->nameType) {
case CE_CDNT_NameRelativeToCrlIssuer:
tpErrorLog("tpFetchCrlFromNet: "
"CE_CDNT_NameRelativeToCrlIssuerÊnot implemented\n");
break;
case CE_CDNT_FullName:
{
CE_GeneralNames *names = dp->distPointName->dpn.fullName;
crtn = tpFetchViaGeneralNames(names,
cert,
&vfyCtx,
0, 0, vfyCtx.verifyTime,
NULL,
&rtnCrl);
break;
}
default:
tpErrorLog("tpFetchCrlFromNet: "
"unknown distPointName->nameType (%u)\n",
(unsigned)dp->distPointName->nameType);
break;
}
if(crtn == CSSM_OK) {
break;
}
}
cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
if(crtn == CSSM_OK) {
assert(rtnCrl != NULL);
crl = rtnCrl;
}
return crtn;
}
CSSM_RETURN tpFetchIssuerFromNet(
TPCertInfo &subject,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const char *verifyTime,
TPCertInfo *&issuer) {
CSSM_DATA_PTR fieldValue;
CSSM_RETURN crtn = subject.fetchField(&CSSMOID_IssuerAltName,
&fieldValue);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CL_NO_FIELD_VALUES:
return CSSMERR_TP_CERTGROUP_INCOMPLETE;
default:
return crtn;
}
if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
TPCertInfo *rtnCert = NULL;
crtn = tpFetchViaGeneralNames(names,
subject,
NULL, clHand,
cspHand,
verifyTime,
&rtnCert,
NULL);
subject.freeField(&CSSMOID_IssuerAltName, fieldValue);
switch(crtn) {
case CSSM_OK:
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
issuer = rtnCert;
break;
default:
break;
}
return crtn;
}