]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/certcrl/script.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / certcrl / script.cpp
diff --git a/SecurityTests/clxutils/certcrl/script.cpp b/SecurityTests/clxutils/certcrl/script.cpp
new file mode 100644 (file)
index 0000000..0f49faf
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * script.cpp - run certcrl from script file
+ */
+#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"
+
+/* Line type returned from parseLine */
+typedef enum {
+       LT_Empty,                       // comments, whitespace
+       LT_TestName,
+       LT_DirName,     
+       LT_Cert,
+       LT_Root,
+       LT_CRL,
+       LT_CertDb,
+       LT_CrlDb,
+       LT_ExpectError,         // expected function return
+       LT_CertError,           // per-cert error string
+       LT_CertStatus,          // per-cert StatusBits
+       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,
+       
+       /* variables which can be in globals or per-test */
+       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;
+
+/* table to map key names to 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                                },      // no longer used
+       { "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))
+
+/* map policy string to CertVerifyPolicy */
+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             }
+};
+
+/* skip whitespace (but not line terminators) */
+static void skipWhite(
+       const unsigned char *&cp,
+       unsigned &bytesLeft)
+{
+       while(bytesLeft != 0) {
+               switch(*cp) {
+                       case ' ':
+                       case '\t':
+                               cp++;
+                               bytesLeft--;
+                               break;
+                       default:
+                               return;
+               }
+       }
+}
+
+/* skip to next char after EOL */
+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;
+               }
+       }
+}
+
+/* skip to end of current token (i.e., find next whitespace or '=') */
+static void skipToken(
+       const unsigned char *&cp,
+       unsigned &bytesLeft,
+       bool isQuoted)
+{
+       while(bytesLeft != 0) {
+               char c = *cp;
+               if(isQuoted) {
+                       if(c == '"') {
+                               /* end of quoted string, return still pointing to it */
+                               return;
+                       }
+               }
+               else {
+                       if(isspace(c)) {
+                               return;
+                       }
+                       if(c == '=') {
+                               /* hopefully, end of key */
+                               return;
+                       }
+               }
+               cp++;
+               bytesLeft--;
+       }
+}
+
+/*
+ * Parse one line, return value (following "=" and whitespace) as
+ * mallocd C string. On return, scriptData points to next char after line
+ * terminator(s).
+ *
+ * The basic form of a line is
+ * [whitespace] key [whitespace] = [whitespace] value [whitespace] \n|\r...
+ *
+ * ...except for comments and blank lines. Comments contain '#' as the 
+ * first non-whitespace char.
+ */
+#define CHECK_EOF(bytesLeft)   \
+       if(bytesLeft == 0) {            \
+               return LT_BadLine;      \
+       }
+
+#define MAX_KEY_LEN            80
+
+static LineType parseLine(
+       const unsigned char     *&cp,                   // IN/OUT
+       unsigned                        &bytesLeft,             // IN/OUT bytes left in script
+       char                            *&value,                // mallocd and RETURNED
+       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;
+       }
+       
+       /* 
+        * cp points to start of key
+        * get key value as NULL terminated C string
+        */
+       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';
+       
+       /* parse key */
+       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;
+               }       
+       }
+
+       /* these keys have no value */
+       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) {
+               /* done with line */
+               skipLine(cp, bytesLeft);
+               return rtnType;
+       }
+       
+       /* get to start of value */
+       skipWhite(cp, bytesLeft);
+       CHECK_EOF(bytesLeft);
+       if(rtnType == LT_Echo) {
+               /* echo: value is everything from this char to end of line */
+               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;
+       }
+       
+       /* all other line types: value is first token after '=' */
+       if(*cp != '=') {
+               printf("===missing = after key\n");
+               return LT_BadLine;
+       }
+       cp++;
+       bytesLeft--;
+       skipWhite(cp, bytesLeft);
+       CHECK_EOF(bytesLeft);
+
+       /* cp points to start of value */
+       bool isQuoted = false;
+       if(*cp == '"') {
+               cp++;
+               bytesLeft--;
+               CHECK_EOF(bytesLeft)
+               isQuoted = true;
+       }
+       tokenStart = cp;
+       skipToken(cp, bytesLeft, isQuoted);
+       /* cp points to next char after end of value */
+       /* get value as mallocd C string */
+       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;
+}
+
+/* describe fate of one run of runOneTest() */
+typedef enum {
+       OTR_Success,
+       OTR_Fail,
+       OTR_EndOfScript
+} OneTestResult;
+
+/* parse boolean variable, in globals or per-test */
+OneTestResult parseVar(
+       LineType lineType, 
+       const char *value, 
+       ScriptVars &scriptVars)
+{
+       /* parse value  */
+       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
+/* sure wish X had strnstr */
+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++) {
+               /* find first char of little in what's left of big */
+               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,       // IN/OUT
+       unsigned                        &bytesLeft,             // IN/OUT
+       ScriptVars                      &scriptVars,    // may be modified
+       CSSM_BOOL                       verbose)
+{
+       char *value;            // mallocd by parseLine
+       LineType lineType;
+       OneTestResult result;
+       
+       if(verbose) {
+               printf("...processing global section\n");
+       }
+       /* parse globals section until end encountered */
+       do {
+               value = NULL;
+               lineType = parseLine(scriptData, bytesLeft, value, verbose);
+               switch(lineType) {
+                       case LT_Empty:
+                       case LT_Globals:
+                               break;                                  // nop
+                       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:
+                               /* hopefully a variable */
+                               result = parseVar(lineType, value, scriptVars);
+                               if(result != OTR_Success) {
+                                       return OTR_Fail;
+                               }
+                               break;
+               }
+               if(value != NULL) {
+                       free(value);
+               }
+       } while(1);
+       /* NOT REACHED */
+       return OTR_Success;
+}
+
+/* parse script fragment for one test, run it */
+OneTestResult runOneTest(
+       const unsigned char *&scriptData,                       // IN/OUT
+       unsigned                        &bytesLeft,                             // IN/OUT bytes left in script
+       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));
+
+       /* to be gathered from script */
+       char                    *testName = NULL;
+       char                    *dirName = NULL;
+       BlobList                certs;
+       BlobList                roots;
+       BlobList                crls;
+       
+       LineType                lineType;
+       char                    *value;                 // mallocd by parseLine 
+       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;
+       
+       /* parse script up to end of test */
+       do {
+               value = NULL;
+               blobErr = 0;
+               lineType = parseLine(scriptData, bytesLeft, value, verbose);
+               switch(lineType) {
+                       case LT_Empty:
+                               break;                                  // nop
+                       case LT_TestName:
+                               if(testName != NULL) {
+                                       printf("***Duplicate test name ignored\n");
+                                       free(value);
+                               }
+                               else {
+                                       testName = value;       // free after test
+                               }
+                               value = NULL;
+                               break;
+                       case LT_DirName:
+                               if(dirName != NULL) {
+                                       printf("***Duplicate directory name ignored\n");
+                                       free(value);
+                               }
+                               else {
+                                       dirName = value;        // free after test
+                               }
+                               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:
+                               /* these can be called multiple times */
+                               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,                   // DbLocation
+                                       CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE,
+                                       NULL,                   // CSSM_ACCESS_CREDENTIALS *AccessCred
+                                       NULL,                   // void *OpenParameters
+                                       &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; // free after test
+                               }
+                               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;                                           // free after test 
+                               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;                                           // // free after test 
+                               break;
+                       case LT_SslHost:
+                               vfyArgs.sslHost = value;
+                               value = NULL;                   // free after test
+                               vfyArgs.vfyPolicy = CVP_SSL;
+                               break;
+                       case LT_SenderEmail:
+                               vfyArgs.senderEmail = value;
+                               value = NULL;                   // free after test
+                               if(vfyArgs.vfyPolicy == CVP_Basic) {
+                                       /* don't overwrite if it's already been set to e.g. iChat */
+                                       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;                   // free after test
+                               break;
+                       case LT_VerifyTime:
+                               vfyArgs.vfyTime = value;
+                               value = NULL;                   // free after test
+                               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:
+                               /* only legal if we haven't gotten a test name */
+                               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;
+                               }
+                               /* and start over with these variables */
+                               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:
+                               /* hopefully a variable */
+                               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);
+       
+       /* some args: copy from ScriptVars -> CertVerifyArgs */
+       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;
+       
+       /* here we go */
+       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;
+               }
+       }
+       /* free the stuff that didn't get freed and the end of the
+        * main per-line loop */
+       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]);            // mallocd by parseLine
+               }
+               free((void *)vfyArgs.certErrors);                               // reallocd by us
+       }
+       if(vfyArgs.certStatus != NULL) {
+               for(unsigned i=0; i<vfyArgs.numCertStatus; i++) {
+                       free((void *)vfyArgs.certStatus[i]);            // mallocd by parseLine
+               }
+               free((void *)vfyArgs.certStatus);                               // reallocd by us
+       }
+       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;
+}
+
+/* parse policy string; returns nonzero if not found */
+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");
+}
+