--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -369,33 +369,35 @@ CertVerifier::MozillaPKIXVerifyCert(
callbackContainer.isChainValidArg = callbackState;
NSSCertDBTrustDomain::OCSPFetching ocspFetching
= !mOCSPDownloadEnabled ||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
: !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
: NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
+ ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
+ : ocsp_get_disabled;
+
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.
mozilla::pkix::ScopedCERTCertList builtChain;
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.
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
KeyPurposeId::id_kp_clientAuth,
CertPolicyId::anyPolicy, stapledOCSPResponse,
builtChain);
break;
}
@@ -410,17 +412,17 @@ CertVerifier::MozillaPKIXVerifyCert(
SECOidTag evPolicyOidTag;
rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
if (rv == SECSuccess) {
NSSCertDBTrustDomain
trustDomain(trustSSL,
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
: NSSCertDBTrustDomain::FetchOCSPForEV,
- mOCSPCache, pinArg, &callbackContainer);
+ mOCSPCache, pinArg, ocspGETConfig, &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
KU_KEY_ENCIPHERMENT, // RSA
KU_KEY_AGREEMENT, // ECDH/DH
KeyPurposeId::id_kp_serverAuth,
evPolicy, stapledOCSPResponse,
builtChain);
if (rv == SECSuccess) {
@@ -436,67 +438,68 @@ CertVerifier::MozillaPKIXVerifyCert(
if (flags & FLAG_MUST_BE_EV) {
PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
rv = SECFailure;
break;
}
// Now try non-EV.
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
- pinArg, &callbackContainer);
+ pinArg, ocspGETConfig,
+ &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
KU_KEY_ENCIPHERMENT, // RSA
KU_KEY_AGREEMENT, // ECDH/DH
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
break;
}
case certificateUsageSSLCA: {
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
break;
}
case certificateUsageEmailSigner: {
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
KeyPurposeId::id_kp_emailProtection,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
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).
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KU_KEY_ENCIPHERMENT, // RSA
KU_KEY_AGREEMENT, // ECDH/DH
0,
KeyPurposeId::id_kp_emailProtection,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
break;
}
case certificateUsageObjectSigner: {
NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
- mOCSPCache, pinArg);
+ mOCSPCache, pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
KeyPurposeId::id_kp_codeSigning,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
break;
}
@@ -515,30 +518,30 @@ CertVerifier::MozillaPKIXVerifyCert(
eku = KeyPurposeId::anyExtendedKeyUsage;
} else {
endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
keyUsage = KU_DIGITAL_SIGNATURE;
eku = KeyPurposeId::id_kp_OCSPSigning;
}
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, ocspGETConfig);
rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
}
}
break;
}
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -39,21 +39,23 @@ namespace {
typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
} // unnamed namespace
NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
OCSPFetching ocspFetching,
OCSPCache& ocspCache,
void* pinArg,
+ CertVerifier::ocsp_get_config ocspGETConfig,
CERTChainVerifyCallback* checkChainCallback)
: mCertDBTrustType(certDBTrustType)
, mOCSPFetching(ocspFetching)
, mOCSPCache(ocspCache)
, mPinArg(pinArg)
+ , mOCSPGetConfig(ocspGETConfig)
, mCheckChainCallback(checkChainCallback)
{
}
SECStatus
NSSCertDBTrustDomain::FindPotentialIssuers(
const SECItem* encodedIssuerName, PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results)
@@ -333,17 +335,18 @@ NSSCertDBTrustDomain::CheckRevocation(
cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
issuerCert));
if (!request) {
return SECFailure;
}
response = DoOCSPRequest(arena.get(), url.get(), request,
- OCSPFetchingTypeToTimeoutTime(mOCSPFetching));
+ OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
+ mOCSPGetConfig == CertVerifier::ocsp_get_enabled);
}
if (!response) {
PRErrorCode error = PR_GetError();
if (error == 0) {
error = cachedResponseErrorCode;
}
PRTime timeout = time + ServerFailureDelay;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -53,16 +53,17 @@ public:
NeverFetchOCSP = 0,
FetchOCSPForDVSoftFail = 1,
FetchOCSPForDVHardFail = 2,
FetchOCSPForEV = 3,
LocalOnlyOCSPForEV = 4,
};
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache, void* pinArg,
+ CertVerifier::ocsp_get_config ocspGETConfig,
CERTChainVerifyCallback* checkChainCallback = nullptr);
virtual SECStatus FindPotentialIssuers(
const SECItem* encodedIssuerName,
PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results);
virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
@@ -90,14 +91,15 @@ private:
SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
const SECItem* encodedResponse, EncodedResponseSource responseSource);
const SECTrustType mCertDBTrustType;
const OCSPFetching mOCSPFetching;
OCSPCache& mOCSPCache; // non-owning!
void* mPinArg; // non-owning!
+ const CertVerifier::ocsp_get_config mOCSPGetConfig;
CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
};
} } // namespace mozilla::psm
#endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/certverifier/OCSPRequestor.cpp
+++ b/security/certverifier/OCSPRequestor.cpp
@@ -1,23 +1,28 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "OCSPRequestor.h"
+#include "mozilla/Base64.h"
#include "nsIURLParser.h"
#include "nsNSSCallbacks.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "pkix/ScopedPtr.h"
#include "secerr.h"
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gCertVerifierLog;
+#endif
+
namespace mozilla { namespace psm {
using mozilla::pkix::ScopedPtr;
void
ReleaseHttpServerSession(nsNSSHttpServerSession* httpServerSession)
{
delete httpServerSession;
@@ -28,18 +33,46 @@ typedef ScopedPtr<nsNSSHttpServerSession
void
ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession)
{
httpRequestSession->Release();
}
typedef ScopedPtr<nsNSSHttpRequestSession, ReleaseHttpRequestSession>
ScopedHTTPRequestSession;
-SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
- const SECItem* encodedRequest, PRIntervalTime timeout)
+static nsresult
+AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path)
+{
+ nsresult rv;
+ nsDependentCSubstring requestAsSubstring(
+ reinterpret_cast<const char*>(encodedRequest->data), encodedRequest->len);
+ nsCString base64Request;
+ rv = Base64Encode(requestAsSubstring, base64Request);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("Setting up OCSP GET path, pre path =%s\n",
+ PromiseFlatCString(path).get()));
+
+ // The path transformation is not a direct url encoding. Three characters
+ // need change '+' -> "%2B", '/' -> "%2F", and '=' -> '%3D'.
+ // http://tools.ietf.org/html/rfc5019#section-5
+ base64Request.ReplaceSubstring("+", "%2B");
+ base64Request.ReplaceSubstring("/", "%2F");
+ base64Request.ReplaceSubstring("=", "%3D");
+ path.Append(base64Request);
+ return NS_OK;
+}
+
+SECItem*
+DoOCSPRequest(PLArenaPool* arena, const char* url,
+ const SECItem* encodedRequest, PRIntervalTime timeout,
+ bool useGET)
{
nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
if (!urlParser) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
return nullptr;
}
uint32_t schemePos;
@@ -81,49 +114,66 @@ SECItem* DoOCSPRequest(PLArenaPool* aren
}
if (hostnameLen < 0) {
PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
return nullptr;
}
if (port == -1) {
port = 80;
}
+ nsAutoCString hostname(url + authorityPos + hostnamePos, hostnameLen);
- nsAutoCString hostname(url + authorityPos + hostnamePos, hostnameLen);
SEC_HTTP_SERVER_SESSION serverSessionPtr = nullptr;
if (nsNSSHttpInterface::createSessionFcn(hostname.BeginReading(), port,
&serverSessionPtr) != SECSuccess) {
PR_SetError(SEC_ERROR_NO_MEMORY, 0);
return nullptr;
}
-
ScopedHTTPServerSession serverSession(
reinterpret_cast<nsNSSHttpServerSession*>(serverSessionPtr));
+
nsAutoCString path;
if (pathLen > 0) {
path.Assign(url + pathPos, pathLen);
} else {
path.Assign("/");
}
- SEC_HTTP_REQUEST_SESSION requestSessionPtr;
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("Setting up OCSP request: pre all path =%s pathlen=%d\n", path.get(),
+ pathLen));
+ nsAutoCString method("POST");
+ if (useGET) {
+ method.Assign("GET");
+ if (!StringEndsWith(path, NS_LITERAL_CSTRING("/"))) {
+ path.Append("/");
+ }
+ nsresult rv = AppendEscapedBase64Item(encodedRequest, path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ }
+
+ SEC_HTTP_REQUEST_SESSION requestSessionPtr = nullptr;
if (nsNSSHttpInterface::createFcn(serverSession.get(), "http",
- path.BeginReading(), "POST",
+ path.get(), method.get(),
timeout, &requestSessionPtr)
!= SECSuccess) {
PR_SetError(SEC_ERROR_NO_MEMORY, 0);
return nullptr;
}
-
ScopedHTTPRequestSession requestSession(
reinterpret_cast<nsNSSHttpRequestSession*>(requestSessionPtr));
- if (nsNSSHttpInterface::setPostDataFcn(requestSession.get(),
- reinterpret_cast<char*>(encodedRequest->data), encodedRequest->len,
- "application/ocsp-request") != SECSuccess) {
- PR_SetError(SEC_ERROR_NO_MEMORY, 0);
- return nullptr;
+
+ if (!useGET) {
+ if (nsNSSHttpInterface::setPostDataFcn(requestSession.get(),
+ reinterpret_cast<char*>(encodedRequest->data), encodedRequest->len,
+ "application/ocsp-request") != SECSuccess) {
+ PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+ return nullptr;
+ }
}
uint16_t httpResponseCode;
const char* httpResponseData;
uint32_t httpResponseDataLen = 0; // 0 means any response size is acceptable
if (nsNSSHttpInterface::trySendAndReceiveFcn(requestSession.get(), nullptr,
&httpResponseCode, nullptr,
nullptr, &httpResponseData,
--- a/security/certverifier/OCSPRequestor.h
+++ b/security/certverifier/OCSPRequestor.h
@@ -2,19 +2,21 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_psm_OCSPRequestor_h
#define mozilla_psm_OCSPRequestor_h
+#include "CertVerifier.h"
#include "secmodt.h"
namespace mozilla { namespace psm {
// The memory returned is owned by the given arena.
SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
- const SECItem* encodedRequest, PRIntervalTime timeout);
+ const SECItem* encodedRequest, PRIntervalTime timeout,
+ bool useGET);
} } // namespace mozilla::psm
#endif // mozilla_psm_OCSPRequestor_h