diff options
Diffstat (limited to 'security/nss/cmd/vfychain')
-rw-r--r-- | security/nss/cmd/vfychain/Makefile | 46 | ||||
-rw-r--r-- | security/nss/cmd/vfychain/manifest.mn | 23 | ||||
-rw-r--r-- | security/nss/cmd/vfychain/vfychain.c | 820 | ||||
-rw-r--r-- | security/nss/cmd/vfychain/vfychain.gyp | 30 |
4 files changed, 919 insertions, 0 deletions
diff --git a/security/nss/cmd/vfychain/Makefile b/security/nss/cmd/vfychain/Makefile new file mode 100644 index 000000000..a27a3ce97 --- /dev/null +++ b/security/nss/cmd/vfychain/Makefile @@ -0,0 +1,46 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + +#include ../platlibs.mk + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +include ../platrules.mk + diff --git a/security/nss/cmd/vfychain/manifest.mn b/security/nss/cmd/vfychain/manifest.mn new file mode 100644 index 000000000..5b2ae25e7 --- /dev/null +++ b/security/nss/cmd/vfychain/manifest.mn @@ -0,0 +1,23 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CORE_DEPTH = ../.. + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +# This next line is used by .mk files +# and gets translated into $LINCS in manifest.mnw +# The MODULE is always implicitly required. +# Listing it here in REQUIRES makes it appear twice in the cc command line. +REQUIRES = seccmd + +# DIRS = + +CSRCS = vfychain.c +DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" + +PROGRAM = vfychain + diff --git a/security/nss/cmd/vfychain/vfychain.c b/security/nss/cmd/vfychain/vfychain.c new file mode 100644 index 000000000..d42274c12 --- /dev/null +++ b/security/nss/cmd/vfychain/vfychain.c @@ -0,0 +1,820 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/**************************************************************************** + * Read in a cert chain from one or more files, and verify the chain for + * some usage. + * * + * This code was modified from other code also kept in the NSS directory. + ****************************************************************************/ + +#include <stdio.h> +#include <string.h> + +#if defined(XP_UNIX) +#include <unistd.h> +#endif + +#include "prerror.h" + +#include "pk11func.h" +#include "seccomon.h" +#include "secutil.h" +#include "secmod.h" +#include "secitem.h" +#include "cert.h" +#include "ocsp.h" + +/* #include <stdlib.h> */ +/* #include <errno.h> */ +/* #include <fcntl.h> */ +/* #include <stdarg.h> */ + +#include "nspr.h" +#include "plgetopt.h" +#include "prio.h" +#include "nss.h" + +/* #include "vfyutil.h" */ + +#define RD_BUF_SIZE (60 * 1024) + +int verbose; + +secuPWData pwdata = { PW_NONE, 0 }; + +static void +Usage(const char *progName) +{ + fprintf(stderr, + "Usage: %s [options] [revocation options] certfile " + "[[options] certfile] ...\n" + "\tWhere options are:\n" + "\t-a\t\t Following certfile is base64 encoded\n" + "\t-b YYMMDDHHMMZ\t Validate date (default: now)\n" + "\t-d directory\t Database directory\n" + "\t-i number of consecutive verifications\n" + "\t-f \t\t Enable cert fetching from AIA URL\n" + "\t-o oid\t\t Set policy OID for cert validation(Format OID.1.2.3)\n" + "\t-p \t\t Use PKIX Library to validate certificate by calling:\n" + "\t\t\t * CERT_VerifyCertificate if specified once,\n" + "\t\t\t * CERT_PKIXVerifyCert if specified twice and more.\n" + "\t-r\t\t Following certfile is raw binary DER (default)\n" + "\t-t\t\t Following cert is explicitly trusted (overrides db trust).\n" + "\t-u usage \t 0=SSL client, 1=SSL server, 2=SSL StepUp, 3=SSL CA,\n" + "\t\t\t 4=Email signer, 5=Email recipient, 6=Object signer,\n" + "\t\t\t 9=ProtectedObjectSigner, 10=OCSP responder, 11=Any CA\n" + "\t-T\t\t Trust both explicit trust anchors (-t) and the database.\n" + "\t\t\t (Default is to only trust certificates marked -t, if there are any,\n" + "\t\t\t or to trust the database if there are certificates marked -t.)\n" + "\t-v\t\t Verbose mode. Prints root cert subject(double the\n" + "\t\t\t argument for whole root cert info)\n" + "\t-w password\t Database password.\n" + "\t-W pwfile\t Password file.\n\n" + "\tRevocation options for PKIX API(invoked with -pp options) is a\n" + "\tcollection of the following flags:\n" + "\t\t[-g type [-h flags] [-m type [-s flags]] ...] ...\n" + "\tWhere:\n" + "\t-g test type\t Sets status checking test type. Possible values\n" + "\t\t\tare \"leaf\" or \"chain\"\n" + "\t-h test flags\t Sets revocation flags for the test type it\n" + "\t\t\tfollows. Possible flags: \"testLocalInfoFirst\" and\n" + "\t\t\t\"requireFreshInfo\".\n" + "\t-m method type\t Sets method type for the test type it follows.\n" + "\t\t\tPossible types are \"crl\" and \"ocsp\".\n" + "\t-s method flags\t Sets revocation flags for the method it follows.\n" + "\t\t\tPossible types are \"doNotUse\", \"forbidFetching\",\n" + "\t\t\t\"ignoreDefaultSrc\", \"requireInfo\" and \"failIfNoInfo\".\n", + progName); + exit(1); +} + +/************************************************************************** +** +** Error and information routines. +** +**************************************************************************/ + +void +errWarn(char *function) +{ + fprintf(stderr, "Error in function %s: %s\n", + function, SECU_Strerror(PR_GetError())); +} + +void +exitErr(char *function) +{ + errWarn(function); + /* Exit gracefully. */ + /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/ + (void)NSS_Shutdown(); + PR_Cleanup(); + exit(1); +} + +typedef struct certMemStr { + struct certMemStr *next; + CERTCertificate *cert; +} certMem; + +certMem *theCerts; +CERTCertList *trustedCertList; + +void +rememberCert(CERTCertificate *cert, PRBool trusted) +{ + if (trusted) { + if (!trustedCertList) { + trustedCertList = CERT_NewCertList(); + } + CERT_AddCertToListTail(trustedCertList, cert); + } else { + certMem *newCertMem = PORT_ZNew(certMem); + if (newCertMem) { + newCertMem->next = theCerts; + newCertMem->cert = cert; + theCerts = newCertMem; + } + } +} + +void +forgetCerts(void) +{ + certMem *oldCertMem; + while (theCerts) { + oldCertMem = theCerts; + theCerts = theCerts->next; + CERT_DestroyCertificate(oldCertMem->cert); + PORT_Free(oldCertMem); + } + if (trustedCertList) { + CERT_DestroyCertList(trustedCertList); + } +} + +CERTCertificate * +getCert(const char *name, PRBool isAscii, const char *progName) +{ + CERTCertificate *cert; + CERTCertDBHandle *defaultDB; + PRFileDesc *fd; + SECStatus rv; + SECItem item = { 0, NULL, 0 }; + + defaultDB = CERT_GetDefaultCertDB(); + + /* First, let's try to find the cert in existing DB. */ + cert = CERT_FindCertByNicknameOrEmailAddr(defaultDB, name); + if (cert) { + return cert; + } + + /* Don't have a cert with name "name" in the DB. Try to + * open a file with such name and get the cert from there.*/ + fd = PR_Open(name, PR_RDONLY, 0777); + if (!fd) { + PRErrorCode err = PR_GetError(); + fprintf(stderr, "open of %s failed, %d = %s\n", + name, err, SECU_Strerror(err)); + return cert; + } + + rv = SECU_ReadDERFromFile(&item, fd, isAscii, PR_FALSE); + PR_Close(fd); + if (rv != SECSuccess) { + fprintf(stderr, "%s: SECU_ReadDERFromFile failed\n", progName); + return cert; + } + + if (!item.len) { /* file was empty */ + fprintf(stderr, "cert file %s was empty.\n", name); + return cert; + } + + cert = CERT_NewTempCertificate(defaultDB, &item, + NULL /* nickname */, + PR_FALSE /* isPerm */, + PR_TRUE /* copyDER */); + if (!cert) { + PRErrorCode err = PR_GetError(); + fprintf(stderr, "couldn't import %s, %d = %s\n", + name, err, SECU_Strerror(err)); + } + PORT_Free(item.data); + return cert; +} + +#define REVCONFIG_TEST_UNDEFINED 0 +#define REVCONFIG_TEST_LEAF 1 +#define REVCONFIG_TEST_CHAIN 2 +#define REVCONFIG_METHOD_CRL 1 +#define REVCONFIG_METHOD_OCSP 2 + +#define REVCONFIG_TEST_LEAF_STR "leaf" +#define REVCONFIG_TEST_CHAIN_STR "chain" +#define REVCONFIG_METHOD_CRL_STR "crl" +#define REVCONFIG_METHOD_OCSP_STR "ocsp" + +#define REVCONFIG_TEST_TESTLOCALINFOFIRST_STR "testLocalInfoFirst" +#define REVCONFIG_TEST_REQUIREFRESHINFO_STR "requireFreshInfo" +#define REVCONFIG_METHOD_DONOTUSEMETHOD_STR "doNotUse" +#define REVCONFIG_METHOD_FORBIDNETWORKFETCHIN_STR "forbidFetching" +#define REVCONFIG_METHOD_IGNOREDEFAULTSRC_STR "ignoreDefaultSrc" +#define REVCONFIG_METHOD_REQUIREINFO_STR "requireInfo" +#define REVCONFIG_METHOD_FAILIFNOINFO_STR "failIfNoInfo" + +#define REV_METHOD_INDEX_MAX 4 + +typedef struct RevMethodsStruct { + unsigned int testType; + char *testTypeStr; + unsigned int testFlags; + char *testFlagsStr; + unsigned int methodType; + char *methodTypeStr; + unsigned int methodFlags; + char *methodFlagsStr; +} RevMethods; + +RevMethods revMethodsData[REV_METHOD_INDEX_MAX]; + +SECStatus +parseRevMethodsAndFlags() +{ + int i; + unsigned int testType = 0; + + for (i = 0; i < REV_METHOD_INDEX_MAX; i++) { + /* testType */ + if (revMethodsData[i].testTypeStr) { + char *typeStr = revMethodsData[i].testTypeStr; + + testType = 0; + if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_LEAF_STR)) { + testType = REVCONFIG_TEST_LEAF; + } else if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_CHAIN_STR)) { + testType = REVCONFIG_TEST_CHAIN; + } + } + if (!testType) { + return SECFailure; + } + revMethodsData[i].testType = testType; + /* testFlags */ + if (revMethodsData[i].testFlagsStr) { + char *flagStr = revMethodsData[i].testFlagsStr; + unsigned int testFlags = 0; + + if (PORT_Strstr(flagStr, REVCONFIG_TEST_TESTLOCALINFOFIRST_STR)) { + testFlags |= CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; + } + if (PORT_Strstr(flagStr, REVCONFIG_TEST_REQUIREFRESHINFO_STR)) { + testFlags |= CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; + } + revMethodsData[i].testFlags = testFlags; + } + /* method type */ + if (revMethodsData[i].methodTypeStr) { + char *methodStr = revMethodsData[i].methodTypeStr; + unsigned int methodType = 0; + + if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_CRL_STR)) { + methodType = REVCONFIG_METHOD_CRL; + } else if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_OCSP_STR)) { + methodType = REVCONFIG_METHOD_OCSP; + } + if (!methodType) { + return SECFailure; + } + revMethodsData[i].methodType = methodType; + } + if (!revMethodsData[i].methodType) { + revMethodsData[i].testType = REVCONFIG_TEST_UNDEFINED; + continue; + } + /* method flags */ + if (revMethodsData[i].methodFlagsStr) { + char *flagStr = revMethodsData[i].methodFlagsStr; + unsigned int methodFlags = 0; + + if (!PORT_Strstr(flagStr, REVCONFIG_METHOD_DONOTUSEMETHOD_STR)) { + methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD; + } + if (PORT_Strstr(flagStr, + REVCONFIG_METHOD_FORBIDNETWORKFETCHIN_STR)) { + methodFlags |= CERT_REV_M_FORBID_NETWORK_FETCHING; + } + if (PORT_Strstr(flagStr, REVCONFIG_METHOD_IGNOREDEFAULTSRC_STR)) { + methodFlags |= CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE; + } + if (PORT_Strstr(flagStr, REVCONFIG_METHOD_REQUIREINFO_STR)) { + methodFlags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE; + } + if (PORT_Strstr(flagStr, REVCONFIG_METHOD_FAILIFNOINFO_STR)) { + methodFlags |= CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO; + } + revMethodsData[i].methodFlags = methodFlags; + } else { + revMethodsData[i].methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD; + } + } + return SECSuccess; +} + +SECStatus +configureRevocationParams(CERTRevocationFlags *flags) +{ + int i; + unsigned int testType = REVCONFIG_TEST_UNDEFINED; + static CERTRevocationTests *revTests = NULL; + PRUint64 *revFlags = NULL; + + for (i = 0; i < REV_METHOD_INDEX_MAX; i++) { + if (revMethodsData[i].testType == REVCONFIG_TEST_UNDEFINED) { + continue; + } + if (revMethodsData[i].testType != testType) { + testType = revMethodsData[i].testType; + if (testType == REVCONFIG_TEST_CHAIN) { + revTests = &flags->chainTests; + } else { + revTests = &flags->leafTests; + } + revTests->number_of_preferred_methods = 0; + revTests->preferred_methods = 0; + revFlags = revTests->cert_rev_flags_per_method; + } + /* Set the number of the methods independently to the max number of + * methods. If method flags are not set it will be ignored due to + * default DO_NOT_USE flag. */ + revTests->number_of_defined_methods = cert_revocation_method_count; + revTests->cert_rev_method_independent_flags |= + revMethodsData[i].testFlags; + if (revMethodsData[i].methodType == REVCONFIG_METHOD_CRL) { + revFlags[cert_revocation_method_crl] = + revMethodsData[i].methodFlags; + } else if (revMethodsData[i].methodType == REVCONFIG_METHOD_OCSP) { + revFlags[cert_revocation_method_ocsp] = + revMethodsData[i].methodFlags; + } + } + return SECSuccess; +} + +void +freeRevocationMethodData() +{ + int i = 0; + for (; i < REV_METHOD_INDEX_MAX; i++) { + if (revMethodsData[i].testTypeStr) { + PORT_Free(revMethodsData[i].testTypeStr); + } + if (revMethodsData[i].testFlagsStr) { + PORT_Free(revMethodsData[i].testFlagsStr); + } + if (revMethodsData[i].methodTypeStr) { + PORT_Free(revMethodsData[i].methodTypeStr); + } + if (revMethodsData[i].methodFlagsStr) { + PORT_Free(revMethodsData[i].methodFlagsStr); + } + } +} + +PRBool +isOCSPEnabled() +{ + int i; + + for (i = 0; i < REV_METHOD_INDEX_MAX; i++) { + if (revMethodsData[i].methodType == REVCONFIG_METHOD_OCSP) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +int +main(int argc, char *argv[], char *envp[]) +{ + char *certDir = NULL; + char *progName = NULL; + char *oidStr = NULL; + CERTCertificate *cert; + CERTCertificate *firstCert = NULL; + CERTCertificate *issuerCert = NULL; + CERTCertDBHandle *defaultDB = NULL; + PRBool isAscii = PR_FALSE; + PRBool trusted = PR_FALSE; + SECStatus secStatus; + SECCertificateUsage certUsage = certificateUsageSSLServer; + PLOptState *optstate; + PRTime time = 0; + PLOptStatus status; + int usePkix = 0; + int rv = 1; + int usage; + CERTVerifyLog log; + CERTCertList *builtChain = NULL; + PRBool certFetching = PR_FALSE; + int revDataIndex = 0; + PRBool ocsp_fetchingFailureIsAFailure = PR_TRUE; + PRBool useDefaultRevFlags = PR_TRUE; + PRBool onlyTrustAnchors = PR_TRUE; + int vfyCounts = 1; + + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + progName = PL_strdup(argv[0]); + + optstate = PL_CreateOptState(argc, argv, "ab:c:d:efg:h:i:m:o:prs:tTu:vw:W:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 0: /* positional parameter */ + goto breakout; + case 'a': + isAscii = PR_TRUE; + break; + case 'b': + secStatus = DER_AsciiToTime(&time, optstate->value); + if (secStatus != SECSuccess) + Usage(progName); + break; + case 'd': + certDir = PL_strdup(optstate->value); + break; + case 'e': + ocsp_fetchingFailureIsAFailure = PR_FALSE; + break; + case 'f': + certFetching = PR_TRUE; + break; + case 'g': + if (revMethodsData[revDataIndex].testTypeStr || + revMethodsData[revDataIndex].methodTypeStr) { + revDataIndex += 1; + if (revDataIndex == REV_METHOD_INDEX_MAX) { + fprintf(stderr, "Invalid revocation configuration" + "specified.\n"); + secStatus = SECFailure; + break; + } + } + useDefaultRevFlags = PR_FALSE; + revMethodsData[revDataIndex].testTypeStr = + PL_strdup(optstate->value); + break; + case 'h': + revMethodsData[revDataIndex].testFlagsStr = + PL_strdup(optstate->value); + break; + case 'i': + vfyCounts = PORT_Atoi(optstate->value); + break; + break; + case 'm': + if (revMethodsData[revDataIndex].methodTypeStr) { + revDataIndex += 1; + if (revDataIndex == REV_METHOD_INDEX_MAX) { + fprintf(stderr, "Invalid revocation configuration" + "specified.\n"); + secStatus = SECFailure; + break; + } + } + useDefaultRevFlags = PR_FALSE; + revMethodsData[revDataIndex].methodTypeStr = + PL_strdup(optstate->value); + break; + case 'o': + oidStr = PL_strdup(optstate->value); + break; + case 'p': + usePkix += 1; + break; + case 'r': + isAscii = PR_FALSE; + break; + case 's': + revMethodsData[revDataIndex].methodFlagsStr = + PL_strdup(optstate->value); + break; + case 't': + trusted = PR_TRUE; + break; + case 'T': + onlyTrustAnchors = PR_FALSE; + break; + case 'u': + usage = PORT_Atoi(optstate->value); + if (usage < 0 || usage > 62) + Usage(progName); + certUsage = ((SECCertificateUsage)1) << usage; + if (certUsage > certificateUsageHighest) + Usage(progName); + break; + case 'w': + pwdata.source = PW_PLAINTEXT; + pwdata.data = PORT_Strdup(optstate->value); + break; + + case 'W': + pwdata.source = PW_FROMFILE; + pwdata.data = PORT_Strdup(optstate->value); + break; + case 'v': + verbose++; + break; + default: + Usage(progName); + break; + } + } +breakout: + if (status != PL_OPT_OK) + Usage(progName); + + if (usePkix < 2) { + if (oidStr) { + fprintf(stderr, "Policy oid(-o) can be used only with" + " CERT_PKIXVerifyCert(-pp) function.\n"); + Usage(progName); + } + if (trusted) { + fprintf(stderr, "Cert trust flag can be used only with" + " CERT_PKIXVerifyCert(-pp) function.\n"); + Usage(progName); + } + if (!onlyTrustAnchors) { + fprintf(stderr, "Cert trust anchor exclusiveness can be" + " used only with CERT_PKIXVerifyCert(-pp)" + " function.\n"); + } + } + + if (!useDefaultRevFlags && parseRevMethodsAndFlags()) { + fprintf(stderr, "Invalid revocation configuration specified.\n"); + goto punt; + } + + /* Set our password function callback. */ + PK11_SetPasswordFunc(SECU_GetModulePassword); + + /* Initialize the NSS libraries. */ + if (certDir) { + secStatus = NSS_Init(certDir); + } else { + secStatus = NSS_NoDB_Init(NULL); + + /* load the builtins */ + SECMOD_AddNewModule("Builtins", DLL_PREFIX "nssckbi." DLL_SUFFIX, 0, 0); + } + if (secStatus != SECSuccess) { + exitErr("NSS_Init"); + } + SECU_RegisterDynamicOids(); + if (isOCSPEnabled()) { + CERT_EnableOCSPChecking(CERT_GetDefaultCertDB()); + CERT_DisableOCSPDefaultResponder(CERT_GetDefaultCertDB()); + if (!ocsp_fetchingFailureIsAFailure) { + CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure); + } + } + + while (status == PL_OPT_OK) { + switch (optstate->option) { + default: + Usage(progName); + break; + case 'a': + isAscii = PR_TRUE; + break; + case 'r': + isAscii = PR_FALSE; + break; + case 't': + trusted = PR_TRUE; + break; + case 0: /* positional parameter */ + if (usePkix < 2 && trusted) { + fprintf(stderr, "Cert trust flag can be used only with" + " CERT_PKIXVerifyCert(-pp) function.\n"); + Usage(progName); + } + cert = getCert(optstate->value, isAscii, progName); + if (!cert) + goto punt; + rememberCert(cert, trusted); + if (!firstCert) + firstCert = cert; + trusted = PR_FALSE; + } + status = PL_GetNextOpt(optstate); + } + PL_DestroyOptState(optstate); + if (status == PL_OPT_BAD || !firstCert) + Usage(progName); + + /* Initialize log structure */ + log.arena = PORT_NewArena(512); + log.head = log.tail = NULL; + log.count = 0; + + do { + if (usePkix < 2) { + /* NOW, verify the cert chain. */ + if (usePkix) { + /* Use old API with libpkix validation lib */ + CERT_SetUsePKIXForValidation(PR_TRUE); + } + if (!time) + time = PR_Now(); + + defaultDB = CERT_GetDefaultCertDB(); + secStatus = CERT_VerifyCertificate(defaultDB, firstCert, + PR_TRUE /* check sig */, + certUsage, + time, + &pwdata, /* wincx */ + &log, /* error log */ + NULL); /* returned usages */ + } else + do { + static CERTValOutParam cvout[4]; + static CERTValInParam cvin[7]; + SECOidTag oidTag; + int inParamIndex = 0; + static PRUint64 revFlagsLeaf[2]; + static PRUint64 revFlagsChain[2]; + static CERTRevocationFlags rev; + + if (oidStr) { + PLArenaPool *arena; + SECOidData od; + memset(&od, 0, sizeof od); + od.offset = SEC_OID_UNKNOWN; + od.desc = "User Defined Policy OID"; + od.mechanism = CKM_INVALID_MECHANISM; + od.supportedExtension = INVALID_CERT_EXTENSION; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + fprintf(stderr, "out of memory"); + goto punt; + } + + secStatus = SEC_StringToOID(arena, &od.oid, oidStr, 0); + if (secStatus != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + fprintf(stderr, "Can not encode oid: %s(%s)\n", oidStr, + SECU_Strerror(PORT_GetError())); + break; + } + + oidTag = SECOID_AddEntry(&od); + PORT_FreeArena(arena, PR_FALSE); + if (oidTag == SEC_OID_UNKNOWN) { + fprintf(stderr, "Can not add new oid to the dynamic " + "table: %s\n", + oidStr); + secStatus = SECFailure; + break; + } + + cvin[inParamIndex].type = cert_pi_policyOID; + cvin[inParamIndex].value.arraySize = 1; + cvin[inParamIndex].value.array.oids = &oidTag; + + inParamIndex++; + } + + if (trustedCertList) { + cvin[inParamIndex].type = cert_pi_trustAnchors; + cvin[inParamIndex].value.pointer.chain = trustedCertList; + + inParamIndex++; + } + + cvin[inParamIndex].type = cert_pi_useAIACertFetch; + cvin[inParamIndex].value.scalar.b = certFetching; + inParamIndex++; + + rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf; + rev.chainTests.cert_rev_flags_per_method = revFlagsChain; + secStatus = configureRevocationParams(&rev); + if (secStatus) { + fprintf(stderr, "Can not config revocation parameters "); + break; + } + + cvin[inParamIndex].type = cert_pi_revocationFlags; + cvin[inParamIndex].value.pointer.revocation = &rev; + inParamIndex++; + + if (time) { + cvin[inParamIndex].type = cert_pi_date; + cvin[inParamIndex].value.scalar.time = time; + inParamIndex++; + } + + if (!onlyTrustAnchors) { + cvin[inParamIndex].type = cert_pi_useOnlyTrustAnchors; + cvin[inParamIndex].value.scalar.b = onlyTrustAnchors; + inParamIndex++; + } + + cvin[inParamIndex].type = cert_pi_end; + + cvout[0].type = cert_po_trustAnchor; + cvout[0].value.pointer.cert = NULL; + cvout[1].type = cert_po_certList; + cvout[1].value.pointer.chain = NULL; + + /* setting pointer to CERTVerifyLog. Initialized structure + * will be used CERT_PKIXVerifyCert */ + cvout[2].type = cert_po_errorLog; + cvout[2].value.pointer.log = &log; + + cvout[3].type = cert_po_end; + + secStatus = CERT_PKIXVerifyCert(firstCert, certUsage, + cvin, cvout, &pwdata); + if (secStatus != SECSuccess) { + break; + } + issuerCert = cvout[0].value.pointer.cert; + builtChain = cvout[1].value.pointer.chain; + } while (0); + + /* Display validation results */ + if (secStatus != SECSuccess || log.count > 0) { + CERTVerifyLogNode *node = NULL; + fprintf(stderr, "Chain is bad!\n"); + + SECU_displayVerifyLog(stderr, &log, verbose); + /* Have cert refs in the log only in case of failure. + * Destroy them. */ + for (node = log.head; node; node = node->next) { + if (node->cert) + CERT_DestroyCertificate(node->cert); + } + log.head = log.tail = NULL; + log.count = 0; + rv = 1; + } else { + fprintf(stderr, "Chain is good!\n"); + if (issuerCert) { + if (verbose > 1) { + rv = SEC_PrintCertificateAndTrust(issuerCert, "Root Certificate", + NULL); + if (rv != SECSuccess) { + SECU_PrintError(progName, "problem printing certificate"); + } + } else if (verbose > 0) { + SECU_PrintName(stdout, &issuerCert->subject, "Root " + "Certificate Subject:", + 0); + } + CERT_DestroyCertificate(issuerCert); + } + if (builtChain) { + CERTCertListNode *node; + int count = 0; + char buff[256]; + + if (verbose) { + for (node = CERT_LIST_HEAD(builtChain); !CERT_LIST_END(node, builtChain); + node = CERT_LIST_NEXT(node), count++) { + sprintf(buff, "Certificate %d Subject", count + 1); + SECU_PrintName(stdout, &node->cert->subject, buff, 0); + } + } + CERT_DestroyCertList(builtChain); + } + rv = 0; + } + } while (--vfyCounts > 0); + + /* Need to destroy CERTVerifyLog arena at the end */ + PORT_FreeArena(log.arena, PR_FALSE); + +punt: + forgetCerts(); + if (NSS_Shutdown() != SECSuccess) { + SECU_PrintError(progName, "NSS_Shutdown"); + rv = 1; + } + PORT_Free(progName); + PORT_Free(certDir); + PORT_Free(oidStr); + freeRevocationMethodData(); + if (pwdata.data) { + PORT_Free(pwdata.data); + } + PL_ArenaFinish(); + PR_Cleanup(); + return rv; +} diff --git a/security/nss/cmd/vfychain/vfychain.gyp b/security/nss/cmd/vfychain/vfychain.gyp new file mode 100644 index 000000000..775ba1f05 --- /dev/null +++ b/security/nss/cmd/vfychain/vfychain.gyp @@ -0,0 +1,30 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi', + '../../cmd/platlibs.gypi' + ], + 'targets': [ + { + 'target_name': 'vfychain', + 'type': 'executable', + 'sources': [ + 'vfychain.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'target_defaults': { + 'defines': [ + 'DLL_PREFIX=\"<(dll_prefix)\"', + 'DLL_SUFFIX=\"<(dll_suffix)\"' + ] + }, + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file |