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-01-26 19:36:28 -08:00
|
|
|
#include "ExtendedValidation.h"
|
2013-07-08 16:30:59 -07:00
|
|
|
#include "NSSCertDBTrustDomain.h"
|
2014-05-28 15:28:03 -07:00
|
|
|
#include "NSSErrorsService.h"
|
2014-02-05 14:49:10 -08:00
|
|
|
#include "PublicKeyPinningService.h"
|
2012-10-27 00:11:35 -07:00
|
|
|
#include "cert.h"
|
2014-02-05 14:49:10 -08:00
|
|
|
#include "pk11pub.h"
|
2014-05-28 15:28:03 -07:00
|
|
|
#include "pkix/pkix.h"
|
2014-07-18 11:48:49 -07:00
|
|
|
#include "pkix/pkixnss.h"
|
2014-01-19 14:05:40 -08:00
|
|
|
#include "prerror.h"
|
2014-05-28 15:28:03 -07:00
|
|
|
#include "secerr.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
|
|
|
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-06-16 23:13:29 -07:00
|
|
|
CertVerifier::CertVerifier(ocsp_download_config odc,
|
2012-10-27 00:11:35 -07:00
|
|
|
ocsp_strict_config osc,
|
2014-02-05 14:49:10 -08:00
|
|
|
ocsp_get_config ogc,
|
|
|
|
pinning_enforcement_config pel)
|
2014-06-16 23:13:29 -07:00
|
|
|
: mOCSPDownloadEnabled(odc == ocsp_on)
|
2012-10-27 00:11:35 -07:00
|
|
|
, 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-02-05 14:49:10 -08:00
|
|
|
SECStatus
|
|
|
|
IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
|
|
|
|
result = false;
|
2014-07-06 15:55:38 -07:00
|
|
|
ScopedPK11SlotList slots;
|
2014-02-05 14:49:10 -08:00
|
|
|
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;
|
2014-08-02 08:49:12 -07:00
|
|
|
const Time time;
|
2014-02-05 14:49:10 -08:00
|
|
|
};
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-19 13:24:41 -07:00
|
|
|
bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
|
|
|
|
CertVerifier::pinningEnforceTestMode);
|
2014-02-05 14:49:10 -08:00
|
|
|
*chainOK = PublicKeyPinningService::
|
2014-05-19 13:04:40 -07:00
|
|
|
ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
|
|
|
|
enforceTestMode);
|
2014-02-05 14:49:10 -08:00
|
|
|
|
2014-02-05 14:49:14 -08:00
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
2014-07-18 11:48:49 -07:00
|
|
|
static Result
|
2014-07-31 12:17:31 -07:00
|
|
|
BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, Input certDER,
|
2014-08-02 08:49:12 -07:00
|
|
|
Time time, KeyUsage ku1, KeyUsage ku2,
|
2014-06-19 00:13:20 -07:00
|
|
|
KeyUsage ku3, KeyPurposeId eku,
|
2014-05-15 18:59:52 -07:00
|
|
|
const CertPolicyId& requiredPolicy,
|
2014-07-31 12:17:31 -07:00
|
|
|
const Input* stapledOCSPResponse)
|
2014-02-10 11:41:12 -08:00
|
|
|
{
|
2014-07-18 22:30:51 -07:00
|
|
|
Result rv = BuildCertChain(trustDomain, certDER, time,
|
2014-07-18 11:48:49 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity, ku1,
|
|
|
|
eku, requiredPolicy, stapledOCSPResponse);
|
|
|
|
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-04-25 16:29:26 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity, ku2,
|
2014-07-06 15:55:38 -07:00
|
|
|
eku, requiredPolicy, stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-04-25 16:29:26 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity, ku3,
|
2014-07-06 15:55:38 -07:00
|
|
|
eku, requiredPolicy, stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv != Success) {
|
|
|
|
rv = Result::ERROR_INADEQUATE_KEY_USAGE;
|
2014-02-10 11:41:12 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
2014-06-16 23:13:29 -07:00
|
|
|
CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
2014-08-02 08:49:12 -07:00
|
|
|
Time time, void* pinArg, const char* hostname,
|
2014-06-16 23:13:29 -07:00
|
|
|
const Flags flags,
|
2014-07-18 22:30:51 -07:00
|
|
|
/*optional*/ const SECItem* stapledOCSPResponseSECItem,
|
2014-07-06 15:55:38 -07:00
|
|
|
/*optional out*/ ScopedCERTCertList* builtChain,
|
2014-06-16 23:13:29 -07:00
|
|
|
/*optional out*/ SECOidTag* evOidPolicy)
|
2014-02-10 11:41:12 -08:00
|
|
|
{
|
2014-06-16 23:13:29 -07:00
|
|
|
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n"));
|
2014-02-23 22:15:53 -08:00
|
|
|
|
|
|
|
PR_ASSERT(cert);
|
|
|
|
PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
|
|
|
|
|
2014-07-06 15:55:38 -07:00
|
|
|
if (builtChain) {
|
|
|
|
*builtChain = nullptr;
|
2014-02-23 22:15:53 -08:00
|
|
|
}
|
|
|
|
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-07-18 22:30:51 -07:00
|
|
|
Result rv;
|
|
|
|
|
2014-07-31 12:17:31 -07:00
|
|
|
Input certDER;
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = certDER.Init(cert->derCert.data, cert->derCert.len);
|
|
|
|
if (rv != Success) {
|
|
|
|
PR_SetError(MapResultToPRErrorCode(rv), 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-06-16 23:13:29 -07:00
|
|
|
ChainValidationCallbackState callbackState = {
|
|
|
|
hostname, mPinningEnforcementLevel, usage, time
|
|
|
|
};
|
2014-02-05 14:49:10 -08:00
|
|
|
CERTChainVerifyCallback callbackContainer;
|
|
|
|
callbackContainer.isChainValid = chainValidationCallback;
|
2014-06-16 23:13:29 -07:00
|
|
|
callbackContainer.isChainValidArg = &callbackState;
|
2014-02-05 14:49:10 -08:00
|
|
|
|
2014-02-23 22:15:53 -08:00
|
|
|
NSSCertDBTrustDomain::OCSPFetching ocspFetching
|
|
|
|
= !mOCSPDownloadEnabled ||
|
|
|
|
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
|
|
|
|
: !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
|
|
|
|
: NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
|
|
|
|
|
2014-05-21 15:42:21 -07:00
|
|
|
ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
|
|
|
|
: ocsp_get_disabled;
|
2014-02-10 11:41:12 -08:00
|
|
|
|
2014-07-31 12:17:31 -07:00
|
|
|
Input stapledOCSPResponseInput;
|
|
|
|
const Input* stapledOCSPResponse = nullptr;
|
2014-07-18 22:30:51 -07:00
|
|
|
if (stapledOCSPResponseSECItem) {
|
2014-07-31 12:17:31 -07:00
|
|
|
rv = stapledOCSPResponseInput.Init(stapledOCSPResponseSECItem->data,
|
|
|
|
stapledOCSPResponseSECItem->len);
|
2014-07-18 22:30:51 -07:00
|
|
|
if (rv != Success) {
|
|
|
|
// The stapled OCSP response was too big.
|
|
|
|
PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2014-07-31 12:17:31 -07:00
|
|
|
stapledOCSPResponse = &stapledOCSPResponseInput;
|
2014-07-18 22:30:51 -07:00
|
|
|
}
|
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,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, nullptr,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity,
|
|
|
|
KeyUsage::digitalSignature,
|
2014-05-14 01:02:34 -07:00
|
|
|
KeyPurposeId::id_kp_clientAuth,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
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.
|
2014-05-15 18:59:52 -07:00
|
|
|
CertPolicyId evPolicy;
|
|
|
|
SECOidTag evPolicyOidTag;
|
2014-07-18 11:48:49 -07:00
|
|
|
SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
|
|
|
|
if (srv == SECSuccess) {
|
2014-02-23 22:15:53 -08:00
|
|
|
NSSCertDBTrustDomain
|
|
|
|
trustDomain(trustSSL,
|
|
|
|
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
|
|
|
|
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
|
|
|
|
: NSSCertDBTrustDomain::FetchOCSPForEV,
|
2014-07-06 15:55:38 -07:00
|
|
|
mOCSPCache, pinArg, ocspGETConfig,
|
|
|
|
&callbackContainer, builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
KeyUsage::digitalSignature,// (EC)DHE
|
|
|
|
KeyUsage::keyEncipherment, // RSA
|
|
|
|
KeyUsage::keyAgreement, // (EC)DH
|
2014-05-14 01:02:34 -07:00
|
|
|
KeyPurposeId::id_kp_serverAuth,
|
2014-07-06 15:55:38 -07:00
|
|
|
evPolicy, stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv == Success) {
|
2014-02-23 22:15:53 -08:00
|
|
|
if (evOidPolicy) {
|
2014-05-15 18:59:52 -07:00
|
|
|
*evOidPolicy = evPolicyOidTag;
|
2014-02-23 22:15:53 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (flags & FLAG_MUST_BE_EV) {
|
2014-07-18 11:48:49 -07:00
|
|
|
rv = Result::ERROR_POLICY_VALIDATION_FAILED;
|
2014-02-23 22:15:53 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now try non-EV.
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, &callbackContainer,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
KeyUsage::digitalSignature, // (EC)DHE
|
|
|
|
KeyUsage::keyEncipherment, // RSA
|
|
|
|
KeyUsage::keyAgreement, // (EC)DH
|
2014-05-14 01:02:34 -07:00
|
|
|
KeyPurposeId::id_kp_serverAuth,
|
2014-05-15 18:59:52 -07:00
|
|
|
CertPolicyId::anyPolicy,
|
2014-07-06 15:55:38 -07:00
|
|
|
stapledOCSPResponse);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageSSLCA: {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, nullptr,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-07-06 15:55:38 -07:00
|
|
|
EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
|
2014-06-19 00:13:20 -07:00
|
|
|
KeyPurposeId::id_kp_serverAuth,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageEmailSigner: {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, nullptr,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity,
|
|
|
|
KeyUsage::digitalSignature,
|
2014-05-15 18:59:52 -07:00
|
|
|
KeyPurposeId::id_kp_emailProtection,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
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,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, nullptr,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity,
|
|
|
|
KeyUsage::keyEncipherment, // RSA
|
|
|
|
KeyPurposeId::id_kp_emailProtection,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity,
|
|
|
|
KeyUsage::keyAgreement, // ECDH/DH
|
|
|
|
KeyPurposeId::id_kp_emailProtection,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
2014-06-19 00:13:20 -07:00
|
|
|
}
|
2014-02-10 11:41:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case certificateUsageObjectSigner: {
|
2014-02-23 22:15:53 -08:00
|
|
|
NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
|
2014-07-06 15:55:38 -07:00
|
|
|
mOCSPCache, pinArg, ocspGETConfig,
|
|
|
|
nullptr, builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(trustDomain, certDER, time,
|
2014-06-19 00:13:20 -07:00
|
|
|
EndEntityOrCA::MustBeEndEntity,
|
|
|
|
KeyUsage::digitalSignature,
|
2014-05-15 18:59:52 -07:00
|
|
|
KeyPurposeId::id_kp_codeSigning,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
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
|
2014-08-14 09:38:42 -07:00
|
|
|
// by the certificate viewer UI. Because we don't know what trust bit is
|
2014-02-10 11:41:12 -08:00
|
|
|
// interesting, we just try them all.
|
2014-03-20 14:29:21 -07:00
|
|
|
mozilla::pkix::EndEntityOrCA endEntityOrCA;
|
2014-06-19 00:13:20 -07:00
|
|
|
mozilla::pkix::KeyUsage keyUsage;
|
2014-05-14 01:02:34 -07:00
|
|
|
KeyPurposeId eku;
|
2014-02-10 11:41:12 -08:00
|
|
|
if (usage == certificateUsageVerifyCA) {
|
2014-04-25 16:29:26 -07:00
|
|
|
endEntityOrCA = EndEntityOrCA::MustBeCA;
|
2014-06-19 00:13:20 -07:00
|
|
|
keyUsage = KeyUsage::keyCertSign;
|
2014-05-14 01:02:34 -07:00
|
|
|
eku = KeyPurposeId::anyExtendedKeyUsage;
|
2014-02-10 11:41:12 -08:00
|
|
|
} else {
|
2014-04-25 16:29:26 -07:00
|
|
|
endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
|
2014-06-19 00:13:20 -07:00
|
|
|
keyUsage = KeyUsage::digitalSignature;
|
2014-05-14 01:02:34 -07:00
|
|
|
eku = KeyPurposeId::id_kp_OCSPSigning;
|
2014-02-10 11:41:12 -08:00
|
|
|
}
|
|
|
|
|
2014-07-06 15:55:38 -07:00
|
|
|
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
|
|
|
|
ocspGETConfig, nullptr, builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
|
2014-05-15 18:59:52 -07:00
|
|
|
keyUsage, eku, CertPolicyId::anyPolicy,
|
2014-07-06 15:55:38 -07:00
|
|
|
stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
|
2014-03-12 13:08:48 -07:00
|
|
|
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig, nullptr,
|
|
|
|
builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
|
2014-07-03 16:59:42 -07:00
|
|
|
keyUsage, eku, CertPolicyId::anyPolicy,
|
2014-07-06 15:55:38 -07:00
|
|
|
stapledOCSPResponse);
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
|
2014-02-10 11:41:12 -08:00
|
|
|
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
|
2014-03-12 13:08:48 -07:00
|
|
|
ocspFetching, mOCSPCache,
|
2014-07-06 15:55:38 -07:00
|
|
|
pinArg, ocspGETConfig,
|
|
|
|
nullptr, builtChain);
|
2014-07-18 22:30:51 -07:00
|
|
|
rv = BuildCertChain(objectSigningTrust, certDER, time,
|
2014-07-03 16:59:42 -07:00
|
|
|
endEntityOrCA, keyUsage, eku,
|
2014-07-06 15:55:38 -07:00
|
|
|
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
2014-02-10 11:41:12 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2014-07-18 11:48:49 -07:00
|
|
|
rv = Result::FATAL_ERROR_INVALID_ARGS;
|
2014-02-10 11:41:12 -08:00
|
|
|
}
|
|
|
|
|
2014-07-18 11:48:49 -07:00
|
|
|
if (rv != Success) {
|
|
|
|
PR_SetError(MapResultToPRErrorCode(rv), 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SECSuccess;
|
2014-02-10 11:41:12 -08: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,
|
2014-08-02 08:49:12 -07:00
|
|
|
Time time,
|
2013-07-08 16:30:59 -07:00
|
|
|
/*optional*/ void* pinarg,
|
|
|
|
const char* hostname,
|
|
|
|
bool saveIntermediatesInPermanentDatabase,
|
2014-08-21 10:37:23 -07:00
|
|
|
Flags flags,
|
2014-07-06 15:55:38 -07:00
|
|
|
/*optional out*/ ScopedCERTCertList* builtChain,
|
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]);
|
|
|
|
|
2014-07-06 15:55:38 -07:00
|
|
|
if (builtChain) {
|
|
|
|
*builtChain = nullptr;
|
2013-07-08 16:30:59 -07:00
|
|
|
}
|
|
|
|
if (evOidPolicy) {
|
|
|
|
*evOidPolicy = SEC_OID_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hostname || !hostname[0]) {
|
|
|
|
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
2014-07-14 16:43:33 -07:00
|
|
|
ScopedCERTCertList builtChainTemp;
|
2014-02-22 19:08:06 -08:00
|
|
|
// CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
|
|
|
|
// if VerifyCert succeeded.
|
2014-02-05 14:49:10 -08:00
|
|
|
SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
|
2014-08-21 10:37:23 -07:00
|
|
|
hostname, flags, stapledOCSPResponse,
|
|
|
|
&builtChainTemp, evOidPolicy);
|
2013-07-08 16:30:59 -07:00
|
|
|
if (rv != SECSuccess) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = CERT_VerifyCertName(peerCert, hostname);
|
|
|
|
if (rv != SECSuccess) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2014-07-14 16:43:33 -07:00
|
|
|
if (saveIntermediatesInPermanentDatabase) {
|
|
|
|
SaveIntermediateCerts(builtChainTemp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (builtChain) {
|
|
|
|
*builtChain = builtChainTemp.forget();
|
2013-07-08 16:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:11:35 -07:00
|
|
|
} } // namespace mozilla::psm
|