#include <stdlib.h>
#include <stdio.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <Security/cuCdsaUtils.h>
#include <Security/cssmerrno.h>
#include <Security/cssm.h>
#include <Security/oidsalg.h>
#include <Security/SecTrust.h>
#define X509_CERT_DB "/System/Library/Keychains/X509Certificates"
#define X509_CRL_DB "/private/var/db/crls/crlcache.db"
static void usage(char **argv)
{
printf("Usage: %s certFileName [options]\n", argv[0]);
printf("Options:\n");
printf(" a allow unverified certs\n");
printf(" d disable CRL verification\n");
printf(" n no network fetch of CRLs\n");
exit(1);
}
static void statusBitTest(
CSSM_TP_APPLE_CERT_STATUS certStatus,
uint32 bit,
const char *str)
{
if(certStatus & bit) {
printf("%s ", str);
}
}
static void printCertInfo(
unsigned numCerts, const CSSM_TP_APPLE_EVIDENCE_INFO *info)
{
CSSM_TP_APPLE_CERT_STATUS cs;
for(unsigned i=0; i<numCerts; i++) {
const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[i];
cs = thisInfo->StatusBits;
printf(" cert %u:\n", i);
printf(" StatusBits : 0x%x", (unsigned)cs);
if(cs) {
printf(" ( ");
statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED");
statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET,
"NOT_VALID_YET");
statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS,
"IS_IN_INPUT_CERTS");
statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS,
"IS_IN_ANCHORS");
statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT");
statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET");
printf(")\n");
}
else {
printf("\n");
}
printf(" NumStatusCodes : %u ",
thisInfo->NumStatusCodes);
for(unsigned j=0; j<thisInfo->NumStatusCodes; j++) {
printf("%s ",
cssmErrorString(thisInfo->StatusCodes[j]).c_str());
}
printf("\n");
printf(" Index: %u\n", thisInfo->Index);
}
return;
}
#define SHOW_ALL_VFY_RESULTS 0
static void dumpVfyResult(
const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult)
{
unsigned numEvidences = vfyResult->NumberOfEvidences;
unsigned numCerts = 0;
printf("Returned evidence:\n");
for(unsigned dex=0; dex<numEvidences; dex++) {
CSSM_EVIDENCE_PTR ev = &vfyResult->Evidence[dex];
#if SHOW_ALL_VFY_RESULTS
printf(" Evidence %u:\n", dex);
#endif
switch(ev->EvidenceForm) {
case CSSM_EVIDENCE_FORM_APPLE_HEADER:
{
#if SHOW_ALL_VFY_RESULTS
const CSSM_TP_APPLE_EVIDENCE_HEADER *hdr =
(const CSSM_TP_APPLE_EVIDENCE_HEADER *)(ev->Evidence);
printf(" Form = HEADER; Version = %u\n", hdr->Version);
#endif
break;
}
case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP:
{
const CSSM_CERTGROUP *grp = (const CSSM_CERTGROUP *)ev->Evidence;
numCerts = grp->NumCerts;
#if SHOW_ALL_VFY_RESULTS
printf(" Form = CERTGROUP; numCerts = %u\n", numCerts);
#endif
break;
}
case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO:
{
const CSSM_TP_APPLE_EVIDENCE_INFO *info =
(const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence;
printCertInfo(numCerts, info);
break;
}
default:
printf("***UNKNOWN Evidence form (%u)\n",
(unsigned)ev->EvidenceForm);
break;
}
}
}
void tpFreeCertGroup(
CSSM_CERTGROUP_PTR certGroup,
CSSM_BOOL freeCertData, CSSM_BOOL freeStruct) {
unsigned dex;
if(certGroup == NULL) {
return;
}
if(freeCertData) {
for(dex=0; dex<certGroup->NumCerts; dex++) {
APP_FREE(certGroup->GroupList.CertList[dex].Data);
}
}
if(certGroup->GroupList.CertList) {
APP_FREE(certGroup->GroupList.CertList);
}
if(freeStruct) {
APP_FREE(certGroup);
}
}
CSSM_RETURN freeVfyResult(
CSSM_TP_VERIFY_CONTEXT_RESULT *ctx)
{
int numCerts = -1;
CSSM_RETURN crtn = CSSM_OK;
for(unsigned i=0; i<ctx->NumberOfEvidences; i++) {
CSSM_EVIDENCE_PTR evp = &ctx->Evidence[i];
switch(evp->EvidenceForm) {
case CSSM_EVIDENCE_FORM_APPLE_HEADER:
APP_FREE(evp->Evidence);
evp->Evidence = NULL;
break;
case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP:
{
CSSM_CERTGROUP_PTR cgp = (CSSM_CERTGROUP_PTR)evp->Evidence;
numCerts = cgp->NumCerts;
tpFreeCertGroup(cgp, CSSM_TRUE, CSSM_TRUE);
evp->Evidence = NULL;
break;
}
case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO:
{
if(numCerts < 0) {
printf("***Malformed VerifyContextResult (2)\n");
crtn = CSSMERR_TP_INTERNAL_ERROR;
break;
}
CSSM_TP_APPLE_EVIDENCE_INFO *evInfo =
(CSSM_TP_APPLE_EVIDENCE_INFO *)evp->Evidence;
for(unsigned k=0; k<(unsigned)numCerts; k++) {
CSSM_TP_APPLE_EVIDENCE_INFO *thisEvInfo =
&evInfo[k];
if(thisEvInfo->StatusCodes) {
APP_FREE(thisEvInfo->StatusCodes);
}
if(thisEvInfo->UniqueRecord) {
CSSM_RETURN crtn =
CSSM_DL_FreeUniqueRecord(thisEvInfo->DlDbHandle,
thisEvInfo->UniqueRecord);
if(crtn) {
cuPrintError("CSSM_DL_FreeUniqueRecord", crtn);
break;
}
thisEvInfo->UniqueRecord = NULL;
}
}
APP_FREE(evp->Evidence);
evp->Evidence = NULL;
break;
}
}
}
if(ctx->Evidence) {
APP_FREE(ctx->Evidence);
ctx->Evidence = NULL;
}
return crtn;
}
static int testError(CSSM_BOOL quiet)
{
char resp;
if(quiet) {
printf("\n***Test aborting.\n");
exit(1);
}
fpurge(stdin);
printf("a to abort, c to continue: ");
resp = getchar();
return (resp == 'a');
}
int vfyCert(
CSSM_TP_HANDLE tpHand,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const void * certData,
unsigned certLength,
bool enableCrlCheck,
bool requireFullCrlVerify,
bool enableFetchFromNet,
CSSM_DL_DB_HANDLE_PTR certKeychain,
CSSM_DL_DB_HANDLE_PTR crlKeychain)
{
CSSM_TP_VERIFY_CONTEXT vfyCtx;
CSSM_TP_CALLERAUTH_CONTEXT authCtx;
CSSM_TP_VERIFY_CONTEXT_RESULT vfyResult;
memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
CSSM_FIELD policyIds[2];
CSSM_APPLE_TP_CRL_OPTIONS crlOpts;
policyIds[0].FieldOid = CSSMOID_APPLE_X509_BASIC;
policyIds[0].FieldValue.Data = NULL;
policyIds[0].FieldValue.Length = 0;
if(enableCrlCheck) {
policyIds[1].FieldOid = CSSMOID_APPLE_TP_REVOCATION_CRL;
policyIds[1].FieldValue.Data = (uint8 *)&crlOpts;
policyIds[1].FieldValue.Length = sizeof(crlOpts);
crlOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
crlOpts.CrlFlags = 0;
if(requireFullCrlVerify) {
crlOpts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT;
}
if(enableFetchFromNet) {
crlOpts.CrlFlags |= CSSM_TP_ACTION_FETCH_CRL_FROM_NET;
}
crlOpts.crlStore = crlKeychain;
authCtx.Policy.NumberOfPolicyIds = 2;
}
else {
authCtx.Policy.NumberOfPolicyIds = 1;
}
authCtx.Policy.PolicyIds = policyIds;
authCtx.Policy.PolicyControl = NULL;
authCtx.VerifyTime = NULL;
authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY;
authCtx.CallbackWithVerifiedCert = NULL;
const CSSM_DATA *anchors;
uint32 anchorCount;
OSStatus ortn;
ortn = SecTrustGetCSSMAnchorCertificates(&anchors, &anchorCount);
if(ortn) {
printf("SecTrustGetCSSMAnchorCertificates returned %u\n", ortn);
return -1;
}
authCtx.NumberOfAnchorCerts = anchorCount;
authCtx.AnchorCerts = const_cast<CSSM_DATA_PTR>(anchors);
CSSM_DL_DB_HANDLE handles[2];
unsigned numDbs = 0;
if(certKeychain != NULL) {
handles[0] = *certKeychain;
numDbs++;
}
if(crlKeychain != NULL) {
handles[numDbs] = *crlKeychain;
numDbs++;
}
CSSM_DL_DB_LIST dlDbList;
dlDbList.NumHandles = numDbs;
dlDbList.DLDBHandle = &handles[0];
authCtx.DBList = &dlDbList;
authCtx.CallerCredentials = NULL;
vfyCtx.ActionData.Data = NULL;
vfyCtx.ActionData.Length = 0;
vfyCtx.Action = CSSM_TP_ACTION_DEFAULT;
vfyCtx.Cred = &authCtx;
CSSM_CERTGROUP cssmCerts;
cssmCerts.CertType = CSSM_CERT_X_509v3;
cssmCerts.CertEncoding = CSSM_CERT_ENCODING_DER;
cssmCerts.NumCerts = 1;
CSSM_DATA cert;
cert.Data = (uint8 *)certData;
cert.Length = certLength;
cssmCerts.GroupList.CertList = &cert;
cssmCerts.CertGroupType = CSSM_CERTGROUP_DATA;
CSSM_RETURN crtn = CSSM_TP_CertGroupVerify(tpHand,
clHand,
cspHand,
&cssmCerts,
&vfyCtx,
&vfyResult);
if(crtn) {
cuPrintError("CSSM_TP_CertGroupVerify", crtn);
}
else {
printf("...verify successful\n");
}
dumpVfyResult(&vfyResult);
freeVfyResult(&vfyResult);
if(crtn) {
return testError(0);
}
else {
return 0;
}
}
int main(int argc, char **argv)
{
int rtn;
CSSM_RETURN crtn;
CSSM_DL_HANDLE dlHand;
CSSM_CSP_HANDLE cspHand;
CSSM_CL_HANDLE clHand;
CSSM_TP_HANDLE tpHand;
CSSM_DL_DB_HANDLE certKeychain;
CSSM_DL_DB_HANDLE crlKeychain;
CSSM_DL_DB_HANDLE_PTR certKeychainPtr = NULL;
CSSM_DL_DB_HANDLE_PTR crlKeychainPtr = NULL;
unsigned char *certData;
unsigned certLength;
bool requireFullCrlVerify = true;
bool enableFetchFromNet = true;
bool enableCrlCheck = true;
int arg;
char *argp;
if(argc < 2) {
usage(argv);
}
for(arg=2; arg<argc; arg++) {
argp = argv[arg];
switch(argp[0]) {
case 'a':
requireFullCrlVerify = false;
break;
case 'd':
enableCrlCheck = false;
break;
case 'n':
enableFetchFromNet = false;
break;
default:
usage(argv);
}
}
cspHand = cuCspStartup(CSSM_TRUE);
clHand = cuClStartup();
tpHand = cuTpStartup();
dlHand = cuDlStartup();
rtn = readFile(argv[1], &certData, &certLength);
if(rtn) {
printf("***Error reading cert file %s\n", argv[1]);
exit(1);
}
crtn = CSSM_DL_DbOpen(dlHand,
X509_CERT_DB,
NULL, CSSM_DB_ACCESS_READ,
NULL, NULL, &certKeychain.DBHandle);
if(crtn) {
cuPrintError("CSSM_DL_DbOpen", crtn);
printf("***Error opening intermediate cert file %s. This op will"
"probably fail.n", X509_CERT_DB);
}
else {
certKeychain.DLHandle = dlHand;
certKeychainPtr = &certKeychain;
}
crtn = CSSM_DL_DbOpen(dlHand,
X509_CRL_DB,
NULL, CSSM_DB_ACCESS_READ,
NULL, NULL, &crlKeychain.DBHandle);
if(crtn == CSSM_OK) {
crlKeychain.DLHandle = dlHand;
crlKeychainPtr = &crlKeychain;
}
vfyCert(tpHand, clHand, cspHand, certData, certLength,
enableCrlCheck, requireFullCrlVerify, enableFetchFromNet,
certKeychainPtr, crlKeychainPtr);
free(certData);
if(crlKeychainPtr != NULL) {
CSSM_DL_DbClose(crlKeychain);
}
if(certKeychainPtr != NULL) {
CSSM_DL_DbClose(certKeychain);
}
CSSM_ModuleDetach(dlHand);
CSSM_ModuleDetach(clHand);
CSSM_ModuleDetach(tpHand);
CSSM_ModuleDetach(cspHand);
return 0;
}