X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/certcrl/script.cpp diff --git a/SecurityTests/clxutils/certcrl/script.cpp b/SecurityTests/clxutils/certcrl/script.cpp new file mode 100644 index 00000000..0f49fafb --- /dev/null +++ b/SecurityTests/clxutils/certcrl/script.cpp @@ -0,0 +1,923 @@ +/* + * script.cpp - run certcrl from script file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; ikeyName, 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; cpDLHandle = 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; istr; 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