#include "cuCdsaUtils.h"
#include "cuTimeStr.h"
#include "cuDbUtils.h"
#include "cuPrintCert.h"
#include <stdlib.h>
#include <stdio.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/cssmapple.h>
#include <Security/oidscert.h>
#include <Security/oidscrl.h>
#include <Security/oidsattr.h>
#include <strings.h>
#include <security_cdsa_utilities/Schema.h>
#ifndef NDEBUG
#define dprintf(args...) printf(args)
#else
#define dprintf(args...)
#endif
CSSM_RETURN cuAddCertToDb(
CSSM_DL_DB_HANDLE dlDbHand,
const CSSM_DATA *cert,
CSSM_CERT_TYPE certType,
CSSM_CERT_ENCODING certEncoding,
const char *printName, const CSSM_DATA *publicKeyHash)
{
CSSM_DB_ATTRIBUTE_DATA attrs[6];
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0];
CSSM_DATA certTypeData;
CSSM_DATA certEncData;
CSSM_DATA printNameData;
CSSM_RETURN crtn;
CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
CSSM_DATA issuer = {6, (uint8 *)"issuer"};
CSSM_DATA serial = {6, (uint8 *)"serial"};
certTypeData.Data = (uint8 *)&certType;
certTypeData.Length = sizeof(CSSM_CERT_TYPE);
certEncData.Data = (uint8 *)&certEncoding;
certEncData.Length = sizeof(CSSM_CERT_ENCODING);
printNameData.Data = (uint8 *)printName;
printNameData.Length = strlen(printName) + 1;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "CertType";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &certTypeData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "CertEncoding";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &certEncData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "PrintName";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &printNameData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "PublicKeyHash";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = (CSSM_DATA_PTR)publicKeyHash;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "Issuer";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &issuer;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "SerialNumber";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &serial;
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = 6;
recordAttrs.AttributeData = attrs;
crtn = CSSM_DL_DataInsert(dlDbHand,
CSSM_DL_DB_RECORD_X509_CERTIFICATE,
&recordAttrs,
cert,
&recordPtr);
if(crtn) {
cuPrintError("CSSM_DL_DataInsert", crtn);
}
else {
CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
}
return crtn;
}
static CSSM_RETURN cuAddCrlSchema(
CSSM_DL_DB_HANDLE dlDbHand);
static void cuInferCrlLabel(
const CSSM_X509_NAME *x509Name,
CSSM_DATA *label) {
const CSSM_DATA *printValue = SecInferLabelFromX509Name(x509Name);
if(printValue == NULL) {
label->Data = (uint8 *)"X509 CRL";
label->Length = 8;
}
else {
*label = *printValue;
}
}
static bool cuSearchNumericExtension(
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(!cuCompareOid(&exten->extnId, oid)) {
continue;
}
if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
dprintf("***Malformed extension\n");
continue;
}
*val = *((uint32 *)exten->value.parsedValue);
return true;
}
return false;
}
#define MAX_CRL_ATTRS 9
CSSM_RETURN cuAddCrlToDb(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_CL_HANDLE clHand,
const CSSM_DATA *crl,
const CSSM_DATA *URI) {
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_DATA printNameData;
CSSM_RETURN crtn;
CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
CSSM_DATA_PTR issuer = NULL; CSSM_DATA_PTR crlValue = NULL; uint32 numFields;
CSSM_HANDLE result;
CSSM_CRL_ENCODING crlEnc = CSSM_CRL_ENCODING_DER;
const CSSM_X509_SIGNED_CRL *signedCrl;
const CSSM_X509_TBS_CERTLIST *tbsCrl;
CSSM_CRL_TYPE crlType;
CSSM_DATA thisUpdateData = {0, NULL};
CSSM_DATA nextUpdateData = {0, NULL};
char *thisUpdate = NULL;
char *nextUpdate = NULL;
unsigned timeLen;
uint32 crlNumber;
uint32 deltaCrlNumber;
CSSM_DATA crlNumberData;
CSSM_DATA deltaCrlNumberData;
bool crlNumberPresent = false;
bool deltaCrlPresent = false;
CSSM_DATA attrUri;
crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
crl,
&CSSMOID_X509V1IssuerName,
&result,
&numFields,
&issuer);
if(crtn) {
cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
return crtn;
}
CSSM_CL_CrlAbortQuery(clHand, result);
crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
crl,
&CSSMOID_X509V2CRLSignedCrlCStruct,
&result,
&numFields,
&crlValue);
if(crtn) {
cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
goto errOut;
}
CSSM_CL_CrlAbortQuery(clHand, result);
if(crlValue == NULL) {
dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
crtn = CSSMERR_CL_INVALID_CRL_POINTER;
goto errOut;
}
if((crlValue->Data == NULL) ||
(crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) {
dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
crtn = CSSMERR_CL_INVALID_CRL_POINTER;
goto errOut;
}
signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data;
tbsCrl = &signedCrl->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:
dprintf("***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);
cuInferCrlLabel(&tbsCrl->issuer, &printNameData);
thisUpdate = cuX509TimeToCssmTimestring(&tbsCrl->thisUpdate, &timeLen);
if(thisUpdate == NULL) {
dprintf("***Badly formatted thisUpdate\n");
}
else {
thisUpdateData.Data = (uint8 *)thisUpdate;
thisUpdateData.Length = timeLen;
}
if(tbsCrl->nextUpdate.time.Data != NULL) {
nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen);
if(nextUpdate == NULL) {
dprintf("***Badly formatted nextUpdate\n");
}
else {
nextUpdateData.Data = (uint8 *)nextUpdate;
nextUpdateData.Length = timeLen;
}
}
else {
CSSM_X509_TIME tempTime = { 0, { strlen(CSSM_APPLE_CRL_END_OF_TIME),
(uint8 *)CSSM_APPLE_CRL_END_OF_TIME} };
nextUpdate = cuX509TimeToCssmTimestring(&tempTime, &timeLen);
nextUpdateData.Data = (uint8 *)nextUpdate;
nextUpdateData.Length = CSSM_TIME_STRLEN;
}
if(cuSearchNumericExtension(&tbsCrl->extensions,
&CSSMOID_CrlNumber,
&crlNumber)) {
crlNumberData.Data = (uint8 *)&crlNumber;
crlNumberData.Length = sizeof(uint32);
crlNumberPresent = true;
}
if(cuSearchNumericExtension(&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 = (char*) "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 = (char*) "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 = (char*) "PrintName";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &printNameData;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "Issuer";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = issuer;
attr++;
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "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 = (char*) "NextUpdate";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &nextUpdateData;
attr++;
if(crlNumberPresent) {
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "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 = (char*) "DeltaCrlNumber";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
attr->NumberOfValues = 1;
attr->Value = &deltaCrlNumberData;
attr++;
}
if(URI) {
attrUri = *URI;
if((attrUri.Length != 0) &&
(attrUri.Data[attrUri.Length - 1] == 0)) {
attrUri.Length--;
}
attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr->Info.Label.AttributeName = (char*) "URI";
attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr->NumberOfValues = 1;
attr->Value = &attrUri;
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,
&recordPtr);
if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
crtn = cuAddCrlSchema(dlDbHand);
if(crtn == CSSM_OK) {
crtn = CSSM_DL_DataInsert(dlDbHand,
CSSM_DL_DB_RECORD_X509_CRL,
&recordAttrs,
crl,
&recordPtr);
}
}
if(crtn == CSSM_OK) {
CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
}
errOut:
if(issuer) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer);
}
if(crlValue) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue);
}
if(thisUpdate) {
free(thisUpdate);
}
if(nextUpdate) {
free(nextUpdate);
}
return crtn;
}
static CSSM_RETURN cuAddCrlSchema(
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);
}
CSSM_RETURN cuDumpCrlsCerts(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_CL_HANDLE clHand,
CSSM_BOOL isCert,
unsigned &numItems, CSSM_BOOL verbose)
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_HANDLE resultHand;
CSSM_RETURN crtn;
CSSM_DATA certCrl;
const char *itemStr;
numItems = 0;
itemStr = isCert ? "Certificate" : "CRL";
if(isCert) {
query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
}
else {
query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
}
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 0;
query.SelectionPredicate = NULL;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
certCrl.Data = NULL;
certCrl.Length = 0;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
NULL, &certCrl,
&record);
switch(crtn) {
case CSSM_OK:
break; case CSSMERR_DL_ENDOFDATA:
return CSSM_OK;
case CSSMERR_DL_INVALID_RECORDTYPE:
return crtn;
default:
cuPrintError("DataGetFirst", crtn);
return crtn;
}
dprintf("%s %u:\n", itemStr, numItems);
if(isCert) {
printCert(certCrl.Data, certCrl.Length, verbose);
}
else {
printCrl(certCrl.Data, certCrl.Length, verbose);
}
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
APP_FREE(certCrl.Data);
certCrl.Data = NULL;
certCrl.Length = 0;
numItems++;
for(;;) {
crtn = CSSM_DL_DataGetNext(dlDbHand,
resultHand,
NULL,
&certCrl,
&record);
switch(crtn) {
case CSSM_OK:
dprintf("%s %u:\n", itemStr, numItems);
if(isCert) {
printCert(certCrl.Data, certCrl.Length, verbose);
}
else {
printCrl(certCrl.Data, certCrl.Length, verbose);
}
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
APP_FREE(certCrl.Data);
certCrl.Data = NULL;
certCrl.Length = 0;
numItems++;
break; case CSSMERR_DL_ENDOFDATA:
return CSSM_OK;
default:
cuPrintError("DataGetNext", crtn);
return crtn;
}
}
}