#include <Security/cssmtype.h>
#include <Security/cssmapi.h>
#include <security_cdsa_utilities/Schema.h>
#include <security_keychain/TrustKeychains.h>
#include <Security/SecCertificatePriv.h>
#include <Security/oidscert.h>
#include "TPDatabase.h"
#include "tpdebugging.h"
#include "certGroupUtils.h"
#include "TPCertInfo.h"
#include "TPCrlInfo.h"
#include "tpCrlVerify.h"
#include "tpTime.h"
static CSSM_DB_UNIQUE_RECORD_PTR tpCertLookup(
CSSM_DL_DB_HANDLE dlDb,
const CSSM_DATA *subjectName, CSSM_HANDLE_PTR resultHand, CSSM_DATA_PTR cert) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
cert->Data = NULL;
cert->Length = 0;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName = (char*) "Subject";
predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(subjectName);
predicate.Attribute.NumberOfValues = 1;
query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
CSSM_DL_DataGetFirst(dlDb,
&query,
resultHand,
NULL, cert,
&record);
return record;
}
TPCertInfo *tpDbFindIssuerCert(
Allocator &alloc,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const TPClItemInfo *subjectItem,
const CSSM_DL_DB_LIST *dbList,
const char *verifyTime, bool &partialIssuerKey, TPCertInfo *oldRoot)
{
StLock<Mutex> _(SecTrustKeychainsGetMutex());
uint32 dbDex;
CSSM_HANDLE resultHand;
CSSM_DATA cert;
CSSM_DL_DB_HANDLE dlDb;
CSSM_DB_UNIQUE_RECORD_PTR record;
TPCertInfo *issuerCert = NULL;
bool foundIt;
TPCertInfo *expiredIssuer = NULL;
TPCertInfo *nonRootIssuer = NULL;
partialIssuerKey = false;
if(dbList == NULL) {
return NULL;
}
for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
dlDb = dbList->DLDBHandle[dbDex];
cert.Data = NULL;
cert.Length = 0;
resultHand = 0;
record = tpCertLookup(dlDb,
subjectItem->issuerName(),
&resultHand,
&cert);
if(record != NULL) {
assert(cert.Data != NULL);
tpDbDebug("tpDbFindIssuerCert: found cert record (1) %p", record);
issuerCert = NULL;
CSSM_RETURN crtn = CSSM_OK;
try {
issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime);
}
catch(...) {
crtn = CSSMERR_TP_INVALID_CERTIFICATE;
}
tpFreePluginMemory(dlDb.DLHandle, cert.Data);
cert.Data = NULL;
cert.Length = 0;
if(crtn == CSSM_OK) {
crtn = subjectItem->verifyWithIssuer(issuerCert);
}
if(crtn == CSSM_OK) {
if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)");
crtn = CSSM_CERT_STATUS_EXPIRED;
if (expiredIssuer) {
expiredIssuer->freeUniqueRecord();
delete expiredIssuer;
}
expiredIssuer = issuerCert;
expiredIssuer->dlDbHandle(dlDb);
expiredIssuer->uniqueRecord(record);
}
}
if(crtn == CSSM_OK && !issuerCert->isSelfSigned()) {
tpDbDebug("tpDbFindIssuerCert: holding non-root cert (1)");
crtn = CSSM_CERT_STATUS_IS_ROOT;
if(!nonRootIssuer ||
(nonRootIssuer && (nonRootIssuer->isExpired() || nonRootIssuer->isNotValidYet()))) {
if(nonRootIssuer) {
nonRootIssuer->freeUniqueRecord();
delete nonRootIssuer;
}
nonRootIssuer = issuerCert;
nonRootIssuer->dlDbHandle(dlDb);
nonRootIssuer->uniqueRecord(record);
}
else {
delete issuerCert;
CSSM_DL_FreeUniqueRecord(dlDb, record);
issuerCert = NULL;
}
}
switch(crtn) {
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
partialIssuerKey = true;
break;
case CSSM_OK:
if((oldRoot == NULL) ||
!tp_CompareCerts(issuerCert->itemData(), oldRoot->itemData())) {
break;
}
default:
if(issuerCert != NULL) {
if(crtn != CSSM_CERT_STATUS_EXPIRED &&
crtn != CSSM_CERT_STATUS_IS_ROOT) {
delete issuerCert;
CSSM_DL_FreeUniqueRecord(dlDb, record);
}
issuerCert = NULL;
}
for(;;) {
cert.Data = NULL;
cert.Length = 0;
record = NULL;
CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb,
resultHand,
NULL, &cert,
&record);
if(crtn) {
assert(cert.Data == NULL);
break;
}
assert(cert.Data != NULL);
tpDbDebug("tpDbFindIssuerCert: found cert record (2) %p", record);
try {
issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData,
verifyTime);
}
catch(...) {
crtn = CSSMERR_TP_INVALID_CERTIFICATE;
}
tpFreePluginMemory(dlDb.DLHandle, cert.Data);
cert.Data = NULL;
cert.Length = 0;
if(crtn == CSSM_OK) {
crtn = subjectItem->verifyWithIssuer(issuerCert);
}
if(crtn == CSSM_OK) {
if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)");
crtn = CSSM_CERT_STATUS_EXPIRED;
if (expiredIssuer) {
expiredIssuer->freeUniqueRecord();
delete expiredIssuer;
}
expiredIssuer = issuerCert;
expiredIssuer->dlDbHandle(dlDb);
expiredIssuer->uniqueRecord(record);
}
}
if(crtn == CSSM_OK && !issuerCert->isSelfSigned()) {
tpDbDebug("tpDbFindIssuerCert: holding non-root cert (2)");
crtn = CSSM_CERT_STATUS_IS_ROOT;
if(!nonRootIssuer ||
(nonRootIssuer && (nonRootIssuer->isExpired() || nonRootIssuer->isNotValidYet()))) {
if(nonRootIssuer) {
nonRootIssuer->freeUniqueRecord();
delete nonRootIssuer;
}
nonRootIssuer = issuerCert;
nonRootIssuer->dlDbHandle(dlDb);
nonRootIssuer->uniqueRecord(record);
}
else {
delete issuerCert;
CSSM_DL_FreeUniqueRecord(dlDb, record);
issuerCert = NULL;
}
}
foundIt = false;
switch(crtn) {
case CSSM_OK:
if((oldRoot == NULL) ||
!tp_CompareCerts(issuerCert->itemData(), oldRoot->itemData())) {
foundIt = true;
}
break;
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
partialIssuerKey = true;
foundIt = true;
break;
default:
break;
}
if(foundIt) {
break;
}
if(issuerCert != NULL) {
if(crtn != CSSM_CERT_STATUS_EXPIRED &&
crtn != CSSM_CERT_STATUS_IS_ROOT) {
delete issuerCert;
CSSM_DL_FreeUniqueRecord(dlDb, record);
}
issuerCert = NULL;
}
}
}
if(record != NULL) {
crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand);
assert(crtn == CSSM_OK);
}
if(issuerCert != NULL) {
tpDbDebug("tpDbFindIssuer: returning record %p", record);
issuerCert->dlDbHandle(dlDb);
issuerCert->uniqueRecord(record);
if(expiredIssuer != NULL) {
tpDbDebug("tpDbFindIssuer: discarding expired cert");
expiredIssuer->freeUniqueRecord();
delete expiredIssuer;
}
if(nonRootIssuer != NULL && nonRootIssuer != expiredIssuer) {
tpDbDebug("tpDbFindIssuer: discarding non-root cert");
nonRootIssuer->freeUniqueRecord();
delete nonRootIssuer;
}
return issuerCert;
}
}
else {
assert(cert.Data == NULL);
assert(resultHand == 0);
}
}
if(nonRootIssuer != NULL) {
tpDbDebug("tpDbFindIssuer: taking non-root issuer cert, record %p",
nonRootIssuer->uniqueRecord());
if(expiredIssuer != NULL && expiredIssuer != nonRootIssuer) {
expiredIssuer->freeUniqueRecord();
delete expiredIssuer;
}
return nonRootIssuer;
}
if(expiredIssuer != NULL) {
tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p",
expiredIssuer->uniqueRecord());
return expiredIssuer;
}
return NULL;
}
#define SEARCH_BY_DATE 1
static CSSM_DB_UNIQUE_RECORD_PTR tpCrlLookup(
CSSM_DL_DB_HANDLE dlDb,
const CSSM_DATA *issuerName, CSSM_TIMESTRING verifyTime, CSSM_HANDLE_PTR resultHand, CSSM_DATA_PTR crl) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE pred[3];
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
char timeStr[CSSM_TIME_STRLEN + 1];
crl->Data = NULL;
crl->Length = 0;
pred[0].DbOperator = CSSM_DB_EQUAL;
pred[0].Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
pred[0].Attribute.Info.Label.AttributeName = (char*) "Issuer";
pred[0].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
pred[0].Attribute.Value = const_cast<CSSM_DATA_PTR>(issuerName);
pred[0].Attribute.NumberOfValues = 1;
if(verifyTime != NULL) {
int rtn = tpTimeToCssmTimestring(verifyTime, (unsigned)strlen(verifyTime), timeStr);
if(rtn) {
tpErrorLog("tpCrlLookup: Invalid VerifyTime string\n");
return NULL;
}
}
else {
StLock<Mutex> _(tpTimeLock());
timeAtNowPlus(0, TIME_CSSM, timeStr);
}
CSSM_DATA timeData;
timeData.Data = (uint8 *)timeStr;
timeData.Length = CSSM_TIME_STRLEN;
#if SEARCH_BY_DATE
pred[1].DbOperator = CSSM_DB_LESS_THAN;
pred[1].Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
pred[1].Attribute.Info.Label.AttributeName = (char*) "NextUpdate";
pred[1].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
pred[1].Attribute.Value = &timeData;
pred[1].Attribute.NumberOfValues = 1;
pred[2].DbOperator = CSSM_DB_GREATER_THAN;
pred[2].Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
pred[2].Attribute.Info.Label.AttributeName = (char*) "ThisUpdate";
pred[2].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
pred[2].Attribute.Value = &timeData;
pred[2].Attribute.NumberOfValues = 1;
#endif
query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
query.Conjunctive = CSSM_DB_AND;
#if SEARCH_BY_DATE
query.NumSelectionPredicates = 3;
#else
query.NumSelectionPredicates = 1;
#endif
query.SelectionPredicate = pred;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
CSSM_DL_DataGetFirst(dlDb,
&query,
resultHand,
NULL, crl,
&record);
return record;
}
TPCrlInfo *tpDbFindIssuerCrl(
TPVerifyContext &vfyCtx,
const CSSM_DATA &issuer,
TPCertInfo &forCert)
{
StLock<Mutex> _(SecTrustKeychainsGetMutex());
uint32 dbDex;
CSSM_HANDLE resultHand;
CSSM_DATA crl;
CSSM_DL_DB_HANDLE dlDb;
CSSM_DB_UNIQUE_RECORD_PTR record;
TPCrlInfo *issuerCrl = NULL;
CSSM_DL_DB_LIST_PTR dbList = vfyCtx.dbList;
CSSM_RETURN crtn;
if(dbList == NULL) {
return NULL;
}
for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
dlDb = dbList->DLDBHandle[dbDex];
crl.Data = NULL;
crl.Length = 0;
record = tpCrlLookup(dlDb,
&issuer,
vfyCtx.verifyTime,
&resultHand,
&crl);
if(record != NULL) {
assert(crl.Data != NULL);
issuerCrl = new TPCrlInfo(vfyCtx.clHand,
vfyCtx.cspHand,
&crl,
TIC_CopyData,
vfyCtx.verifyTime);
tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
crl.Data = NULL;
crl.Length = 0;
CSSM_DL_FreeUniqueRecord(dlDb, record);
crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
if(crtn) {
delete issuerCrl;
issuerCrl = NULL;
for(;;) {
crl.Data = NULL;
crl.Length = 0;
crtn = CSSM_DL_DataGetNext(dlDb,
resultHand,
NULL, &crl,
&record);
if(crtn) {
assert(crl.Data == NULL);
break;
}
assert(crl.Data != NULL);
issuerCrl = new TPCrlInfo(vfyCtx.clHand,
vfyCtx.cspHand,
&crl,
TIC_CopyData,
vfyCtx.verifyTime);
tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
crl.Data = NULL;
crl.Length = 0;
CSSM_DL_FreeUniqueRecord(dlDb, record);
crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
if(crtn == CSSM_OK) {
break;
}
delete issuerCrl;
issuerCrl = NULL;
}
}
if(issuerCrl != NULL) {
CSSM_DL_DataAbortQuery(dlDb, resultHand);
tpDebug("tpDbFindIssuerCrl: found CRL record %p", record);
return issuerCrl;
}
}
else {
assert(crl.Data == NULL);
}
CSSM_DL_DataAbortQuery(dlDb, resultHand);
}
return NULL;
}