#include "certVerify.h"
#include "tpUtils.h"
#include <utilLib/common.h>
#include <clAppUtils/clutils.h>
#include <clAppUtils/tpUtils.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Security/cssm.h>
#include <Security/oidsalg.h>
#include <Security/SecTrust.h>
#include <Security/SecPolicySearch.h>
#include <Security/cssmapplePriv.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <Security/TrustSettingsSchema.h>
static int vfyCertErrors(
const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult,
unsigned numCertErrors,
const char **certErrors, CSSM_BOOL quiet)
{
if(numCertErrors == 0) {
return 0;
}
if(vfyResult->NumberOfEvidences != 3) {
printf("***vfyCertErrors: NumberOfEvidences is %u, expect 3\n",
(unsigned)vfyResult->NumberOfEvidences);
return 1;
}
const CSSM_EVIDENCE *ev = &vfyResult->Evidence[1];
const CSSM_CERTGROUP *grp = (const CSSM_CERTGROUP *)ev->Evidence;
unsigned numCerts = grp->NumCerts;
ev = &vfyResult->Evidence[2];
const CSSM_TP_APPLE_EVIDENCE_INFO *info =
(const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence;
int ourRtn = 0;
for(unsigned dex=0; dex<numCertErrors; dex++) {
const char *str = certErrors[dex];
char buf[8];
unsigned i;
for(i=0; *str != '\0'; i++, str++) {
if(*str == ':') {
break;
}
buf[i] = *str;
}
if(*str != ':') {
printf("***Bad certerror value, format is certNum:errorString\n");
return 1;
}
buf[i] = '\0';
unsigned certNum = atoi(buf);
if(certNum > (numCerts-1)) {
printf("***certerror specified for cert %u, but only %u certs"
" available\n", certNum, numCerts);
return 1;
}
str++;
const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[certNum];
char *found = NULL;
for(unsigned i=0; i<thisInfo->NumStatusCodes; i++) {
CSSM_RETURN actRtn = thisInfo->StatusCodes[i];
const char *actRtnStr = cssmErrToStr(actRtn);
found = strstr(actRtnStr, str);
if(found) {
break;
}
}
if(found) {
if(!quiet) {
printf("...%s per-cert status received as expected\n", str);
}
}
else {
printf("***Per cert status %s not found\n", str);
ourRtn = 1;
}
}
return ourRtn;
}
static int vfyCertStatus(
const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult,
unsigned numCertStatus,
const char **certStatus, CSSM_BOOL quiet)
{
if(numCertStatus == 0) {
return 0;
}
if(vfyResult->NumberOfEvidences != 3) {
printf("***vfyCertStatus: NumberOfEvidences is %u, expect 3\n",
(unsigned)vfyResult->NumberOfEvidences);
return 1;
}
const CSSM_EVIDENCE *ev = &vfyResult->Evidence[1];
const CSSM_CERTGROUP *grp = (const CSSM_CERTGROUP *)ev->Evidence;
unsigned numCerts = grp->NumCerts;
ev = &vfyResult->Evidence[2];
const CSSM_TP_APPLE_EVIDENCE_INFO *info =
(const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence;
int ourRtn = 0;
for(unsigned dex=0; dex<numCertStatus; dex++) {
const char *str = certStatus[dex];
char buf[8];
unsigned i;
for(i=0; *str != '\0'; i++, str++) {
if(*str == ':') {
break;
}
buf[i] = *str;
}
if(*str != ':') {
printf("***Bad certstatus value, format is certNum:status_in_hex\n");
return 1;
}
buf[i] = '\0';
unsigned certNum = atoi(buf);
if(certNum > (numCerts-1)) {
printf("***certerror specified for cert %u, but only %u certs"
" available\n", certNum, numCerts);
return 1;
}
str++; unsigned certStat = hexToBin(str);
const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[certNum];
if(certStat == thisInfo->StatusBits) {
if(!quiet) {
printf("...0x%x per-cert status received as expected\n", certStat);
}
}
else {
printf("***Expected per cert status 0x%x, got 0x%x\n",
(unsigned)certStat, (unsigned)thisInfo->StatusBits);
ourRtn = 1;
}
}
return ourRtn;
}
static int verifySecPolicy(
const CSSM_OID *oid)
{
SecPolicySearchRef srchRef = NULL;
OSStatus ortn;
ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, oid, NULL, &srchRef);
if(ortn) {
cssmPerror("SecPolicySearchCreate", ortn);
return -1;
}
SecPolicyRef policyRef = NULL;
ortn = SecPolicySearchCopyNext(srchRef, &policyRef);
if(ortn) {
cssmPerror("SecPolicySearchCopyNext", ortn);
printf("***The TP policy used in this test is not accessible via SecPolicySearchCopyNext().\n");
printf(" You probably forgot to add the policy to the theOidList table in PolicyCursor.cpp\n");
printf(" in the libsecurity_keychain project.\n");
}
CFRelease(srchRef);
if(policyRef) {
CFRelease(policyRef);
}
return ortn;
}
int certVerify(CertVerifyArgs *vfyArgs)
{
if(vfyArgs->version != CERT_VFY_ARGS_VERS) {
printf("***CertVerifyArgs.Version mismatch. Clean and rebuild.\n");
return -1;
}
CSSM_TP_VERIFY_CONTEXT vfyCtx;
CSSM_TP_CALLERAUTH_CONTEXT authCtx;
CSSM_TP_VERIFY_CONTEXT_RESULT vfyResult;
CSSM_APPLE_TP_SSL_OPTIONS sslOpts;
CSSM_APPLE_TP_SMIME_OPTIONS smimeOpts;
memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
CSSM_FIELD policyIds[3];
CSSM_FIELD *policyPtr = &policyIds[0];
uint32 numPolicies = 0;
memset(policyIds, 0, 3 * sizeof(CSSM_FIELD));
switch(vfyArgs->vfyPolicy) {
case CVP_SSL:
case CVP_IPSec:
if(vfyArgs->vfyPolicy == CVP_SSL) {
policyPtr->FieldOid = CSSMOID_APPLE_TP_SSL;
}
else {
policyPtr->FieldOid = CSSMOID_APPLE_TP_IP_SEC;
}
if((vfyArgs->sslHost != NULL) || vfyArgs->sslClient) {
memset(&sslOpts, 0, sizeof(sslOpts));
sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
sslOpts.ServerName = vfyArgs->sslHost;
if(vfyArgs->sslHost != NULL) {
sslOpts.ServerNameLen = strlen(vfyArgs->sslHost) + 1;
}
if(vfyArgs->sslClient) {
sslOpts.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
}
policyPtr->FieldValue.Data = (uint8 *)&sslOpts;
policyPtr->FieldValue.Length = sizeof(sslOpts);
}
break;
case CVP_SMIME:
case CVP_iChat:
if(vfyArgs->vfyPolicy == CVP_SMIME) {
policyPtr->FieldOid = CSSMOID_APPLE_TP_SMIME;
}
else {
policyPtr->FieldOid = CSSMOID_APPLE_TP_ICHAT;
}
if(vfyArgs->senderEmail != NULL) {
smimeOpts.Version = CSSM_APPLE_TP_SMIME_OPTS_VERSION;
smimeOpts.IntendedUsage = vfyArgs->intendedKeyUse;
smimeOpts.SenderEmail = vfyArgs->senderEmail;
smimeOpts.SenderEmailLen = strlen(vfyArgs->senderEmail) + 1;
policyPtr->FieldValue.Data = (uint8 *)&smimeOpts;
policyPtr->FieldValue.Length = sizeof(smimeOpts);
}
break;
case CVP_Basic:
policyPtr->FieldOid = CSSMOID_APPLE_X509_BASIC;
break;
case CVP_SWUpdateSign:
policyPtr->FieldOid = CSSMOID_APPLE_TP_SW_UPDATE_SIGNING;
break;
case CVP_ResourceSigning:
policyPtr->FieldOid = CSSMOID_APPLE_TP_RESOURCE_SIGN;
break;
case CVP_PKINIT_Server:
policyPtr->FieldOid = CSSMOID_APPLE_TP_PKINIT_SERVER;
break;
case CVP_PKINIT_Client:
policyPtr->FieldOid = CSSMOID_APPLE_TP_PKINIT_CLIENT;
break;
case CVP_AppleCodeSigning:
policyPtr->FieldOid = CSSMOID_APPLE_TP_CODE_SIGNING;
break;
case CVP_PackageSigning:
policyPtr->FieldOid = CSSMOID_APPLE_TP_PACKAGE_SIGNING;
break;
default:
printf("***certVerify: bogus vfyPolicy\n");
return 1;
}
if(verifySecPolicy(&policyPtr->FieldOid)) {
return -1;
}
policyPtr++;
numPolicies++;
CSSM_APPLE_TP_CRL_OPTIONS crlOpts;
if((vfyArgs->revokePolicy == CRP_CRL) || (vfyArgs->revokePolicy == CRP_CRL_OCSP)) {
memset(&crlOpts, 0, sizeof(crlOpts));
policyPtr->FieldOid = CSSMOID_APPLE_TP_REVOCATION_CRL;
crlOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
crlOpts.CrlFlags = 0;
crlOpts.crlStore = NULL;
policyPtr->FieldValue.Data = (uint8 *)&crlOpts;
policyPtr->FieldValue.Length = sizeof(crlOpts);
if(vfyArgs->requireCrlForAll) {
crlOpts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT;
}
if(vfyArgs->crlNetFetchEnable) {
crlOpts.CrlFlags |= CSSM_TP_ACTION_FETCH_CRL_FROM_NET;
}
if(vfyArgs->requireCrlIfPresent) {
crlOpts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT;
}
crlOpts.crlStore = vfyArgs->crlDlDb;
policyPtr++;
numPolicies++;
}
CSSM_APPLE_TP_OCSP_OPTIONS ocspOpts;
CSSM_DATA respUriData;
CSSM_DATA respCertData = {vfyArgs->responderCertLen,
(uint8 *)vfyArgs->responderCert};
if((vfyArgs->revokePolicy == CRP_OCSP) || (vfyArgs->revokePolicy == CRP_CRL_OCSP)) {
memset(&ocspOpts, 0, sizeof(ocspOpts));
policyPtr->FieldOid = CSSMOID_APPLE_TP_REVOCATION_OCSP;
crlOpts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
policyPtr->FieldValue.Data = (uint8 *)&ocspOpts;
policyPtr->FieldValue.Length = sizeof(ocspOpts);
if(vfyArgs->requireOcspForAll) {
ocspOpts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT;
}
if(vfyArgs->requireOcspIfPresent) {
ocspOpts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT;
}
if(vfyArgs->disableCache) {
ocspOpts.Flags |= (CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE |
CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE);
}
if(vfyArgs->disableOcspNet) {
ocspOpts.Flags |= CSSM_TP_ACTION_OCSP_DISABLE_NET;
}
if(vfyArgs->generateOcspNonce) {
ocspOpts.Flags |= CSSM_TP_OCSP_GEN_NONCE;
}
if(vfyArgs->requireOcspRespNonce) {
ocspOpts.Flags |= CSSM_TP_OCSP_REQUIRE_RESP_NONCE;
}
if(vfyArgs->responderURI != NULL) {
respUriData.Data = (uint8 *)vfyArgs->responderURI;
respUriData.Length = strlen(vfyArgs->responderURI);
ocspOpts.LocalResponder = &respUriData;
}
if(vfyArgs->responderCert != NULL) {
ocspOpts.LocalResponderCert = &respCertData;
}
policyPtr++;
numPolicies++;
}
authCtx.Policy.NumberOfPolicyIds = numPolicies;
authCtx.Policy.PolicyIds = policyIds;
authCtx.Policy.PolicyControl = NULL;
authCtx.VerifyTime = vfyArgs->vfyTime; authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY;
authCtx.CallbackWithVerifiedCert = NULL;
uint32 totalNumDbs = 0;
uint32 numCallerDbs = 0;
CSSM_BOOL weOpenedDbs = CSSM_FALSE;
if(vfyArgs->dlDbList != NULL) {
totalNumDbs = numCallerDbs = vfyArgs->dlDbList->NumHandles;
}
if(vfyArgs->useTrustSettings && vfyArgs->useSystemAnchors) {
totalNumDbs += 2;
weOpenedDbs = CSSM_TRUE;
}
CSSM_DL_DB_HANDLE dlDbHandles[totalNumDbs];
CSSM_DL_DB_LIST dlDbList;
CSSM_DL_HANDLE dlHand = 0;
for(unsigned dex=0; dex<numCallerDbs; dex++) {
dlDbHandles[dex] = vfyArgs->dlDbList->DLDBHandle[dex];
}
if(weOpenedDbs) {
if(numCallerDbs == 0) {
dlHand = cuDlStartup();
dlDbHandles[0].DLHandle = dlHand;
dlDbHandles[1].DLHandle = dlHand;
}
else {
dlDbHandles[numCallerDbs].DLHandle = dlDbHandles[0].DLHandle;
dlDbHandles[numCallerDbs + 1].DLHandle = dlDbHandles[0].DLHandle;
}
dlDbHandles[numCallerDbs].DBHandle =
cuDbStartupByName(dlDbHandles[numCallerDbs].DLHandle,
(char *)ADMIN_CERT_STORE_PATH, CSSM_FALSE, CSSM_TRUE);
dlDbHandles[numCallerDbs + 1].DBHandle =
cuDbStartupByName(dlDbHandles[numCallerDbs].DLHandle,
(char *)SYSTEM_ROOT_STORE_PATH, CSSM_FALSE, CSSM_TRUE);
}
dlDbList.DLDBHandle = dlDbHandles;
dlDbList.NumHandles = totalNumDbs;
authCtx.DBList = &dlDbList;
CFArrayRef cfAnchors = NULL;
CSSM_DATA *cssmAnchors = NULL;
unsigned numAnchors = 0;
if(vfyArgs->useSystemAnchors) {
if(!vfyArgs->useTrustSettings) {
getSystemAnchors(&cfAnchors, &cssmAnchors, &numAnchors);
authCtx.NumberOfAnchorCerts = numAnchors;
authCtx.AnchorCerts = cssmAnchors;
}
}
else {
if(vfyArgs->roots) {
authCtx.NumberOfAnchorCerts = vfyArgs->roots->numBlobs();
authCtx.AnchorCerts = vfyArgs->roots->blobList();
}
}
authCtx.CallerCredentials = NULL;
if(vfyArgs->crls) {
CSSM_CRLGROUP_PTR cssmCrls = &vfyCtx.Crls;
cssmCrls->CrlType = CSSM_CRL_TYPE_X_509v1;
cssmCrls->CrlEncoding = CSSM_CRL_ENCODING_DER;
cssmCrls->NumberOfCrls = vfyArgs->crls->numBlobs();
cssmCrls->GroupCrlList.CrlList = vfyArgs->crls->blobList();
cssmCrls->CrlGroupType = CSSM_CRLGROUP_DATA;
}
CSSM_APPLE_TP_ACTION_DATA tpAction;
tpAction.Version = CSSM_APPLE_TP_ACTION_VERSION;
tpAction.ActionFlags = 0;
if(vfyArgs->leafCertIsCA) {
tpAction.ActionFlags |= CSSM_TP_ACTION_LEAF_IS_CA;
}
if(vfyArgs->certNetFetchEnable) {
tpAction.ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
}
if(vfyArgs->allowExpiredRoot) {
tpAction.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT;
}
if(!vfyArgs->allowUnverified) {
tpAction.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
}
if(vfyArgs->useTrustSettings) {
tpAction.ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS;
}
if(vfyArgs->implicitAnchors) {
tpAction.ActionFlags |= CSSM_TP_ACTION_IMPLICIT_ANCHORS;
}
vfyCtx.ActionData.Data = (uint8 *)&tpAction;
vfyCtx.ActionData.Length = sizeof(tpAction);
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 = vfyArgs->certs->numBlobs();
cssmCerts.GroupList.CertList = vfyArgs->certs->blobList();
cssmCerts.CertGroupType = CSSM_CERTGROUP_DATA;
int ourRtn = 0;
CSSM_RETURN crtn = CSSM_TP_CertGroupVerify(vfyArgs->tpHand,
vfyArgs->clHand,
vfyArgs->cspHand,
&cssmCerts,
&vfyCtx,
&vfyResult);
if(vfyArgs->expectedErrStr != NULL) {
const char *actRtn;
if(crtn == CSSM_OK) {
actRtn = "CSSM_OK";
}
else {
actRtn = cssmErrToStr(crtn);
}
char *found = strstr(actRtn, vfyArgs->expectedErrStr);
if(found) {
if(!vfyArgs->quiet) {
printf("...%s received as expected\n", vfyArgs->expectedErrStr);
}
}
else {
printf("***CSSM_TP_CertGroupVerify error\n");
printf(" expected rtn : %s\n", vfyArgs->expectedErrStr);
printf(" actual rtn : %s\n", actRtn);
ourRtn = 1;
}
}
else {
if(crtn) {
if(!vfyArgs->quiet) {
printError("CSSM_TP_CertGroupVerify", crtn);
}
ourRtn = 1;
}
else if(!vfyArgs->quiet) {
printf("...verify successful\n");
}
}
if(vfyArgs->certErrors) {
if(vfyCertErrors(&vfyResult, vfyArgs->numCertErrors, vfyArgs->certErrors,
vfyArgs->quiet)) {
ourRtn = 1;
}
}
if(vfyArgs->certStatus) {
if(vfyCertStatus(&vfyResult, vfyArgs->numCertStatus, vfyArgs->certStatus,
vfyArgs->quiet)) {
ourRtn = 1;
}
}
if(vfyArgs->verbose) {
dumpVfyResult(&vfyResult);
}
freeVfyResult(&vfyResult);
if(weOpenedDbs) {
CSSM_DL_DbClose(dlDbHandles[numCallerDbs]);
CSSM_DL_DbClose(dlDbHandles[numCallerDbs + 1]);
if(dlHand != 0) {
cuDlDetachUnload(dlHand);
}
}
if(cfAnchors) {
CFRelease(cfAnchors);
}
if(cssmAnchors) {
free(cssmAnchors);
}
return ourRtn;
}
unsigned hexDigit(char digit)
{
if((digit >= '0') && (digit <= '9')) {
return digit - '0';
}
if((digit >= 'a') && (digit <= 'f')) {
return 10 + digit - 'a';
}
if((digit >= 'A') && (digit <= 'F')) {
return 10 + digit - 'A';
}
printf("***BAD HEX DIGIT (%c)\n", digit);
return 0;
}
unsigned hexToBin(const char *hex)
{
unsigned rtn = 0;
const char *cp = hex;
if((cp[0] == '0') && (cp[1] == 'x')) {
cp += 2;
}
if(strlen(cp) > 8) {
printf("***BAD HEX STRING (%s)\n", cp);
return 0;
}
while(*cp) {
rtn <<= 4;
rtn += hexDigit(*cp);
cp++;
}
return rtn;
}
int certVerifySimple(
CSSM_TP_HANDLE tpHand,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
BlobList &certs,
BlobList &roots,
CSSM_BOOL useSystemAnchors,
CSSM_BOOL leafCertIsCA,
CSSM_BOOL allowExpiredRoot,
CertVerifyPolicy vfyPolicy,
const char *sslHost, CSSM_BOOL sslClient, const char *senderEmail, CE_KeyUsage intendedKeyUse, const char *expectedErrStr,
unsigned numCertErrors,
const char **certErrors,
unsigned numCertStatus,
const char **certStatus,
CSSM_BOOL useTrustSettings,
CSSM_BOOL quiet,
CSSM_BOOL verbose)
{
CertVerifyArgs vfyArgs;
memset(&vfyArgs, 0, sizeof(vfyArgs));
vfyArgs.version = CERT_VFY_ARGS_VERS;
vfyArgs.tpHand = tpHand;
vfyArgs.clHand = clHand;
vfyArgs.cspHand = cspHand;
vfyArgs.certs = &certs;
vfyArgs.roots = &roots;
vfyArgs.useSystemAnchors = useSystemAnchors;
vfyArgs.useTrustSettings = useTrustSettings;
vfyArgs.leafCertIsCA = leafCertIsCA;
vfyArgs.allowExpiredRoot = allowExpiredRoot;
vfyArgs.vfyPolicy = vfyPolicy;
vfyArgs.sslHost = sslHost;
vfyArgs.sslClient = sslClient;
vfyArgs.senderEmail = senderEmail;
vfyArgs.intendedKeyUse = intendedKeyUse;
vfyArgs.allowUnverified = CSSM_TRUE;
vfyArgs.expectedErrStr = expectedErrStr;
vfyArgs.numCertErrors = numCertErrors;
vfyArgs.certErrors = certErrors;
vfyArgs.numCertStatus = numCertStatus;
vfyArgs.certStatus = certStatus;
vfyArgs.quiet = quiet;
vfyArgs.verbose = verbose;
return certVerify(&vfyArgs);
}