2014-01-20 22:10:33 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-10-27 00:11:35 -07:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
#include "CertVerifier.h"
|
2014-01-22 17:13:19 -08:00
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
2014-03-20 14:29:21 -07:00
|
|
|
#include "pkix/pkix.h"
|
2014-01-26 19:36:28 -08:00
|
|
|
#include "ExtendedValidation.h"
|
2013-07-08 16:30:59 -07:00
|
|
|
#include "NSSCertDBTrustDomain.h"
|
2014-02-05 14:49:10 -08:00
|
|
|
#include "PublicKeyPinningService.h"
|
2012-10-27 00:11:35 -07:00
|
|
|
#include "cert.h"
|
2013-09-27 19:53:36 -07:00
|
|
|
#include "ocsp.h"
|
2012-10-27 00:11:35 -07:00
|
|
|
#include "secerr.h"
|
2014-02-05 14:49:10 -08:00
|
|
|
#include "pk11pub.h"
|
2014-01-19 14:05:40 -08:00
|
|
|
#include "prerror.h"
|
2013-07-08 16:30:59 -07:00
|
|
|
#include "sslerr.h"
|
2012-10-27 00:11:35 -07:00
|
|
|
|
2014-03-20 14:29:21 -07:00
|
|
|
// ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
|
2014-01-22 17:13:19 -08:00
|
|
|
// mozilla::ScopedXXX.
|
2014-03-20 14:29:21 -07:00
|
|
|
using namespace mozilla::pkix;
|
2014-01-22 17:13:19 -08:00
|
|
|
using namespace mozilla::psm;
|
|
|
|
|
2014-02-05 22:11:26 -08:00
|
|
|
#ifdef PR_LOGGING
|
2014-02-16 17:35:40 -08:00
|
|
|
PRLogModuleInfo* gCertVerifierLog = nullptr;
|
2012-10-27 00:11:35 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace mozilla { namespace psm {
|
|
|
|
|
|
|
|
const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
|
2014-01-24 13:57:35 -08:00
|
|
|
const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
|
2012-10-27 00:11:35 -07:00
|
|
|
|
2014-01-19 14:05:40 -08:00
|
|
|
CertVerifier::CertVerifier(implementation_config ic,
|
2014-02-10 11:41:12 -08:00
|
|
|
#ifndef NSS_NO_LIBPKIX
|
2014-01-19 14:05:40 -08:00
|
|
|
missing_cert_download_config mcdc,
|
2012-10-27 00:11:35 -07:00
|
|
|
crl_download_config cdc,
|
2014-02-10 11:41:12 -08:00
|
|
|
#endif
|
2012-10-27 00:11:35 -07:00
|
|
|
ocsp_download_config odc,
|
|
|
|
ocsp_strict_config osc,
|
2014-02-05 14:49:10 -08:00
|
|
|
ocsp_get_config ogc,
|
|
|
|
pinning_enforcement_config pel)
|
2014-01-19 14:05:40 -08:00
|
|
|
: mImplementation(ic)
|
2014-02-10 11:41:12 -08:00
|
|
|
#ifndef NSS_NO_LIBPKIX
|
2014-01-19 14:05:40 -08:00
|
|
|
, mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
|
2012-10-27 00:11:35 -07:00
|
|
|
, mCRLDownloadEnabled(cdc == crl_download_allowed)
|
2014-02-10 11:41:12 -08:00
|
|
|
#endif
|
2012-10-27 00:11:35 -07:00
|
|
|
, mOCSPDownloadEnabled(odc == ocsp_on)
|
|
|
|
, mOCSPStrict(osc == ocsp_strict)
|
2013-10-24 14:32:09 -07:00
|
|
|
, mOCSPGETEnabled(ogc == ocsp_get_enabled)
|
2014-02-05 14:49:10 -08:00
|
|
|
, mPinningEnforcementLevel(pel)
|
2012-10-27 00:11:35 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CertVerifier::~CertVerifier()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-07-10 23:47:09 -07:00
|
|
|
void
|
|
|
|
InitCertVerifierLog()
|
|
|
|
{
|
2014-02-05 22:11:26 -08:00
|
|
|
#ifdef PR_LOGGING
|
2013-07-10 23:47:09 -07:00
|
|
|
if (!gCertVerifierLog) {
|
|
|
|
gCertVerifierLog = PR_NewLogModule("certverifier");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:29:21 -07:00
|
|
|
// Once we migrate to mozilla::pkix or change the overridable error
|
2014-01-24 14:13:25 -08:00
|
|
|
// logic this will become unnecesary.
|
|
|
|
static SECStatus
|
|
|
|
insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
|
|
|
|
CERTVerifyLog* verifyLog){
|
|
|
|
CERTVerifyLogNode* node;
|
|
|
|
node = (CERTVerifyLogNode *)PORT_ArenaAlloc(verifyLog->arena,
|
|
|
|
sizeof(CERTVerifyLogNode));
|
|
|
|
if (!node) {
|
|
|
|
PR_SetError(PR_UNKNOWN_ERROR, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
node->cert = CERT_DupCertificate(cert);
|
|
|
|
node->error = err;
|
|
|
|
node->depth = 0;
|
|
|
|
node->arg = nullptr;
|
|
|
|
//and at to head!
|
|
|
|
node->prev = nullptr;
|
|
|
|
node->next = verifyLog->head;
|
|
|
|
if (verifyLog->head) {
|
|
|
|
verifyLog->head->prev = node;
|
|
|
|
}
|
|
|
|
verifyLog->head = node;
|
|
|
|
if (!verifyLog->tail) {
|
|
|
|
verifyLog->tail = node;
|
|
|
|
}
|
|
|
|
verifyLog->count++;
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
2014-02-05 14:49:10 -08:00
|
|
|
|
|
|
|
SECStatus
|
|
|
|
IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
|
|
|
|
result = false;
|
|
|
|
ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
|
|
|
|
slots = PK11_GetAllSlotsForCert(cert, nullptr);
|
|
|
|
if (!slots) {
|
|
|
|
if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
|
|
|
|
// no list
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
for (PK11SlotListElement* le = slots->head; le; le = le->next) {
|
|
|
|
char* token = PK11_GetTokenName(le->slot);
|
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
|
|
|
|
if (strcmp("Builtin Object Token", token) == 0) {
|
|
|
|
result = true;
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ChainValidationCallbackState
|
|
|
|
{
|
|
|
|
const char* hostname;
|
|
|
|
const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
|
|
|
|
const SECCertificateUsage usage;
|
|
|
|
const PRTime time;
|
|
|
|
};
|
2014-01-24 14:13:25 -08:00
|
|
|
|
2014-02-05 14:49:14 -08:00
|
|
|
SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
|
|
|
|
PRBool* chainOK)
|
|
|
|
{
|
2014-02-05 14:49:10 -08:00
|
|
|
ChainValidationCallbackState* callbackState =
|
|
|
|
reinterpret_cast<ChainValidationCallbackState*>(state);
|
|
|
|
|
2014-02-05 14:49:14 -08:00
|
|
|
*chainOK = PR_FALSE;
|
|
|
|
|
2014-02-05 14:49:10 -08:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("verifycert: Inside the Callback \n"));
|
2014-02-05 14:49:14 -08:00
|
|
|
|
|
|
|
// On sanity failure we fail closed.
|
|
|
|
if (!certList) {
|
2014-02-05 14:49:10 -08:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("verifycert: Short circuit, callback, sanity check failed \n"));
|
|
|
|
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (!callbackState) {
|
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("verifycert: Short circuit, callback, no state! \n"));
|
2014-02-05 14:49:14 -08:00
|
|
|
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2014-02-05 14:49:10 -08:00
|
|
|
|
|
|
|
if (callbackState->usage != certificateUsageSSLServer ||
|
|
|
|
callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
|
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("verifycert: Callback shortcut pel=%d \n",
|
|
|
|
callbackState->pinningEnforcementLevel));
|
|
|
|
*chainOK = PR_TRUE;
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
|
|
|
|
!CERT_LIST_END(node, certList);
|
|
|
|
node = CERT_LIST_NEXT(node)) {
|
|
|
|
CERTCertificate* currentCert = node->cert;
|
|
|
|
if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
|
|
|
|
bool isBuiltInRoot = false;
|
|
|
|
SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
|
|
|
|
if (srv != SECSuccess) {
|
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
|
|
|
|
return srv;
|
|
|
|
}
|
|
|
|
// If desired, the user can enable "allow user CA MITM mode", in which
|
|
|
|
// case key pinning is not enforced for certificates that chain to trust
|
|
|
|
// anchors that are not in Mozilla's root program
|
|
|
|
if (!isBuiltInRoot &&
|
|
|
|
(callbackState->pinningEnforcementLevel ==
|
|
|
|
CertVerifier::pinningAllowUserCAMITM)) {
|
|
|
|
*chainOK = PR_TRUE;
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*chainOK = PublicKeyPinningService::
|
|
|
|
ChainHasValidPins(certList, callbackState->hostname,
|
|
|
|
callbackState->time);
|
|
|
|
|
2014-02-05 14:49:14 -08:00
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
static SECStatus
|
2014-01-21 17:30:44 -08:00
|
|
|
ClassicVerifyCert(CERTCertificate* cert,
|
2012-10-27 00:11:35 -07:00
|
|
|
const SECCertificateUsage usage,
|
|
|
|
const PRTime time,
|
2014-01-19 14:05:40 -08:00
|
|
|
void* pinArg,
|
2014-02-05 14:49:10 -08:00
|
|
|
ChainValidationCallbackState* callbackState,
|
2014-01-22 17:13:19 -08:00
|
|
|
/*optional out*/ ScopedCERTCertList* validationChain,
|
2014-01-21 17:30:44 -08:00
|
|
|
/*optional out*/ CERTVerifyLog* verifyLog)
|
2012-10-27 00:11:35 -07:00
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
SECCertUsage enumUsage;
|
2014-02-05 14:49:10 -08:00
|
|
|
switch (usage) {
|
|
|
|
case certificateUsageSSLClient:
|
|
|
|
enumUsage = certUsageSSLClient;
|
|
|
|
break;
|
|
|
|
case certificateUsageSSLServer:
|
|
|
|
enumUsage = certUsageSSLServer;
|
|
|
|
break;
|
|
|
|
case certificateUsageSSLCA:
|
|
|
|
enumUsage = certUsageSSLCA;
|
|
|
|
break;
|
|
|
|
case certificateUsageEmailSigner:
|
|
|
|
enumUsage = certUsageEmailSigner;
|
|
|
|
break;
|
|
|
|
case certificateUsageEmailRecipient:
|
|
|
|
enumUsage = certUsageEmailRecipient;
|
|
|
|
break;
|
|
|
|
case certificateUsageObjectSigner:
|
|
|
|
enumUsage = certUsageObjectSigner;
|
|
|
|
break;
|
|
|
|
case certificateUsageVerifyCA:
|
|
|
|
enumUsage = certUsageVerifyCA;
|
|
|
|
break;
|
|
|
|
case certificateUsageStatusResponder:
|
|
|
|
enumUsage = certUsageStatusResponder;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PR_NOT_REACHED("unexpected usage");
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
if (usage == certificateUsageSSLServer) {
|
2014-01-21 17:30:44 -08:00
|
|
|
// SSL server cert verification has always used CERT_VerifyCert, so we
|
|
|
|
// continue to use it for SSL cert verification to minimize the risk of
|
|
|
|
// there being any differnce in results between CERT_VerifyCert and
|
|
|
|
// CERT_VerifyCertificate.
|
2012-10-27 00:11:35 -07:00
|
|
|
rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), cert, true,
|
|
|
|
certUsageSSLServer, time, pinArg, verifyLog);
|
|
|
|
} else {
|
|
|
|
rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
|
2014-01-21 17:30:44 -08:00
|
|
|
usage, time, pinArg, verifyLog, nullptr);
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
2014-02-05 14:49:10 -08:00
|
|
|
|
|
|
|
if (rv == SECSuccess &&
|
|
|
|
(validationChain || usage == certificateUsageSSLServer)) {
|
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
|
|
|
("VerifyCert: getting chain in 'classic' \n"));
|
|
|
|
ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
|
|
|
|
enumUsage));
|
|
|
|
if (!certChain) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (usage == certificateUsageSSLServer) {
|
|
|
|
PRBool chainOK = PR_FALSE;
|
|
|
|
SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
|
|
|
|
&chainOK);
|
|
|
|
if (srv != SECSuccess) {
|
|
|
|
return srv;
|
|
|
|
}
|
|
|
|
if (chainOK != PR_TRUE) {
|
|
|
|
if (verifyLog) {
|
|
|
|
insertErrorIntoVerifyLog(cert,
|
|
|
|
SEC_ERROR_APPLICATION_CALLBACK_ERROR,
|
|
|
|
verifyLog);
|
|
|
|
}
|
|
|
|
PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rv == SECSuccess && validationChain) {
|
|
|
|
*validationChain = certChain.release();
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
}
|
2014-02-05 14:49:10 -08:00
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2014-01-22 17:13:19 -08:00
|
|
|
#ifndef NSS_NO_LIBPKIX
|
|
|
|
static void
|
|
|
|
destroyCertListThatShouldNotExist(CERTCertList** certChain)
|
|
|
|
{
|
|
|
|
PR_ASSERT(certChain);
|
|
|
|
PR_ASSERT(!*certChain);
|
|
|
|
if (certChain && *certChain) {
|
|
|
|
// There SHOULD not be a validation chain on failure, asserion here for
|
|
|
|
// the debug builds AND a fallback for production builds
|
|
|
|
CERT_DestroyCertList(*certChain);
|
|
|
|
*certChain = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-02-10 11:41:12 -08:00
|
|
|
static SECStatus
|
|
|
|
BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
|
|
|
|
PRTime time, KeyUsages ku1, KeyUsages ku2,
|
|
|
|
KeyUsages ku3, SECOidTag eku,
|
2014-02-24 12:37:45 -08:00
|
|
|
SECOidTag requiredPolicy,
|
2014-02-16 17:35:40 -08:00
|
|
|
const SECItem* stapledOCSPResponse,
|
2014-02-10 11:41:12 -08:00
|
|
|
ScopedCERTCertList& builtChain)
|
|
|
|
{
|
|
|
|
PR_ASSERT(ku1);
|
|
|
|
PR_ASSERT(ku2);
|
|
|
|
|
2014-04-25 16:29:26 -07:00
|
|
|
SECStatus rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, ku1,
|
|
|
|
eku, requiredPolicy,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
if (rv != SECSuccess && ku2 &&
|
|
|
|
PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, ku2,
|
|
|
|
eku, requiredPolicy,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
if (rv != SECSuccess && ku3 &&
|
|
|
|
PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, ku3,
|
|
|
|
eku, requiredPolicy,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
if (rv != SECSuccess) {
|
|
|
|
PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
2014-03-20 14:29:21 -07:00
|
|
|
CertVerifier::MozillaPKIXVerifyCert(
|
2014-02-10 11:41:12 -08:00
|
|
|
CERTCertificate* cert,
|
|
|
|
const SECCertificateUsage usage,
|
|
|
|
const PRTime time,
|
|
|
|
void* pinArg,
|
|
|
|
const Flags flags,
|
2014-02-05 14:49:10 -08:00
|
|
|
ChainValidationCallbackState* callbackState,
|
2014-02-16 17:35:40 -08:00
|
|
|
/*optional*/ const SECItem* stapledOCSPResponse,
|
2014-03-20 14:29:21 -07:00
|
|
|
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
|
2014-02-23 22:15:53 -08:00
|
|
|
/*optional out*/ SECOidTag* evOidPolicy)
|
2014-02-10 11:41:12 -08:00
|
|
|
{
|
2014-03-20 14:29:21 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of MozillaPKIXVerifyCert\n"));
|
2014-02-23 22:15:53 -08:00
|
|
|
|
|
|
|
PR_ASSERT(cert);
|
|
|
|
PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
|
|
|
|
|
|
|
|
if (validationChain) {
|
|
|
|
*validationChain = nullptr;
|
|
|
|
}
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cert ||
|
|
|
|
(usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
|
|
|
|
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-02-05 14:49:10 -08:00
|
|
|
CERTChainVerifyCallback callbackContainer;
|
|
|
|
callbackContainer.isChainValid = chainValidationCallback;
|
|
|
|
callbackContainer.isChainValidArg = callbackState;
|
|
|
|
|
2014-02-23 22:15:53 -08:00
|
|
|
NSSCertDBTrustDomain::OCSPFetching ocspFetching
|
|
|
|
= !mOCSPDownloadEnabled ||
|
|
|
|
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
|
|
|
|
: !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
|
|
|
|
: NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
|
|
|
|
|
2014-02-10 11:41:12 -08:00
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
// TODO(bug 970750): anyExtendedKeyUsage
|
|
|
|
// TODO: encipherOnly/decipherOnly
|
|
|
|
// S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
|
|
|
|
// S/MIME EKU: http://tools.ietf.org/html/rfc3850#section-4.4.4
|
|
|
|
|
|
|
|
// TODO(bug 915931): Pass in stapled OCSP response in all calls to
|
|
|
|
// BuildCertChain.
|
|
|
|
|
2014-03-20 14:29:21 -07:00
|
|
|
mozilla::pkix::ScopedCERTCertList builtChain;
|
2014-02-10 11:41:12 -08:00
|
|
|
switch (usage) {
|
|
|
|
case certificateUsageSSLClient: {
|
|
|
|
// XXX: We don't really have a trust bit for SSL client authentication so
|
|
|
|
// just use trustEmail as it is the closest alternative.
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
2014-02-10 11:41:12 -08:00
|
|
|
SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageSSLServer: {
|
|
|
|
// TODO: When verifying a certificate in an SSL handshake, we should
|
|
|
|
// restrict the acceptable key usage based on the key exchange method
|
|
|
|
// chosen by the server.
|
2014-02-23 22:15:53 -08:00
|
|
|
|
|
|
|
#ifndef MOZ_NO_EV_CERTS
|
|
|
|
// Try to validate for EV first.
|
|
|
|
SECOidTag evPolicy = SEC_OID_UNKNOWN;
|
|
|
|
rv = GetFirstEVPolicy(cert, evPolicy);
|
|
|
|
if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
|
|
|
|
NSSCertDBTrustDomain
|
|
|
|
trustDomain(trustSSL,
|
|
|
|
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
|
|
|
|
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
|
|
|
|
: NSSCertDBTrustDomain::FetchOCSPForEV,
|
2014-02-05 14:49:10 -08:00
|
|
|
mOCSPCache, pinArg, &callbackContainer);
|
2014-02-23 22:15:53 -08:00
|
|
|
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
|
|
|
|
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
|
|
|
|
KU_KEY_ENCIPHERMENT, // RSA
|
|
|
|
KU_KEY_AGREEMENT, // ECDH/DH
|
|
|
|
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
|
|
|
evPolicy, stapledOCSPResponse,
|
|
|
|
builtChain);
|
|
|
|
if (rv == SECSuccess) {
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = evPolicy;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
builtChain = nullptr; // clear built chain, just in case.
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (flags & FLAG_MUST_BE_EV) {
|
|
|
|
PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
|
|
|
|
rv = SECFailure;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now try non-EV.
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
2014-02-05 14:49:10 -08:00
|
|
|
pinArg, &callbackContainer);
|
2014-02-10 11:41:12 -08:00
|
|
|
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
|
|
|
|
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
|
|
|
|
KU_KEY_ENCIPHERMENT, // RSA
|
|
|
|
KU_KEY_AGREEMENT, // ECDH/DH
|
|
|
|
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageSSLCA: {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
|
2014-02-10 11:41:12 -08:00
|
|
|
KU_KEY_CERT_SIGN,
|
|
|
|
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageEmailSigner: {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
2014-02-10 11:41:12 -08:00
|
|
|
SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageEmailRecipient: {
|
|
|
|
// TODO: The higher level S/MIME processing should pass in which key
|
|
|
|
// usage it is trying to verify for, and base its algorithm choices
|
|
|
|
// based on the result of the verification(s).
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-02-10 11:41:12 -08:00
|
|
|
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
|
|
|
|
KU_KEY_ENCIPHERMENT, // RSA
|
|
|
|
KU_KEY_AGREEMENT, // ECDH/DH
|
|
|
|
0,
|
|
|
|
SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageObjectSigner: {
|
2014-02-23 22:15:53 -08:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
|
2014-03-12 13:08:48 -07:00
|
|
|
mOCSPCache, pinArg);
|
2014-04-25 16:29:26 -07:00
|
|
|
rv = BuildCertChain(trustDomain, cert, time,
|
|
|
|
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
2014-02-10 11:41:12 -08:00
|
|
|
SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
|
2014-02-24 12:37:45 -08:00
|
|
|
SEC_OID_X509_ANY_POLICY,
|
2014-02-16 17:35:40 -08:00
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageVerifyCA:
|
|
|
|
case certificateUsageStatusResponder: {
|
|
|
|
// XXX This is a pretty useless way to verify a certificate. It is used
|
|
|
|
// by the implementation of window.crypto.importCertificates and in the
|
|
|
|
// certificate viewer UI. Because we don't know what trust bit is
|
|
|
|
// interesting, we just try them all.
|
2014-03-20 14:29:21 -07:00
|
|
|
mozilla::pkix::EndEntityOrCA endEntityOrCA;
|
|
|
|
mozilla::pkix::KeyUsages keyUsage;
|
2014-02-10 11:41:12 -08:00
|
|
|
SECOidTag eku;
|
|
|
|
if (usage == certificateUsageVerifyCA) {
|
2014-04-25 16:29:26 -07:00
|
|
|
endEntityOrCA = EndEntityOrCA::MustBeCA;
|
2014-02-10 11:41:12 -08:00
|
|
|
keyUsage = KU_KEY_CERT_SIGN;
|
|
|
|
eku = SEC_OID_UNKNOWN;
|
|
|
|
} else {
|
2014-04-25 16:29:26 -07:00
|
|
|
endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
|
2014-02-10 11:41:12 -08:00
|
|
|
keyUsage = KU_DIGITAL_SIGNATURE;
|
|
|
|
eku = SEC_OID_OCSP_RESPONDER;
|
|
|
|
}
|
|
|
|
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-02-10 11:41:12 -08:00
|
|
|
rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
|
2014-02-24 12:37:45 -08:00
|
|
|
keyUsage, eku, SEC_OID_X509_ANY_POLICY,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-02-10 11:41:12 -08:00
|
|
|
rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
|
2014-02-24 12:37:45 -08:00
|
|
|
eku, SEC_OID_X509_ANY_POLICY,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
|
|
|
|
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
|
2014-03-12 13:08:48 -07:00
|
|
|
ocspFetching, mOCSPCache,
|
|
|
|
pinArg);
|
2014-02-10 11:41:12 -08:00
|
|
|
rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
|
2014-02-24 12:37:45 -08:00
|
|
|
keyUsage, eku, SEC_OID_X509_ANY_POLICY,
|
|
|
|
stapledOCSPResponse, builtChain);
|
2014-02-10 11:41:12 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validationChain && rv == SECSuccess) {
|
|
|
|
*validationChain = builtChain.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
SECStatus
|
2014-01-21 17:30:44 -08:00
|
|
|
CertVerifier::VerifyCert(CERTCertificate* cert,
|
2012-10-27 00:11:35 -07:00
|
|
|
const SECCertificateUsage usage,
|
|
|
|
const PRTime time,
|
2014-01-19 14:05:40 -08:00
|
|
|
void* pinArg,
|
2014-02-05 14:49:10 -08:00
|
|
|
const char* hostname,
|
2012-10-27 00:11:35 -07:00
|
|
|
const Flags flags,
|
2014-02-05 14:49:10 -08:00
|
|
|
/*optional in*/ const SECItem* stapledOCSPResponse,
|
2014-01-22 17:13:19 -08:00
|
|
|
/*optional out*/ ScopedCERTCertList* validationChain,
|
2014-01-21 17:30:44 -08:00
|
|
|
/*optional out*/ SECOidTag* evOidPolicy,
|
|
|
|
/*optional out*/ CERTVerifyLog* verifyLog)
|
2012-10-27 00:11:35 -07:00
|
|
|
{
|
2014-02-05 14:49:10 -08:00
|
|
|
ChainValidationCallbackState callbackState = { hostname,
|
|
|
|
mPinningEnforcementLevel,
|
|
|
|
usage,
|
|
|
|
time };
|
|
|
|
|
2014-03-20 14:29:21 -07:00
|
|
|
if (mImplementation == mozillapkix) {
|
|
|
|
return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
|
2014-02-05 14:49:10 -08:00
|
|
|
&callbackState, stapledOCSPResponse,
|
|
|
|
validationChain, evOidPolicy);
|
2014-02-23 22:15:53 -08:00
|
|
|
}
|
|
|
|
|
2014-01-24 13:57:35 -08:00
|
|
|
if (!cert)
|
2014-01-19 14:05:40 -08:00
|
|
|
{
|
|
|
|
PR_NOT_REACHED("Invalid arguments to CertVerifier::VerifyCert");
|
2012-10-27 00:11:35 -07:00
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (validationChain) {
|
|
|
|
*validationChain = nullptr;
|
|
|
|
}
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2013-12-06 13:42:44 -08:00
|
|
|
switch(usage){
|
|
|
|
case certificateUsageSSLClient:
|
|
|
|
case certificateUsageSSLServer:
|
|
|
|
case certificateUsageSSLCA:
|
|
|
|
case certificateUsageEmailSigner:
|
|
|
|
case certificateUsageEmailRecipient:
|
|
|
|
case certificateUsageObjectSigner:
|
2014-03-28 10:21:30 -07:00
|
|
|
case certificateUsageVerifyCA:
|
2013-12-06 13:42:44 -08:00
|
|
|
case certificateUsageStatusResponder:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-01-24 13:57:35 -08:00
|
|
|
if ((flags & FLAG_MUST_BE_EV) && usage != certificateUsageSSLServer) {
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-01-19 14:05:40 -08:00
|
|
|
#ifndef NSS_NO_LIBPKIX
|
2012-10-27 00:11:35 -07:00
|
|
|
ScopedCERTCertList trustAnchors;
|
|
|
|
SECStatus rv;
|
|
|
|
SECOidTag evPolicy = SEC_OID_UNKNOWN;
|
|
|
|
|
|
|
|
// Do EV checking only for sslserver usage
|
|
|
|
if (usage == certificateUsageSSLServer) {
|
2014-01-26 19:36:28 -08:00
|
|
|
SECStatus srv = GetFirstEVPolicy(cert, evPolicy);
|
2012-10-27 00:11:35 -07:00
|
|
|
if (srv == SECSuccess) {
|
|
|
|
if (evPolicy != SEC_OID_UNKNOWN) {
|
2014-01-26 19:36:28 -08:00
|
|
|
trustAnchors = GetRootsForOid(evPolicy);
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
if (!trustAnchors) {
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2013-10-21 14:27:46 -07:00
|
|
|
// pkix ignores an empty trustanchors list and
|
|
|
|
// decides then to use the whole set of trust in the DB
|
|
|
|
// so we set the evPolicy to unkown in this case
|
|
|
|
if (CERT_LIST_EMPTY(trustAnchors)) {
|
|
|
|
evPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
2012-10-27 00:11:35 -07:00
|
|
|
} else {
|
2014-01-24 13:57:35 -08:00
|
|
|
// No known EV policy found
|
|
|
|
if (flags & FLAG_MUST_BE_EV) {
|
|
|
|
PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2012-10-27 00:11:35 -07:00
|
|
|
// Do not setup EV verification params
|
|
|
|
evPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
2014-01-24 13:57:35 -08:00
|
|
|
if ((evPolicy == SEC_OID_UNKNOWN) && (flags & FLAG_MUST_BE_EV)) {
|
|
|
|
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
2014-01-21 17:30:44 -08:00
|
|
|
|
2014-01-22 17:13:19 -08:00
|
|
|
PR_ASSERT(evPolicy == SEC_OID_UNKNOWN || trustAnchors);
|
2012-10-27 00:11:35 -07:00
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
size_t validationChainLocation = 0;
|
|
|
|
size_t validationTrustAnchorLocation = 0;
|
|
|
|
CERTValOutParam cvout[4];
|
|
|
|
if (verifyLog) {
|
|
|
|
cvout[i].type = cert_po_errorLog;
|
|
|
|
cvout[i].value.pointer.log = verifyLog;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (validationChain) {
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n"));
|
2012-10-27 00:11:35 -07:00
|
|
|
validationChainLocation = i;
|
|
|
|
cvout[i].type = cert_po_certList;
|
2014-01-22 17:13:19 -08:00
|
|
|
cvout[i].value.pointer.chain = nullptr;
|
2012-10-27 00:11:35 -07:00
|
|
|
++i;
|
|
|
|
validationTrustAnchorLocation = i;
|
|
|
|
cvout[i].type = cert_po_trustAnchor;
|
2014-01-22 17:13:19 -08:00
|
|
|
cvout[i].value.pointer.cert = nullptr;
|
2012-10-27 00:11:35 -07:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
cvout[i].type = cert_po_end;
|
|
|
|
|
|
|
|
CERTRevocationFlags rev;
|
|
|
|
|
|
|
|
CERTRevocationMethodIndex revPreferredMethods[2];
|
|
|
|
rev.leafTests.preferred_methods =
|
|
|
|
rev.chainTests.preferred_methods = revPreferredMethods;
|
|
|
|
|
|
|
|
uint64_t revFlagsPerMethod[2];
|
|
|
|
rev.leafTests.cert_rev_flags_per_method =
|
|
|
|
rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod;
|
|
|
|
rev.leafTests.number_of_preferred_methods =
|
|
|
|
rev.chainTests.number_of_preferred_methods = 1;
|
|
|
|
|
|
|
|
rev.leafTests.number_of_defined_methods =
|
|
|
|
rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1;
|
|
|
|
|
|
|
|
const bool localOnly = flags & FLAG_LOCAL_ONLY;
|
2014-02-05 14:49:14 -08:00
|
|
|
CERTValInParam cvin[7];
|
2012-10-27 00:11:35 -07:00
|
|
|
|
|
|
|
// Parameters for both EV and DV validation
|
|
|
|
cvin[0].type = cert_pi_useAIACertFetch;
|
|
|
|
cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly;
|
|
|
|
cvin[1].type = cert_pi_revocationFlags;
|
|
|
|
cvin[1].value.pointer.revocation = &rev;
|
|
|
|
cvin[2].type = cert_pi_date;
|
|
|
|
cvin[2].value.scalar.time = time;
|
|
|
|
i = 3;
|
2014-02-05 14:49:14 -08:00
|
|
|
|
|
|
|
CERTChainVerifyCallback callbackContainer;
|
|
|
|
if (usage == certificateUsageSSLServer) {
|
|
|
|
callbackContainer.isChainValid = chainValidationCallback;
|
2014-02-05 14:49:10 -08:00
|
|
|
callbackContainer.isChainValidArg = &callbackState;
|
2014-02-05 14:49:14 -08:00
|
|
|
cvin[i].type = cert_pi_chainVerifyCallback;
|
|
|
|
cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
const size_t evParamLocation = i;
|
|
|
|
|
|
|
|
if (evPolicy != SEC_OID_UNKNOWN) {
|
|
|
|
// EV setup!
|
2012-10-27 00:11:35 -07:00
|
|
|
// XXX 859872 The current flags are not quite correct. (use
|
|
|
|
// of ocsp flags for crl preferences).
|
2013-12-02 11:08:06 -08:00
|
|
|
uint64_t ocspRevMethodFlags =
|
2012-10-27 00:11:35 -07:00
|
|
|
CERT_REV_M_TEST_USING_THIS_METHOD
|
2012-10-27 00:11:35 -07:00
|
|
|
| ((mOCSPDownloadEnabled && !localOnly) ?
|
|
|
|
CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
|
2012-10-27 00:11:35 -07:00
|
|
|
| CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
|
|
|
|
| CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
|
|
|
|
| CERT_REV_M_IGNORE_MISSING_FRESH_INFO
|
2013-12-02 11:08:06 -08:00
|
|
|
| CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
|
|
|
|
| (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
|
2013-12-02 11:08:06 -08:00
|
|
|
rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl]
|
|
|
|
= CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD;
|
2013-10-24 14:32:09 -07:00
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
|
2013-10-24 14:32:09 -07:00
|
|
|
rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp]
|
2013-12-02 11:08:06 -08:00
|
|
|
= ocspRevMethodFlags;
|
2012-10-27 00:11:35 -07:00
|
|
|
|
|
|
|
rev.leafTests.cert_rev_method_independent_flags =
|
|
|
|
rev.chainTests.cert_rev_method_independent_flags =
|
|
|
|
// avoiding the network is good, let's try local first
|
|
|
|
CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
|
|
|
|
// is overall revocation requirement strict or relaxed?
|
|
|
|
| CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
|
|
|
|
;
|
|
|
|
|
|
|
|
rev.leafTests.preferred_methods[0] =
|
|
|
|
rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
|
|
|
|
|
|
|
|
cvin[i].type = cert_pi_policyOID;
|
|
|
|
cvin[i].value.arraySize = 1;
|
|
|
|
cvin[i].value.array.oids = &evPolicy;
|
|
|
|
++i;
|
2014-01-22 17:13:19 -08:00
|
|
|
PR_ASSERT(trustAnchors);
|
2012-10-27 00:11:35 -07:00
|
|
|
cvin[i].type = cert_pi_trustAnchors;
|
2014-01-22 17:13:19 -08:00
|
|
|
cvin[i].value.pointer.chain = trustAnchors.get();
|
2012-10-27 00:11:35 -07:00
|
|
|
++i;
|
|
|
|
|
|
|
|
cvin[i].type = cert_pi_end;
|
|
|
|
|
|
|
|
rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
|
|
|
|
if (rv == SECSuccess) {
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = evPolicy;
|
|
|
|
}
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
2012-10-27 00:11:35 -07:00
|
|
|
("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n"));
|
|
|
|
goto pkix_done;
|
|
|
|
}
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
2013-11-21 13:40:31 -08:00
|
|
|
("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n"));
|
2014-01-24 13:57:35 -08:00
|
|
|
if (flags & FLAG_MUST_BE_EV) {
|
|
|
|
return rv;
|
|
|
|
}
|
2014-01-22 17:13:19 -08:00
|
|
|
if (validationChain) {
|
|
|
|
destroyCertListThatShouldNotExist(
|
|
|
|
&cvout[validationChainLocation].value.pointer.chain);
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (verifyLog) {
|
|
|
|
// Cleanup the log so that it is ready the the next validation
|
2014-01-21 17:30:44 -08:00
|
|
|
CERTVerifyLogNode* i_node;
|
2012-10-27 00:11:35 -07:00
|
|
|
for (i_node = verifyLog->head; i_node; i_node = i_node->next) {
|
|
|
|
//destroy cert if any.
|
|
|
|
if (i_node->cert) {
|
|
|
|
CERT_DestroyCertificate(i_node->cert);
|
|
|
|
}
|
|
|
|
// No need to cleanup the actual nodes in the arena.
|
|
|
|
}
|
|
|
|
verifyLog->count = 0;
|
|
|
|
verifyLog->head = nullptr;
|
|
|
|
verifyLog->tail = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2014-01-19 14:05:40 -08:00
|
|
|
#endif
|
2012-10-27 00:11:35 -07:00
|
|
|
|
2014-01-17 11:04:09 -08:00
|
|
|
// If we're here, PKIX EV verification failed.
|
|
|
|
// If requested, don't do DV fallback.
|
2014-01-24 13:57:35 -08:00
|
|
|
if (flags & FLAG_MUST_BE_EV) {
|
2014-01-19 14:05:40 -08:00
|
|
|
PR_ASSERT(*evOidPolicy == SEC_OID_UNKNOWN);
|
2014-01-24 13:57:35 -08:00
|
|
|
#ifdef NSS_NO_LIBPKIX
|
|
|
|
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
|
|
|
#else
|
|
|
|
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
|
|
|
#endif
|
|
|
|
return SECFailure;
|
2014-01-17 11:04:09 -08:00
|
|
|
}
|
|
|
|
|
2014-01-19 14:05:40 -08:00
|
|
|
if (mImplementation == classic) {
|
2012-10-27 00:11:35 -07:00
|
|
|
// XXX: we do not care about the localOnly flag (currently) as the
|
|
|
|
// caller that wants localOnly should disable and reenable the fetching.
|
2014-02-05 14:49:10 -08:00
|
|
|
return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
|
|
|
|
validationChain, verifyLog);
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
|
2014-01-19 14:05:40 -08:00
|
|
|
#ifdef NSS_NO_LIBPKIX
|
|
|
|
PR_NOT_REACHED("libpkix implementation chosen but not even compiled in");
|
|
|
|
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
|
|
|
return SECFailure;
|
|
|
|
#else
|
|
|
|
PR_ASSERT(mImplementation == libpkix);
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
// The current flags check the chain the same way as the leafs
|
|
|
|
rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
|
|
|
|
rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
|
|
|
|
// implicit default source - makes no sense for CRLs
|
|
|
|
CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
|
|
|
|
|
|
|
|
// let's not stop on fresh CRL. If OCSP is enabled, too, let's check it
|
|
|
|
| CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO
|
|
|
|
|
|
|
|
// no fresh CRL? well, let other flag decide whether to fail or not
|
|
|
|
| CERT_REV_M_IGNORE_MISSING_FRESH_INFO
|
|
|
|
|
|
|
|
// testing using local CRLs is always allowed
|
|
|
|
| CERT_REV_M_TEST_USING_THIS_METHOD
|
|
|
|
|
|
|
|
// no local crl and don't know where to get it from? ignore
|
|
|
|
| CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
|
|
|
|
|
|
|
|
// crl download based on parameter
|
|
|
|
| ((mCRLDownloadEnabled && !localOnly) ?
|
|
|
|
CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
|
|
|
|
;
|
|
|
|
|
|
|
|
rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
|
|
|
|
rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
|
|
|
|
// use OCSP
|
|
|
|
CERT_REV_M_TEST_USING_THIS_METHOD
|
|
|
|
|
|
|
|
// if app has a default OCSP responder configured, let's use it
|
|
|
|
| CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
|
|
|
|
|
|
|
|
// of course OCSP doesn't work without a source. let's accept such certs
|
|
|
|
| CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
|
|
|
|
|
|
|
|
// if ocsp is required stop on lack of freshness
|
|
|
|
| (mOCSPStrict ?
|
|
|
|
CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO)
|
|
|
|
|
|
|
|
// ocsp success is sufficient
|
|
|
|
| CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
|
|
|
|
|
|
|
|
// ocsp enabled controls network fetching, too
|
|
|
|
| ((mOCSPDownloadEnabled && !localOnly) ?
|
|
|
|
CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
|
2014-01-21 17:30:44 -08:00
|
|
|
|
2013-10-24 14:32:09 -07:00
|
|
|
| (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
|
2012-10-27 00:11:35 -07:00
|
|
|
;
|
|
|
|
|
|
|
|
rev.leafTests.preferred_methods[0] =
|
2013-12-06 00:55:17 -08:00
|
|
|
rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
|
2012-10-27 00:11:35 -07:00
|
|
|
|
|
|
|
rev.leafTests.cert_rev_method_independent_flags =
|
|
|
|
rev.chainTests.cert_rev_method_independent_flags =
|
|
|
|
// avoiding the network is good, let's try local first
|
2013-12-16 00:09:21 -08:00
|
|
|
CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
|
2012-10-27 00:11:35 -07:00
|
|
|
|
|
|
|
// Skip EV parameters
|
|
|
|
cvin[evParamLocation].type = cert_pi_end;
|
|
|
|
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n"));
|
2012-10-27 00:11:35 -07:00
|
|
|
rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
|
|
|
|
|
|
|
|
pkix_done:
|
|
|
|
if (validationChain) {
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n"));
|
2012-10-27 00:11:35 -07:00
|
|
|
ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert);
|
|
|
|
|
|
|
|
if (rv == SECSuccess) {
|
|
|
|
if (! cvout[validationChainLocation].value.pointer.chain) {
|
2012-10-27 00:11:35 -07:00
|
|
|
PR_SetError(PR_UNKNOWN_ERROR, 0);
|
2012-10-27 00:11:35 -07:00
|
|
|
return SECFailure;
|
|
|
|
}
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n"));
|
2012-10-27 00:11:35 -07:00
|
|
|
*validationChain = cvout[validationChainLocation].value.pointer.chain;
|
|
|
|
if (trustAnchor) {
|
|
|
|
// we should only add the issuer to the chain if it is not already
|
|
|
|
// present. On CA cert checking, the issuer is the same cert, so in
|
|
|
|
// that case we do not add the cert to the chain.
|
2014-01-22 17:13:19 -08:00
|
|
|
if (!CERT_CompareCerts(trustAnchor.get(), cert)) {
|
2013-07-10 23:47:09 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: adding issuer to tail for display\n"));
|
2012-10-27 00:11:35 -07:00
|
|
|
// note: rv is reused to catch errors on cert creation!
|
2014-01-22 17:13:19 -08:00
|
|
|
ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor.get()));
|
|
|
|
rv = CERT_AddCertToListTail(validationChain->get(), tempCert.get());
|
2013-04-27 14:56:19 -07:00
|
|
|
if (rv == SECSuccess) {
|
2014-01-22 17:13:19 -08:00
|
|
|
tempCert.release(); // ownership traferred to validationChain
|
2013-04-27 14:56:19 -07:00
|
|
|
} else {
|
2012-10-27 00:11:35 -07:00
|
|
|
*validationChain = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2014-01-22 17:13:19 -08:00
|
|
|
destroyCertListThatShouldNotExist(
|
|
|
|
&cvout[validationChainLocation].value.pointer.chain);
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
}
|
2012-10-27 00:11:35 -07:00
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
return rv;
|
2012-10-27 00:11:35 -07:00
|
|
|
#endif
|
2012-10-27 00:11:35 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 16:30:59 -07:00
|
|
|
SECStatus
|
|
|
|
CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
|
2013-09-27 19:53:36 -07:00
|
|
|
/*optional*/ const SECItem* stapledOCSPResponse,
|
2013-07-08 16:30:59 -07:00
|
|
|
PRTime time,
|
|
|
|
/*optional*/ void* pinarg,
|
|
|
|
const char* hostname,
|
|
|
|
bool saveIntermediatesInPermanentDatabase,
|
2014-03-20 14:29:21 -07:00
|
|
|
/*optional out*/ mozilla::pkix::ScopedCERTCertList* certChainOut,
|
2013-07-08 16:30:59 -07:00
|
|
|
/*optional out*/ SECOidTag* evOidPolicy)
|
|
|
|
{
|
|
|
|
PR_ASSERT(peerCert);
|
|
|
|
// XXX: PR_ASSERT(pinarg)
|
|
|
|
PR_ASSERT(hostname);
|
|
|
|
PR_ASSERT(hostname[0]);
|
|
|
|
|
|
|
|
if (certChainOut) {
|
|
|
|
*certChainOut = nullptr;
|
|
|
|
}
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hostname || !hostname[0]) {
|
|
|
|
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-02-22 19:08:06 -08:00
|
|
|
// CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
|
|
|
|
// if VerifyCert succeeded.
|
2013-07-08 16:30:59 -07:00
|
|
|
ScopedCERTCertList validationChain;
|
2014-02-05 14:49:10 -08:00
|
|
|
SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
|
|
|
|
hostname, 0, stapledOCSPResponse, &validationChain,
|
|
|
|
evOidPolicy, nullptr);
|
2013-07-08 16:30:59 -07:00
|
|
|
if (rv != SECSuccess) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = CERT_VerifyCertName(peerCert, hostname);
|
|
|
|
if (rv != SECSuccess) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (saveIntermediatesInPermanentDatabase) {
|
|
|
|
SaveIntermediateCerts(validationChain);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (certChainOut) {
|
|
|
|
*certChainOut = validationChain.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
} } // namespace mozilla::psm
|