#include <security_cdsa_utils/cuFileIo.h>
#include <utilLib/common.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <Security/cssm.h>
#include <clAppUtils/BlobList.h>
#include <clAppUtils/certVerify.h>
#include "script.h"
typedef enum {
LT_Empty, LT_TestName,
LT_DirName,
LT_Cert,
LT_Root,
LT_CRL,
LT_CertDb,
LT_CrlDb,
LT_ExpectError, LT_CertError, LT_CertStatus, LT_SslHost,
LT_SslClient,
LT_SenderEmail,
LT_Policy,
LT_KeyUsage,
LT_RevokePolicy,
LT_RespURI,
LT_RespCert,
LT_EndOfSection,
LT_EndOfFile,
LT_BadLine,
LT_Globals,
LT_Echo,
LT_GenerateOcspNonce,
LT_RequireOcspNonce,
LT_AllowExpiredRoot,
LT_VerifyTime,
LT_ImplicitAnchors,
LT_AllowUnverified,
LT_CrlNetFetchEnable,
LT_CertNetFetchEnable,
LT_UseSystemAnchors,
LT_UseTrustSettings,
LT_LeafCertIsCA,
LT_CacheDisable,
LT_OcspNetFetchDisable,
LT_RequireOcspIfPresent,
LT_RequireCrlIfPresent,
LT_RequireCrlForAll,
LT_RequireOcspForAll
} LineType;
typedef struct {
const char *keyName;
LineType lineType;
} KeyLineType;
KeyLineType keyLineTypes[] =
{
{ "test", LT_TestName },
{ "dir", LT_DirName },
{ "cert", LT_Cert },
{ "root", LT_Root },
{ "crl", LT_CRL },
{ "certDb", LT_CertDb },
{ "crlDb", LT_CrlDb }, { "error", LT_ExpectError },
{ "certerror", LT_CertError },
{ "certstatus", LT_CertStatus },
{ "sslHost", LT_SslHost },
{ "sslClient", LT_SslClient },
{ "senderEmail", LT_SenderEmail },
{ "policy", LT_Policy },
{ "keyUsage", LT_KeyUsage },
{ "revokePolicy", LT_RevokePolicy },
{ "responderURI", LT_RespURI },
{ "responderCert", LT_RespCert },
{ "cacheDisable", LT_CacheDisable },
{ "echo", LT_Echo },
{ "globals", LT_Globals },
{ "end", LT_EndOfSection },
{ "allowUnverified", LT_AllowUnverified },
{ "requireCrlIfPresent",LT_RequireCrlIfPresent },
{ "crlNetFetchEnable", LT_CrlNetFetchEnable },
{ "certNetFetchEnable", LT_CertNetFetchEnable },
{ "ocspNetFetchDisable",LT_OcspNetFetchDisable },
{ "requireCrlForAll", LT_RequireCrlForAll },
{ "requireOcspForAll", LT_RequireOcspForAll },
{ "useSystemAnchors", LT_UseSystemAnchors },
{ "useTrustSettings", LT_UseTrustSettings },
{ "leafCertIsCA", LT_LeafCertIsCA },
{ "requireOcspIfPresent",LT_RequireOcspIfPresent },
{ "generateOcspNonce", LT_GenerateOcspNonce },
{ "requireOcspNonce", LT_RequireOcspNonce },
{ "allowExpiredRoot", LT_AllowExpiredRoot },
{ "verifyTime", LT_VerifyTime },
{ "implicitAnchors", LT_ImplicitAnchors },
};
#define NUM_KEYS (sizeof(keyLineTypes) / sizeof(KeyLineType))
typedef struct {
const char *str;
CertVerifyPolicy policy;
} PolicyString;
static const PolicyString policyStrings[] =
{
{ "basic", CVP_Basic },
{ "ssl", CVP_SSL },
{ "smime", CVP_SMIME },
{ "swuSign", CVP_SWUpdateSign },
{ "codeSign", CVP_AppleCodeSigning },
{ "pkgSign", CVP_PackageSigning },
{ "resourceSign", CVP_ResourceSigning },
{ "iChat", CVP_iChat },
{ "pkinitServer", CVP_PKINIT_Server },
{ "pkinitClient", CVP_PKINIT_Client },
{ "IPSec", CVP_IPSec },
{ NULL, (CertVerifyPolicy)0 }
};
static void skipWhite(
const unsigned char *&cp,
unsigned &bytesLeft)
{
while(bytesLeft != 0) {
switch(*cp) {
case ' ':
case '\t':
cp++;
bytesLeft--;
break;
default:
return;
}
}
}
static void skipLine(
const unsigned char *&cp,
unsigned &bytesLeft)
{
bool foundEol = false;
while(bytesLeft != 0) {
switch(*cp) {
case '\n':
case '\r':
foundEol = true;
cp++;
bytesLeft--;
break;
default:
if(foundEol) {
return;
}
cp++;
bytesLeft--;
break;
}
}
}
static void skipToken(
const unsigned char *&cp,
unsigned &bytesLeft,
bool isQuoted)
{
while(bytesLeft != 0) {
char c = *cp;
if(isQuoted) {
if(c == '"') {
return;
}
}
else {
if(isspace(c)) {
return;
}
if(c == '=') {
return;
}
}
cp++;
bytesLeft--;
}
}
#define CHECK_EOF(bytesLeft) \
if(bytesLeft == 0) { \
return LT_BadLine; \
}
#define MAX_KEY_LEN 80
static LineType parseLine(
const unsigned char *&cp, unsigned &bytesLeft, char *&value, CSSM_BOOL verbose)
{
if(bytesLeft == 0) {
if(verbose) {
printf("...EOF reached\n");
}
return LT_EndOfFile;
}
skipWhite(cp, bytesLeft);
if(bytesLeft == 0) {
return LT_Empty;
}
switch(*cp) {
case '#':
case '\n':
case '\r':
skipLine(cp, bytesLeft);
return LT_Empty;
}
const unsigned char *tokenStart = cp;
skipToken(cp, bytesLeft, false);
CHECK_EOF(bytesLeft);
unsigned tokenLen = cp - tokenStart;
char key[MAX_KEY_LEN];
memmove(key, tokenStart, tokenLen);
key[tokenLen] = '\0';
LineType rtnType = LT_BadLine;
for(unsigned i=0; i<NUM_KEYS; i++) {
KeyLineType *klt = &keyLineTypes[i];
if(!strcmp(klt->keyName, key)) {
rtnType = klt->lineType;
break;
}
}
bool noValue = false;
switch(rtnType) {
case LT_EndOfSection:
if(verbose) {
printf("...end of section\n");
}
noValue = true;
break;
case LT_Globals:
noValue = true;
break;
case LT_BadLine:
printf("***unknown key '%s'\n", key);
noValue = true;
break;
default:
break;
}
if(noValue) {
skipLine(cp, bytesLeft);
return rtnType;
}
skipWhite(cp, bytesLeft);
CHECK_EOF(bytesLeft);
if(rtnType == LT_Echo) {
tokenStart = cp;
for( ; bytesLeft != 0; cp++, bytesLeft--) {
if((*cp == '\n') || (*cp == '\r')) {
break;
}
}
if(cp != tokenStart) {
tokenLen = cp - tokenStart;
value = (char *)malloc(tokenLen + 1);
memmove(value, tokenStart, tokenLen);
value[tokenLen] = '\0';
}
else {
value = NULL;
}
skipLine(cp, bytesLeft);
return LT_Echo;
}
if(*cp != '=') {
printf("===missing = after key\n");
return LT_BadLine;
}
cp++;
bytesLeft--;
skipWhite(cp, bytesLeft);
CHECK_EOF(bytesLeft);
bool isQuoted = false;
if(*cp == '"') {
cp++;
bytesLeft--;
CHECK_EOF(bytesLeft)
isQuoted = true;
}
tokenStart = cp;
skipToken(cp, bytesLeft, isQuoted);
tokenLen = cp - tokenStart;
if(tokenLen == 0) {
value = NULL;
}
else {
value = (char *)malloc(tokenLen + 1);
memmove(value, tokenStart, tokenLen);
value[tokenLen] = '\0';
}
skipLine(cp, bytesLeft);
if(verbose) {
printf("'%s' = '%s'\n", key, value);
}
return rtnType;
}
typedef enum {
OTR_Success,
OTR_Fail,
OTR_EndOfScript
} OneTestResult;
OneTestResult parseVar(
LineType lineType,
const char *value,
ScriptVars &scriptVars)
{
CSSM_BOOL cval;
if(!strcmp(value, "true")) {
cval = CSSM_TRUE;
}
else if(!strcmp(value, "false")) {
cval = CSSM_FALSE;
}
else {
printf("***boolean variables must be true or false, not '%s'\n", value);
return OTR_Fail;
}
switch(lineType) {
case LT_AllowUnverified:
scriptVars.allowUnverified = cval;
break;
case LT_CrlNetFetchEnable:
scriptVars.crlNetFetchEnable = cval;
break;
case LT_CertNetFetchEnable:
scriptVars.certNetFetchEnable = cval;
break;
case LT_UseSystemAnchors:
scriptVars.useSystemAnchors = cval;
break;
case LT_UseTrustSettings:
scriptVars.useTrustSettings = cval;
break;
case LT_LeafCertIsCA:
scriptVars.leafCertIsCA = cval;
break;
case LT_CacheDisable:
scriptVars.cacheDisable = cval;
break;
case LT_OcspNetFetchDisable:
scriptVars.ocspNetFetchDisable = cval;
break;
case LT_RequireOcspIfPresent:
scriptVars.requireOcspIfPresent = cval;
break;
case LT_RequireCrlIfPresent:
scriptVars.requireCrlIfPresent = cval;
break;
case LT_RequireCrlForAll:
scriptVars.requireCrlForAll = cval;
break;
case LT_RequireOcspForAll:
scriptVars.requireOcspForAll = cval;
break;
default:
return OTR_Fail;
}
return OTR_Success;
}
#if 0
static char *strnstr(
const char *big,
const char *little,
size_t len)
{
const char *cp;
unsigned littleLen = strlen(little);
const char *end = big + len - littleLen;
char first = little[0];
for(cp=big; cp<end; cp++) {
if(*cp != first) {
continue;
}
if(memcmp(cp, little, littleLen) == 0) {
return (char *)cp;
}
} while(cp < end);
return NULL;
}
#endif
OneTestResult fetchGlobals(
const unsigned char *&scriptData, unsigned &bytesLeft, ScriptVars &scriptVars, CSSM_BOOL verbose)
{
char *value; LineType lineType;
OneTestResult result;
if(verbose) {
printf("...processing global section\n");
}
do {
value = NULL;
lineType = parseLine(scriptData, bytesLeft, value, verbose);
switch(lineType) {
case LT_Empty:
case LT_Globals:
break; case LT_EndOfSection:
return OTR_Success;
case LT_EndOfFile:
printf("***Premature end of file in globals section.\n");
return OTR_EndOfScript;
case LT_BadLine:
return OTR_Fail;
default:
result = parseVar(lineType, value, scriptVars);
if(result != OTR_Success) {
return OTR_Fail;
}
break;
}
if(value != NULL) {
free(value);
}
} while(1);
return OTR_Success;
}
OneTestResult runOneTest(
const unsigned char *&scriptData, unsigned &bytesLeft, CSSM_TP_HANDLE tpHand,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
CSSM_DL_HANDLE dlHand,
ScriptVars &scriptVars,
CSSM_BOOL quiet,
CSSM_BOOL verbose)
{
CertVerifyArgs vfyArgs;
memset(&vfyArgs, 0, sizeof(vfyArgs));
char *testName = NULL;
char *dirName = NULL;
BlobList certs;
BlobList roots;
BlobList crls;
LineType lineType;
char *value; int blobErr;
ScriptVars localVars = scriptVars;
OneTestResult result;
char pathName[300];
CSSM_RETURN crtn;
CSSM_DL_DB_HANDLE_PTR currDlDb = NULL;
CSSM_DL_DB_LIST dlDbList = {0, NULL};
vfyArgs.version = CERT_VFY_ARGS_VERS;
vfyArgs.certs = &certs;
vfyArgs.roots = &roots;
vfyArgs.crls = &crls;
vfyArgs.quiet = quiet;
vfyArgs.tpHand = tpHand;
vfyArgs.clHand = clHand;
vfyArgs.cspHand = cspHand;
vfyArgs.quiet = quiet;
vfyArgs.revokePolicy = CRP_None;
vfyArgs.vfyPolicy = CVP_Basic;
vfyArgs.dlDbList = &dlDbList;
do {
value = NULL;
blobErr = 0;
lineType = parseLine(scriptData, bytesLeft, value, verbose);
switch(lineType) {
case LT_Empty:
break; case LT_TestName:
if(testName != NULL) {
printf("***Duplicate test name ignored\n");
free(value);
}
else {
testName = value; }
value = NULL;
break;
case LT_DirName:
if(dirName != NULL) {
printf("***Duplicate directory name ignored\n");
free(value);
}
else {
dirName = value; }
value = NULL;
break;
case LT_Cert:
blobErr = certs.addFile(value, dirName);
break;
case LT_Root:
blobErr = roots.addFile(value, dirName);
break;
case LT_CRL:
blobErr = crls.addFile(value, dirName);
break;
case LT_CertDb:
case LT_CrlDb:
if(dirName) {
sprintf(pathName, "%s/%s", dirName, value);
}
else {
strcpy(pathName, value);
}
dlDbList.NumHandles++;
dlDbList.DLDBHandle = (CSSM_DL_DB_HANDLE_PTR)realloc(
dlDbList.DLDBHandle,
dlDbList.NumHandles * sizeof(CSSM_DL_DB_HANDLE));
currDlDb = &dlDbList.DLDBHandle[dlDbList.NumHandles-1];
currDlDb->DLHandle = dlHand;
crtn = CSSM_DL_DbOpen(dlHand,
pathName,
NULL, CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE,
NULL, NULL, &currDlDb->DBHandle);
if(crtn) {
printError("CSSM_DL_DbOpen", crtn);
printf("***Error opening DB %s. Aborting.\n", value);
return OTR_Fail;
}
break;
case LT_ExpectError:
if(vfyArgs.expectedErrStr != NULL) {
printf("***Duplicate expected error ignored\n");
free(value);
}
else {
vfyArgs.expectedErrStr = value; }
value = NULL;
break;
case LT_CertError:
vfyArgs.numCertErrors++;
vfyArgs.certErrors = (const char **)realloc(vfyArgs.certErrors,
vfyArgs.numCertErrors * sizeof(char *));
vfyArgs.certErrors[vfyArgs.numCertErrors - 1] = value;
value = NULL; break;
case LT_CertStatus:
vfyArgs.numCertStatus++;
vfyArgs.certStatus = (const char **)realloc(vfyArgs.certStatus,
vfyArgs.numCertStatus * sizeof(char *));
vfyArgs.certStatus[vfyArgs.numCertStatus - 1] = value;
value = NULL; break;
case LT_SslHost:
vfyArgs.sslHost = value;
value = NULL; vfyArgs.vfyPolicy = CVP_SSL;
break;
case LT_SenderEmail:
vfyArgs.senderEmail = value;
value = NULL; if(vfyArgs.vfyPolicy == CVP_Basic) {
vfyArgs.vfyPolicy = CVP_SMIME;
}
break;
case LT_Policy:
if(parsePolicyString(value, &vfyArgs.vfyPolicy)) {
printf("Bogus policyValue (%s)\n", value);
printPolicyStrings();
return OTR_Fail;
}
break;
case LT_KeyUsage:
vfyArgs.intendedKeyUse = hexToBin(value);
break;
case LT_RevokePolicy:
if(!strcmp(value, "none")) {
vfyArgs.revokePolicy = CRP_None;
}
else if(!strcmp(value, "crl")) {
vfyArgs.revokePolicy = CRP_CRL;
}
else if(!strcmp(value, "ocsp")) {
vfyArgs.revokePolicy = CRP_OCSP;
}
else if(!strcmp(value, "both")) {
vfyArgs.revokePolicy = CRP_CRL_OCSP;
}
else {
printf("***Illegal revokePolicy (%s)\n.", value);
return OTR_Fail;
}
break;
case LT_RespURI:
vfyArgs.responderURI = value;
value = NULL; break;
case LT_VerifyTime:
vfyArgs.vfyTime = value;
value = NULL; break;
case LT_RespCert:
if(readFile(value, (unsigned char **)&vfyArgs.responderCert,
&vfyArgs.responderCertLen)) {
printf("***Error reading responderCert from %s\n", value);
return OTR_Fail;
}
break;
case LT_EndOfSection:
break;
case LT_EndOfFile:
if(testName == NULL) {
return OTR_EndOfScript;
}
printf("***Premature end of file.\n");
return OTR_Fail;
case LT_BadLine:
return OTR_Fail;
case LT_Globals:
result = fetchGlobals(scriptData, bytesLeft, scriptVars, verbose);
if(result != OTR_Success) {
printf("***Bad globals section\n");
return OTR_Fail;
}
localVars = scriptVars;
break;
case LT_SslClient:
if(!strcmp(value, "true")) {
vfyArgs.sslClient = CSSM_TRUE;
}
else {
vfyArgs.sslClient = CSSM_FALSE;
}
vfyArgs.vfyPolicy = CVP_SSL;
break;
case LT_Echo:
if(!quiet) {
printf("%s\n", value);
}
break;
case LT_GenerateOcspNonce:
vfyArgs.generateOcspNonce = CSSM_TRUE;
break;
case LT_RequireOcspNonce:
vfyArgs.requireOcspRespNonce = CSSM_TRUE;
break;
case LT_AllowExpiredRoot:
if(!strcmp(value, "true")) {
vfyArgs.allowExpiredRoot = CSSM_TRUE;
}
else {
vfyArgs.allowExpiredRoot = CSSM_FALSE;
}
break;
case LT_ImplicitAnchors:
if(!strcmp(value, "true")) {
vfyArgs.implicitAnchors = CSSM_TRUE;
}
else {
vfyArgs.implicitAnchors = CSSM_FALSE;
}
break;
default:
result = parseVar(lineType, value, localVars);
if(result != OTR_Success) {
printf("**Bogus line in script %u bytes from EOF\n",
bytesLeft);
return OTR_Fail;
}
break;
}
if(blobErr) {
return OTR_Fail;
}
if(value != NULL) {
free(value);
}
} while(lineType != LT_EndOfSection);
vfyArgs.allowUnverified = localVars.allowUnverified;
vfyArgs.requireOcspIfPresent = localVars.requireOcspIfPresent;
vfyArgs.requireCrlIfPresent = localVars.requireCrlIfPresent;
vfyArgs.crlNetFetchEnable = localVars.crlNetFetchEnable;
vfyArgs.certNetFetchEnable = localVars.certNetFetchEnable;
vfyArgs.useSystemAnchors = localVars.useSystemAnchors;
vfyArgs.useTrustSettings = localVars.useTrustSettings;
vfyArgs.leafCertIsCA = localVars.leafCertIsCA;
vfyArgs.disableCache = localVars.cacheDisable;
vfyArgs.disableOcspNet = localVars.ocspNetFetchDisable;
vfyArgs.requireCrlForAll = localVars.requireCrlForAll;
vfyArgs.requireOcspForAll = localVars.requireOcspForAll;
vfyArgs.verbose = verbose;
if(!quiet && (testName != NULL)) {
printf("%s\n", testName);
}
int rtn = certVerify(&vfyArgs);
OneTestResult ourRtn = OTR_Success;
if(rtn) {
printf("***Failure on %s\n", testName);
if(testError(quiet)) {
ourRtn = OTR_Fail;
}
}
if(dirName != NULL) {
free(dirName);
}
if(vfyArgs.expectedErrStr != NULL) {
free((void *)vfyArgs.expectedErrStr);
}
if(vfyArgs.certErrors != NULL) {
for(unsigned i=0; i<vfyArgs.numCertErrors; i++) {
free((void *)vfyArgs.certErrors[i]); }
free((void *)vfyArgs.certErrors); }
if(vfyArgs.certStatus != NULL) {
for(unsigned i=0; i<vfyArgs.numCertStatus; i++) {
free((void *)vfyArgs.certStatus[i]); }
free((void *)vfyArgs.certStatus); }
if(testName != NULL) {
free(testName);
}
if(vfyArgs.sslHost) {
free((void *)vfyArgs.sslHost);
}
if(vfyArgs.senderEmail) {
free((void *)vfyArgs.senderEmail);
}
if(vfyArgs.responderURI) {
free((void *)vfyArgs.responderURI);
}
if(vfyArgs.responderCert) {
free((void *)vfyArgs.responderCert);
}
if(vfyArgs.vfyTime) {
free((void *)vfyArgs.vfyTime);
}
if(dlDbList.DLDBHandle) {
for(unsigned dex=0; dex<dlDbList.NumHandles; dex++) {
CSSM_DL_DbClose(dlDbList.DLDBHandle[dex]);
}
free(dlDbList.DLDBHandle);
}
return ourRtn;
}
int runScript(
const char *fileName,
CSSM_TP_HANDLE tpHand,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
CSSM_DL_HANDLE dlHand,
ScriptVars *scriptVars,
CSSM_BOOL quiet,
CSSM_BOOL verbose,
CSSM_BOOL doPause)
{
const unsigned char *scriptData;
unsigned char *cp;
unsigned scriptDataLen;
int rtn;
ScriptVars localVars = *scriptVars;
rtn = readFile(fileName, &cp, &scriptDataLen);
if(rtn) {
printf("***Error reading script file; aborting.\n");
printf("***Are you sure you're running this from the proper directory?\n");
return rtn;
}
scriptData = (const unsigned char *)cp;
OneTestResult result;
do {
result = runOneTest(scriptData, scriptDataLen,
tpHand, clHand, cspHand, dlHand,
localVars, quiet, verbose);
if(result == OTR_Fail) {
rtn = 1;
break;
}
if(doPause) {
fpurge(stdin);
printf("CR to continue: ");
getchar();
}
} while(result == OTR_Success);
free(cp);
return rtn;
}
int parsePolicyString(
const char *str,
CertVerifyPolicy *policy)
{
const PolicyString *ps;
for(ps=policyStrings; ps->str; ps++) {
if(!strcmp(ps->str, str)) {
*policy = ps->policy;
return 0;
}
}
return 1;
}
void printPolicyStrings()
{
printf("Valid policy strings are:\n ");
const PolicyString *ps;
unsigned i=0;
for(ps=policyStrings; ps->str; ps++, i++) {
printf("%s", ps->str);
if(ps[1].str == NULL) {
break;
}
if((i % 6) == 5) {
printf(",\n ");
}
else {
printf(", ");
}
}
printf("\n");
}
void printScriptVars()
{
printf("The list of script variables is as follows:\n");
for(unsigned dex=0; dex<NUM_KEYS; dex++) {
printf(" %s\n", keyLineTypes[dex].keyName);
}
printPolicyStrings();
printf("Valid revokePolicy strings are:\n none, crl, ocsp, both\n");
}