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