#include "TPCertInfo.h"
#include "tpdebugging.h"
#include "tpTime.h"
#include "certGroupUtils.h"
#include <Security/cssmapi.h>
#include <Security/x509defs.h>
#include <Security/oidscert.h>
#include <Security/oidsalg.h>
#include <string.h>
#include <Security/threading.h>
#include <Security/globalizer.h>
#include <Security/debugging.h>
#include <Security/cssmapple.h>
#define tpTimeDbg(args...) debug("tpTime", ## args)
TPCertInfo::TPCertInfo(
const CSSM_DATA *certData,
CSSM_CL_HANDLE clHand,
const char *cssmTimeStr, bool copyCertData) : mClHand(clHand),
mCacheHand(CSSM_INVALID_HANDLE),
mSubjectName(NULL),
mIssuerName(NULL),
mIndex(0),
mIsAnchor(false),
mIsFromDb(false),
mNumStatusCodes(0),
mStatusCodes(NULL),
mUniqueRecord(NULL)
{
CSSM_RETURN crtn;
mDlDbHandle.DBHandle = 0;
mDlDbHandle.DLHandle = 0;
if(copyCertData) {
mCertData = tpMallocCopyCssmData(CssmAllocator::standard(), certData);
}
else {
mCertData = const_cast<CSSM_DATA *>(certData);
}
mWeOwnTheData = copyCertData;
mClHand = clHand;
crtn = CSSM_CL_CertCache(clHand, mCertData, &mCacheHand);
if(crtn) {
CssmError::throwMe(crtn);
}
crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
if(crtn) {
releaseResources();
CssmError::throwMe(crtn);
}
crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
if(crtn) {
releaseResources();
CssmError::throwMe(crtn);
}
mIsRoot = tpCompareCssmData(mSubjectName, mIssuerName) ? true : false;
calculateCurrent(cssmTimeStr);
}
TPCertInfo::~TPCertInfo()
{
releaseResources();
}
void TPCertInfo::releaseResources()
{
if(mWeOwnTheData && (mCertData != NULL)) {
tpFreeCssmData(CssmAllocator::standard(), mCertData, CSSM_TRUE);
}
if(mSubjectName) {
freeField(&CSSMOID_X509V1SubjectName, mSubjectName);
}
if(mIssuerName) {
freeField(&CSSMOID_X509V1IssuerName, mIssuerName);
}
if(mCacheHand != CSSM_INVALID_HANDLE) {
CSSM_CL_CertAbortCache(mClHand, mCacheHand);
}
if(mStatusCodes) {
free(mStatusCodes);
}
}
CSSM_RETURN TPCertInfo::fetchField(
const CSSM_OID *fieldOid,
CSSM_DATA_PTR *fieldData) {
CSSM_RETURN crtn;
uint32 NumberOfFields = 0;
CSSM_HANDLE resultHand = 0;
*fieldData = NULL;
crtn = CSSM_CL_CertGetFirstCachedFieldValue(
mClHand,
mCacheHand,
fieldOid,
&resultHand,
&NumberOfFields,
fieldData);
if(crtn) {
return crtn;
}
if(NumberOfFields != 1) {
errorLog1("TPCertInfo::fetchField: numFields %d, expected 1\n",
(int)NumberOfFields);
}
CSSM_CL_CertAbortQuery(mClHand, resultHand);
return CSSM_OK;
}
CSSM_RETURN TPCertInfo::freeField(
const CSSM_OID *fieldOid,
CSSM_DATA_PTR fieldData)
{
return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
}
CSSM_CL_HANDLE TPCertInfo::clHand()
{
return mClHand;
}
CSSM_HANDLE TPCertInfo::cacheHand()
{
return mCacheHand;
}
const CSSM_DATA *TPCertInfo::certData()
{
CASSERT(mCertData != NULL);
return mCertData;
}
const CSSM_DATA *TPCertInfo::subjectName()
{
CASSERT(mSubjectName != NULL);
return mSubjectName;
}
const CSSM_DATA *TPCertInfo::issuerName()
{
CASSERT(mIssuerName != NULL);
return mIssuerName;
}
ModuleNexus<Mutex> tpTimeLock;
void TPCertInfo::calculateCurrent(
const char *cssmTimeStr )
{
CSSM_DATA_PTR notBeforeField = NULL;
CSSM_DATA_PTR notAfterField = NULL;
CSSM_RETURN crtn = CSSM_OK;
CSSM_X509_TIME *xNotAfter;
CASSERT(mCacheHand != CSSM_INVALID_HANDLE);
crtn = fetchField(&CSSMOID_X509V1ValidityNotBefore, ¬BeforeField);
if(crtn) {
errorLog0("TPCertInfo::calculateCurrent: GetField error");
CssmError::throwMe(crtn);
}
struct tm now;
if(cssmTimeStr != NULL) {
if(timeStringToTm(cssmTimeStr, strlen(cssmTimeStr), &now)) {
errorLog0("TPCertInfo::calculateCurrent: timeStringToTm error");
CssmError::throwMe(CSSMERR_TP_INVALID_TIMESTRING);
}
}
else {
StLock<Mutex> _(tpTimeLock());
nowTime(&now);
}
struct tm notBefore;
CSSM_X509_TIME *xNotBefore = (CSSM_X509_TIME *)notBeforeField->Data;
if(timeStringToTm((char *)xNotBefore->time.Data, xNotBefore->time.Length,
¬Before)) {
errorLog0("TPCertInfo::calculateCurrent: malformed notBefore time\n");
crtn = CSSMERR_TP_INVALID_CERT_POINTER;
goto errOut;
}
if(compareTimes(&now, ¬Before) < 0) {
mNotValidYet = true;
tpTimeDbg("\nTP_CERT_NOT_VALID_YET:\n now y:%d m:%d d:%d h:%d m:%d",
now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour,
now.tm_min);
tpTimeDbg(" notBefore y:%d m:%d d:%d h:%d m:%d",
notBefore.tm_year, notBefore.tm_mon, notBefore.tm_mday,
notBefore.tm_hour, notBefore.tm_min);
}
else {
mNotValidYet = false;
}
struct tm notAfter;
crtn = fetchField(&CSSMOID_X509V1ValidityNotAfter, ¬AfterField);
if(crtn) {
errorLog0("TPCertInfo::calculateCurrent: GetField error");
goto errOut;
}
xNotAfter = (CSSM_X509_TIME *)notAfterField->Data;
if(timeStringToTm((char *)xNotAfter->time.Data, xNotAfter->time.Length,
¬After)) {
errorLog0("TPCertInfo::calculateCurrent: malformed notAfter time\n");
crtn = CSSMERR_TP_INVALID_CERT_POINTER;
goto errOut;
}
else if(compareTimes(&now, ¬After) > 0) {
crtn = CSSMERR_TP_CERT_EXPIRED;
tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
"h:%d m:%d",
now.tm_year, now.tm_mon, now.tm_mday,
now.tm_hour, now.tm_min);
tpTimeDbg(" notAfter y:%d m:%d d:%d h:%d m:%d",
notAfter.tm_year, notAfter.tm_mon, notAfter.tm_mday,
notAfter.tm_hour, notAfter.tm_min);
mExpired = true;
}
else {
mExpired = false;
}
crtn = CSSM_OK;
errOut:
if(notAfterField) {
freeField(&CSSMOID_X509V1ValidityNotAfter, notAfterField);
}
if(notBeforeField) {
freeField(&CSSMOID_X509V1ValidityNotBefore, notBeforeField);
}
if(crtn != CSSM_OK) {
CssmError::throwMe(crtn);
}
}
CSSM_RETURN TPCertInfo::isCurrent(
CSSM_BOOL allowExpired)
{
if(mNotValidYet) {
return CSSMERR_TP_CERT_NOT_VALID_YET;
}
if(allowExpired || !mExpired) {
return CSSM_OK;
}
else {
return CSSMERR_TP_CERT_EXPIRED;
}
}
void TPCertInfo::addStatusCode(CSSM_RETURN code)
{
mNumStatusCodes++;
mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes,
mNumStatusCodes * sizeof(CSSM_RETURN));
mStatusCodes[mNumStatusCodes - 1] = code;
}
TPCertGroup::TPCertGroup(
CssmAllocator &alloc,
unsigned numCerts) :
mAlloc(alloc),
mNumCerts(0)
{
mCertInfo = (TPCertInfo **)alloc.malloc(numCerts * sizeof(TPCertInfo *));
mSizeofCertInfo = numCerts;
}
TPCertGroup::~TPCertGroup()
{
unsigned i;
for(i=0; i<mNumCerts; i++) {
delete mCertInfo[i];
}
mAlloc.free(mCertInfo);
}
void TPCertGroup::appendCert(
TPCertInfo *certInfo) {
if(mNumCerts == mSizeofCertInfo) {
mSizeofCertInfo *= 2;
mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo,
mSizeofCertInfo * sizeof(TPCertInfo *));
}
mCertInfo[mNumCerts++] = certInfo;
}
TPCertInfo *TPCertGroup::certAtIndex(
unsigned index)
{
if(index > (mNumCerts - 1)) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
return mCertInfo[index];
}
TPCertInfo *TPCertGroup::removeCertAtIndex(
unsigned index) {
if(index > (mNumCerts - 1)) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
TPCertInfo *rtn = mCertInfo[index];
unsigned i;
for(i=index; i<(mNumCerts - 1); i++) {
mCertInfo[i] = mCertInfo[i+1];
}
mNumCerts--;
return rtn;
}
unsigned TPCertGroup::numCerts()
{
return mNumCerts;
}
TPCertInfo *TPCertGroup::firstCert()
{
if(mNumCerts == 0) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
else {
return mCertInfo[0];
}
}
TPCertInfo *TPCertGroup::lastCert()
{
if(mNumCerts == 0) {
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
else {
return mCertInfo[mNumCerts - 1];
}
}
CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup()
{
CSSM_CERTGROUP_PTR cgrp =
(CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP));
cgrp->NumCerts = mNumCerts;
cgrp->CertGroupType = CSSM_CERTGROUP_DATA;
cgrp->CertType = CSSM_CERT_X_509v3;
cgrp->CertEncoding = CSSM_CERT_ENCODING_DER;
if(mNumCerts == 0) {
cgrp->GroupList.CertList = NULL;
return cgrp;
}
cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts,
sizeof(CSSM_DATA));
for(unsigned i=0; i<mNumCerts; i++) {
tpCopyCssmData(mAlloc, mCertInfo[i]->certData(),
&cgrp->GroupList.CertList[i]);
}
return cgrp;
}
CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo()
{
CSSM_TP_APPLE_EVIDENCE_INFO *infoArray;
infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)mAlloc.calloc(mNumCerts,
sizeof(CSSM_TP_APPLE_EVIDENCE_INFO));
for(unsigned i=0; i<mNumCerts; i++) {
TPCertInfo *certInfo = mCertInfo[i];
CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[i];
if(certInfo->isExpired()) {
evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED;
}
if(certInfo->isNotValidYet()) {
evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET;
}
if(certInfo->dlDbHandle().DLHandle == 0) {
if(certInfo->isAnchor()) {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS;
}
else {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS;
}
}
if(certInfo->isSelfSigned()) {
evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT;
}
unsigned numCodes = certInfo->numStatusCodes();
if(numCodes) {
evInfo->NumStatusCodes = numCodes;
evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes,
sizeof(CSSM_RETURN));
for(unsigned j=0; j<numCodes; j++) {
evInfo->StatusCodes[j] = (certInfo->statusCodes())[j];
}
}
evInfo->Index = certInfo->index();
evInfo->DlDbHandle = certInfo->dlDbHandle();
evInfo->UniqueRecord = certInfo->uniqueRecord();
}
return infoArray;
}
CSSM_RETURN TPCertGroup::getReturnCode(
CSSM_RETURN constructStatus,
CSSM_BOOL allowExpired,
CSSM_BOOL allowExpiredRoot,
CSSM_RETURN policyStatus )
{
if(constructStatus) {
return constructStatus;
}
bool expired = false;
bool notValid = false;
for(unsigned i=0; i<mNumCerts; i++) {
if(mCertInfo[i]->isExpired() &&
!(allowExpiredRoot && mCertInfo[i]->isSelfSigned())) {
expired = true;
}
if(mCertInfo[i]->isNotValidYet()) {
notValid = true;
}
}
if(expired && !allowExpired) {
return CSSMERR_TP_CERT_EXPIRED;
}
if(notValid) {
return CSSMERR_TP_CERT_NOT_VALID_YET;
}
return policyStatus;
}