#include <Security/cssmtype.h>
#include <Security/cssmapi.h>
#include <security_cdsa_utilities/Schema.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 = "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) {
uint32 dbDex;
CSSM_HANDLE resultHand;
CSSM_DATA cert;
CSSM_DL_DB_HANDLE dlDb;
CSSM_DB_UNIQUE_RECORD_PTR record;
TPCertInfo *issuerCert = NULL;
bool foundIt;
partialIssuerKey = false;
if(dbList == NULL) {
return NULL;
}
for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
dlDb = dbList->DLDBHandle[dbDex];
cert.Data = NULL;
cert.Length = 0;
record = tpCertLookup(dlDb,
subjectItem->issuerName(),
&resultHand,
&cert);
if(record != NULL) {
assert(cert.Data != NULL);
issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime);
tpFreeCssmData(alloc, &cert, CSSM_FALSE);
cert.Data = NULL;
cert.Length = 0;
CSSM_RETURN crtn = subjectItem->verifyWithIssuer(issuerCert);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
partialIssuerKey = true;
break;
default:
delete issuerCert;
issuerCert = NULL;
CSSM_DL_FreeUniqueRecord(dlDb, record);
for(;;) {
cert.Data = NULL;
cert.Length = 0;
CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb,
resultHand,
NULL, &cert,
&record);
if(crtn) {
assert(cert.Data == NULL);
break;
}
assert(cert.Data != NULL);
issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData,
verifyTime);
tpFreeCssmData(alloc, &cert, CSSM_FALSE);
cert.Data = NULL;
cert.Length = 0;
crtn = subjectItem->verifyWithIssuer(issuerCert);
foundIt = false;
switch(crtn) {
case CSSM_OK:
foundIt = true;
break;
case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
partialIssuerKey = true;
foundIt = true;
break;
default:
break;
}
if(foundIt) {
break;
}
delete issuerCert;
CSSM_DL_FreeUniqueRecord(dlDb, record);
issuerCert = NULL;
}
}
if(issuerCert != NULL) {
tpDebug("tpDbFindIssuer: found cert record %p", record);
CSSM_DL_DataAbortQuery(dlDb, resultHand);
issuerCert->dlDbHandle(dlDb);
issuerCert->uniqueRecord(record);
return issuerCert;
}
}
else {
assert(cert.Data == NULL);
}
CSSM_DL_DataAbortQuery(dlDb, resultHand);
}
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 = "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, 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 = "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 = "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)
{
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;
}
#if WRITE_FETCHED_CRLS_TO_DB
static CSSM_RETURN tpAddCrlSchema(
CSSM_DL_DB_HANDLE dlDbHand)
{
return CSSM_DL_CreateRelation(dlDbHand,
CSSM_DL_DB_RECORD_X509_CRL,
"CSSM_DL_DB_RECORD_X509_CRL",
Security::KeychainCore::Schema::X509CrlSchemaAttributeCount,
Security::KeychainCore::Schema::X509CrlSchemaAttributeList,
Security::KeychainCore::Schema::X509CrlSchemaIndexCount,
Security::KeychainCore::Schema::X509CrlSchemaIndexList);
}
static bool tpSearchNumericExtension(
const CSSM_X509_EXTENSIONS *extens,
const CSSM_OID *oid,
uint32 *val)
{
for(uint32 dex=0; dex<extens->numberOfExtensions; dex++) {
const CSSM_X509_EXTENSION *exten = &extens->extensions[dex];
if(!tpCompareOids(&exten->extnId, oid)) {
continue;
}
if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
tpErrorLog("***Malformed CRL extension\n");
continue;
}
*val = *((uint32 *)exten->value.parsedValue);
return true;
}
return false;
}
#define MAX_CRL_ATTRS 9
CSSM_RETURN tpDbStoreCrl(
TPCrlInfo &crl,
CSSM_DL_DB_HANDLE &dlDbHand)
{
CSSM_DB_ATTRIBUTE_DATA attrs[MAX_CRL_ATTRS];
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0];
CSSM_DATA crlTypeData;
CSSM_DATA crlEncData;
CSSM_RETURN crtn;
CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
CSSM_CRL_ENCODING crlEnc = CSSM_CRL_ENCODING_DER;
const CSSM_X509_TBS_CERTLIST *tbsCrl;
CSSM_CRL_TYPE crlType;
CSSM_DATA thisUpdateData = {0, NULL};
CSSM_DATA nextUpdateData = {0, NULL};
char thisUpdate[CSSM_TIME_STRLEN+1];
char nextUpdate[CSSM_TIME_STRLEN+1];
uint32 crlNumber;
uint32 deltaCrlNumber;
CSSM_DATA crlNumberData;
CSSM_DATA deltaCrlNumberData;
bool crlNumberPresent = false;
bool deltaCrlPresent = false;
tbsCrl = &(crl.x509Crl()->tbsCertList);
if(tbsCrl->version.Length == 0) {
crlType = CSSM_CRL_TYPE_X_509v1;
}
else {
uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1];
switch(vers) {
case 0:
crlType = CSSM_CRL_TYPE_X_509v1;
break;
case 1:
crlType = CSSM_CRL_TYPE_X_509v2;
break;
default:
tpErrorLog("***Unknown version in CRL (%u)\n", vers);
crlType = CSSM_CRL_TYPE_X_509v1;
break;
}
}
crlTypeData.Data = (uint8 *)&crlType;
crlTypeData.Length = sizeof(CSSM_CRL_TYPE);
crlEncData.Data = (uint8 *)&crlEnc;
crlEncData.Length = sizeof(CSSM_CRL_ENCODING);
CSSM_DATA printName;
const CSSM_DATA *printNamePtr;
printNamePtr = SecInferLabelFromX509Name(&tbsCrl->issuer);
if(printNamePtr) {
printName = *(const_cast<CSSM_DATA *>(printNamePtr));
}
else {
printName.Data = (uint8 *)"X509 CRL";
printName.Length = 8;
}
int rtn = tpTimeToCssmTimestring((const char *)tbsCrl->thisUpdate.time.Data,
tbsCrl->thisUpdate.time.Length,
thisUpdate);
if(rtn) {
tpErrorLog("***Badly formatted thisUpdate\n");
}
else {
thisUpdateData.Data = (uint8 *)thisUpdate;
thisUpdateData.Length = CSSM_TIME_STRLEN;
}
if(tbsCrl->nextUpdate.time.Data != NULL) {
rtn = tpTimeToCssmTimestring((const char *)tbsCrl->nextUpdate.time.Data,
tbsCrl->nextUpdate.time.Length,
nextUpdate);
if(rtn) {
tpErrorLog("***Badly formatted nextUpdate\n");
}
else {
nextUpdateData.Data = (uint8 *)nextUpdate;
nextUpdateData.Length = CSSM_TIME_STRLEN;
}
}
else {
tpTimeToCssmTimestring(CSSM_APPLE_CRL_END_OF_TIME,
strlen(CSSM_APPLE_CRL_END_OF_TIME), nextUpdate);
nextUpdateData.Data = (uint8 *)nextUpdate;
nextUpdateData.Length = CSSM_TIME_STRLEN;
}
if(tpSearchNumericExtension(&tbsCrl->extensions,
&CSSMOID_CrlNumber,
&crlNumber)) {
crlNumberData.Data = (uint8 *)&crlNumber;
crlNumberData.Length = sizeof(uint32);
crlNumberPresent = true;
}
if(tpSearchNumericExtension(&tbsCrl->extensions,
&CSSMOID_DeltaCrlIndicator,
&deltaCrlNumber)) {
deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber;
deltaCrlNumberData.Length = sizeof(uint32);
deltaCrlPresent = true;
}
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "CrlType";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &crlTypeData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "CrlEncoding";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &crlEncData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "PrintName";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &printName;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "Issuer";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = const_cast<CSSM_DATA *>(crl.issuerName());
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "ThisUpdate";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &thisUpdateData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "NextUpdate";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &nextUpdateData;
attr++;
CSSM_DATA uri = *crl.uri();
if(uri.Data != NULL) {
if(uri.Data[uri.Length - 1] == 0) {
uri.Length--;
}
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "URI";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &uri;
attr++;
}
if(crlNumberPresent) {
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "CrlNumber";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &crlNumberData;
attr++;
}
if(deltaCrlPresent) {
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = "DeltaCrlNumber";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &deltaCrlNumberData;
attr++;
}
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = attr - attrs;
recordAttrs.AttributeData = attrs;
crtn = CSSM_DL_DataInsert(dlDbHand,
CSSM_DL_DB_RECORD_X509_CRL,
&recordAttrs,
crl.itemData(),
&recordPtr);
if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
crtn = tpAddCrlSchema(dlDbHand);
if(crtn == CSSM_OK) {
crtn = CSSM_DL_DataInsert(dlDbHand,
CSSM_DL_DB_RECORD_X509_CRL,
&recordAttrs,
crl.itemData(),
&recordPtr);
}
}
if(crtn) {
tpErrorLog("CSSM_DL_DataInsert: %ld", crtn);
}
else {
CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
}
return crtn;
}
#endif