mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 11:40:13 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
778 lines
27 KiB
C
778 lines
27 KiB
C
/* 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;
|
|
}
|