mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 07:45:30 +00:00
Bug 1612587 - (2/2) incorporate all known potential issuing certificates when filtering client certificates r=kjacobs,jcj
When a server requests a client certificate, it can include a list of distinguished names that it considers valid issuers for client certificates (either as direct issuers or as transitive issuers). Before this patch, the platform would call CERT_FilterCertListByCANames to filter potential client certificates by this list of names. This function uses the "classic" NSS certificate path-building algorithm and thus can't make use of other certificates that gecko may know about, such as third-party intermediates and preloaded intermediates. This patch implements client certificate filtering by re-using the path building implementation provided by mozilla::pkix to determine if each certificate has an issuer with a name included in the acceptable list. These issuers include third-party intermediates, preloaded intermediates, and all certificates known to NSS. Note that this implementation does not actually verify the client certificates - no signatures are checked and no particular key usages are enforced. However, some properties are enforced, such as validity periods. Differential Revision: https://phabricator.services.mozilla.com/D68101 --HG-- rename : security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec => security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec extra : moz-landing-system : lando
This commit is contained in:
parent
7f9a18765c
commit
90d81515f7
@ -57,6 +57,14 @@ interface nsINSSComponent : nsISupports {
|
||||
*/
|
||||
Array<Array<octet> > getEnterpriseIntermediates();
|
||||
|
||||
/**
|
||||
* Test utility for adding an intermediate certificate to the current set of
|
||||
* imported enterprise intermediates, if any. Additions to the set made using
|
||||
* this function will be cleared when the value of the preference
|
||||
* "security.enterprise_roots.enabled" changes.
|
||||
*/
|
||||
void addEnterpriseIntermediate(in Array<octet> intermediateBytes);
|
||||
|
||||
/**
|
||||
* For performance reasons, the builtin roots module is loaded on a background
|
||||
* thread. When any code that depends on the builtin roots module runs, it
|
||||
|
@ -601,6 +601,31 @@ nsNSSComponent::GetEnterpriseIntermediates(
|
||||
return CommonGetEnterpriseCerts(enterpriseIntermediates, false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::AddEnterpriseIntermediate(
|
||||
const nsTArray<uint8_t>& intermediateBytes) {
|
||||
nsresult rv = BlockUntilLoadableCertsLoaded();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
EnterpriseCert intermediate;
|
||||
rv = intermediate.Init(intermediateBytes.Elements(),
|
||||
intermediateBytes.Length(), false);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock nsNSSComponentLock(mMutex);
|
||||
if (!mEnterpriseCerts.append(std::move(intermediate))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCertVerifierWithEnterpriseRoots();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class LoadLoadableCertsTask final : public Runnable {
|
||||
public:
|
||||
LoadLoadableCertsTask(nsNSSComponent* nssComponent,
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "SSLServerCertVerification.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedSSLState.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "cert_storage/src/cert_storage.h"
|
||||
#endif
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Casting.h"
|
||||
@ -26,6 +29,7 @@
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozpkix/pkixnss.h"
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "mozpkix/pkixutil.h"
|
||||
#include "nsArray.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsCRT.h"
|
||||
@ -49,7 +53,6 @@
|
||||
#include "sslexp.h"
|
||||
#include "sslproto.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
//#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
|
||||
@ -1728,10 +1731,10 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
|
||||
public:
|
||||
ClientAuthDataRunnable(nsNSSSocketInfo* info,
|
||||
const UniqueCERTCertificate& serverCert,
|
||||
nsTArray<nsCString>& caNamesStrings)
|
||||
nsTArray<nsTArray<uint8_t>>& collectedCANames)
|
||||
: mSocketInfo(info),
|
||||
mServerCert(serverCert.get()),
|
||||
mCANamesStrings(std::move(caNamesStrings)),
|
||||
mCollectedCANames(std::move(collectedCANames)),
|
||||
mSelectedCertificate(nullptr),
|
||||
mSelectedKey(nullptr) {}
|
||||
|
||||
@ -1751,40 +1754,25 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
|
||||
private:
|
||||
nsNSSSocketInfo* const mSocketInfo;
|
||||
CERTCertificate* const mServerCert;
|
||||
nsTArray<nsCString> mCANamesStrings;
|
||||
nsTArray<nsTArray<uint8_t>> mCollectedCANames;
|
||||
UniqueCERTCertificate mSelectedCertificate;
|
||||
UniqueSECKEYPrivateKey mSelectedKey;
|
||||
};
|
||||
|
||||
nsTArray<nsCString> DecodeCANames(CERTDistNames* caNames) {
|
||||
nsTArray<nsTArray<uint8_t>> CollectCANames(CERTDistNames* caNames) {
|
||||
MOZ_ASSERT(caNames);
|
||||
|
||||
nsTArray<nsCString> caNamesStrings;
|
||||
nsTArray<nsTArray<uint8_t>> collectedCANames;
|
||||
if (!caNames) {
|
||||
return caNamesStrings;
|
||||
return collectedCANames;
|
||||
}
|
||||
|
||||
for (int i = 0; i < caNames->nnames; i++) {
|
||||
char* caName = CERT_DerNameToAscii(&caNames->names[i]);
|
||||
// Ignore failures
|
||||
if (caName) {
|
||||
caNamesStrings.AppendElement(nsCString(caName));
|
||||
PORT_Free(caName); // CERT_DerNameToAscii uses PORT_Alloc
|
||||
}
|
||||
nsTArray<uint8_t> caName;
|
||||
caName.AppendElements(caNames->names[i].data, caNames->names[i].len);
|
||||
collectedCANames.AppendElement(std::move(caName));
|
||||
}
|
||||
return caNamesStrings;
|
||||
}
|
||||
|
||||
nsTArray<char*> GetDependentStringPointers(nsTArray<nsCString>& strings) {
|
||||
nsTArray<char*> pointers;
|
||||
for (auto& string : strings) {
|
||||
// We're not actually writing to the string. The API we're going to use this
|
||||
// data with takes an array of non-const char pointers. To avoid the
|
||||
// undefined behavior of reinterpreting a const pointer as a non-const
|
||||
// pointer, everything we do here has to be non-const.
|
||||
pointers.AppendElement(string.BeginWriting());
|
||||
}
|
||||
return pointers;
|
||||
return collectedCANames;
|
||||
}
|
||||
|
||||
// This callback function is used to pull client certificate
|
||||
@ -1842,10 +1830,10 @@ SECStatus nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> caNamesStrings(DecodeCANames(caNames));
|
||||
nsTArray<nsTArray<uint8_t>> collectedCANames(CollectCANames(caNames));
|
||||
// XXX: This should be done asynchronously; see bug 696976
|
||||
RefPtr<ClientAuthDataRunnable> runnable(
|
||||
new ClientAuthDataRunnable(info, serverCert, caNamesStrings));
|
||||
new ClientAuthDataRunnable(info, serverCert, collectedCANames));
|
||||
nsresult rv = runnable->DispatchToMainThreadAndWait();
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_SetError(SEC_ERROR_NO_MEMORY, 0);
|
||||
@ -1868,6 +1856,214 @@ SECStatus nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
// This TrustDomain only exists to facilitate the mozilla::pkix path building
|
||||
// algorithm. It considers any certificate with an issuer distinguished name in
|
||||
// the set of given CA names to be a trust anchor. It does essentially no
|
||||
// validation or verification (in particular, the signature checking function
|
||||
// always returns "Success").
|
||||
class ClientAuthCertNonverifyingTrustDomain final : public TrustDomain {
|
||||
public:
|
||||
ClientAuthCertNonverifyingTrustDomain(
|
||||
nsTArray<nsTArray<uint8_t>>& collectedCANames,
|
||||
nsTArray<nsTArray<uint8_t>> thirdPartyIntermediates)
|
||||
: mCollectedCANames(collectedCANames),
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
|
||||
#endif
|
||||
mThirdPartyIntermediates(std::move(thirdPartyIntermediates)) {
|
||||
}
|
||||
|
||||
virtual mozilla::pkix::Result GetCertTrust(
|
||||
EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
|
||||
Input candidateCertDER,
|
||||
/*out*/ TrustLevel& trustLevel) override;
|
||||
virtual mozilla::pkix::Result FindIssuer(Input encodedIssuerName,
|
||||
IssuerChecker& checker,
|
||||
Time time) override;
|
||||
|
||||
virtual mozilla::pkix::Result CheckRevocation(
|
||||
EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
|
||||
Time validityPeriodBeginning, Duration validityDuration,
|
||||
/*optional*/ const Input* stapledOCSPresponse,
|
||||
/*optional*/ const Input* aiaExtension) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result IsChainValid(
|
||||
const DERArray& certChain, Time time,
|
||||
const CertPolicyId& requiredPolicy) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
|
||||
DigestAlgorithm digestAlg, EndEntityOrCA endEntityOrCA,
|
||||
Time notBefore) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
|
||||
EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result VerifyRSAPKCS1SignedDigest(
|
||||
const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
|
||||
EndEntityOrCA endEntityOrCA, NamedCurve curve) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result VerifyECDSASignedDigest(
|
||||
const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result CheckValidityIsAcceptable(
|
||||
Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
|
||||
KeyPurposeId keyPurpose) override {
|
||||
return Success;
|
||||
}
|
||||
virtual mozilla::pkix::Result NetscapeStepUpMatchesServerAuth(
|
||||
Time notBefore,
|
||||
/*out*/ bool& matches) override {
|
||||
matches = true;
|
||||
return Success;
|
||||
}
|
||||
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
|
||||
Input extensionData) override {}
|
||||
virtual mozilla::pkix::Result DigestBuf(Input item, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf,
|
||||
size_t digestBufLen) override {
|
||||
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
|
||||
}
|
||||
|
||||
private:
|
||||
nsTArray<nsTArray<uint8_t>>& mCollectedCANames; // non-owning
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsCOMPtr<nsICertStorage> mCertStorage;
|
||||
#endif
|
||||
nsTArray<nsTArray<uint8_t>> mThirdPartyIntermediates;
|
||||
};
|
||||
|
||||
mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::GetCertTrust(
|
||||
EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
|
||||
Input candidateCertDER,
|
||||
/*out*/ TrustLevel& trustLevel) {
|
||||
// If the server did not specify any CA names, all client certificates are
|
||||
// acceptable.
|
||||
if (mCollectedCANames.Length() == 0) {
|
||||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
BackCert cert(candidateCertDER, endEntityOrCA, nullptr);
|
||||
mozilla::pkix::Result rv = cert.Init();
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
// If this certificate's issuer distinguished name is in the set of acceptable
|
||||
// CA names, we say this is a trust anchor so that the client certificate
|
||||
// issued from this certificate will be presented as an option for the user.
|
||||
Input issuer(cert.GetIssuer());
|
||||
for (const auto& caName : mCollectedCANames) {
|
||||
Input caNameInput;
|
||||
rv = caNameInput.Init(caName.Elements(), caName.Length());
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
if (InputsAreEqual(issuer, caNameInput)) {
|
||||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
trustLevel = TrustLevel::InheritsTrust;
|
||||
return Success;
|
||||
}
|
||||
|
||||
mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::FindIssuer(
|
||||
Input encodedIssuerName, IssuerChecker& checker, Time time) {
|
||||
// First try all relevant certificates known to Gecko, which avoids calling
|
||||
// CERT_CreateSubjectCertList, because that can be expensive.
|
||||
Vector<Input> geckoIntermediateCandidates;
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
if (!mCertStorage) {
|
||||
return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsTArray<uint8_t> subject;
|
||||
subject.AppendElements(encodedIssuerName.UnsafeGetData(),
|
||||
encodedIssuerName.GetLength());
|
||||
nsTArray<nsTArray<uint8_t>> certs;
|
||||
nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
|
||||
if (NS_FAILED(rv)) {
|
||||
return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
for (auto& cert : certs) {
|
||||
Input certDER;
|
||||
mozilla::pkix::Result rv = certDER.Init(cert.Elements(), cert.Length());
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
// Currently we're only expecting intermediate certificates in cert storage.
|
||||
if (!geckoIntermediateCandidates.append(certDER)) {
|
||||
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (const auto& thirdPartyIntermediate : mThirdPartyIntermediates) {
|
||||
Input thirdPartyIntermediateInput;
|
||||
mozilla::pkix::Result rv = thirdPartyIntermediateInput.Init(
|
||||
thirdPartyIntermediate.Elements(), thirdPartyIntermediate.Length());
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
if (!geckoIntermediateCandidates.append(thirdPartyIntermediateInput)) {
|
||||
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
bool keepGoing = true;
|
||||
for (Input candidate : geckoIntermediateCandidates) {
|
||||
mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!keepGoing) {
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
|
||||
// NSS seems not to differentiate between "no potential issuers found" and
|
||||
// "there was an error trying to retrieve the potential issuers." We assume
|
||||
// there was no error if CERT_CreateSubjectCertList returns nullptr.
|
||||
UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
|
||||
nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false));
|
||||
Vector<Input> nssIntermediateCandidates;
|
||||
if (candidates) {
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
|
||||
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
|
||||
Input certDER;
|
||||
mozilla::pkix::Result rv =
|
||||
certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
if (!n->cert->isRoot) {
|
||||
if (!nssIntermediateCandidates.append(certDER)) {
|
||||
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Input candidate : nssIntermediateCandidates) {
|
||||
mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!keepGoing) {
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
void ClientAuthDataRunnable::RunOnTargetThread() {
|
||||
// We check the value of a pref in this runnable, so this runnable should only
|
||||
// be run on the main thread.
|
||||
@ -1897,14 +2093,50 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<char*> caNamesStringPointers(
|
||||
GetDependentStringPointers(mCANamesStrings));
|
||||
SECStatus srv = CERT_FilterCertListByCANames(
|
||||
certList.get(), caNamesStringPointers.Length(),
|
||||
caNamesStringPointers.Elements(), certUsageSSLClient);
|
||||
if (NS_WARN_IF(srv != SECSuccess)) {
|
||||
nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
if (NS_WARN_IF(!component)) {
|
||||
return;
|
||||
}
|
||||
nsTArray<nsTArray<uint8_t>> enterpriseIntermediates;
|
||||
nsresult rv = component->GetEnterpriseIntermediates(enterpriseIntermediates);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
ClientAuthCertNonverifyingTrustDomain trustDomain(
|
||||
mCollectedCANames, std::move(enterpriseIntermediates));
|
||||
CERTCertListNode* n = CERT_LIST_HEAD(certList);
|
||||
while (!CERT_LIST_END(n, certList)) {
|
||||
Input certDER;
|
||||
mozilla::pkix::Result result =
|
||||
certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
if (result != Success) {
|
||||
CERTCertListNode* toRemove = n;
|
||||
n = CERT_LIST_NEXT(n);
|
||||
CERT_RemoveCertListNode(toRemove);
|
||||
continue; // probably too big
|
||||
}
|
||||
mozilla::pkix::Result eeResult = BuildCertChain(
|
||||
trustDomain, certDER, Now(), EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::noParticularKeyUsageRequired,
|
||||
KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr);
|
||||
mozilla::pkix::Result caResult = BuildCertChain(
|
||||
trustDomain, certDER, Now(), EndEntityOrCA::MustBeCA,
|
||||
KeyUsage::noParticularKeyUsageRequired,
|
||||
KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr);
|
||||
if (eeResult != Success && caResult != Success) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("client cert non-validation returned %d %d\n",
|
||||
static_cast<int>(eeResult), static_cast<int>(caResult)));
|
||||
CERTCertListNode* toRemove = n;
|
||||
n = CERT_LIST_NEXT(n);
|
||||
CERT_RemoveCertListNode(toRemove);
|
||||
continue;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("keeping cert '%s'\n", n->cert->subjectName));
|
||||
n = CERT_LIST_NEXT(n);
|
||||
}
|
||||
|
||||
if (CERT_LIST_EMPTY(certList)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("no client certificates available after filtering by CA"));
|
||||
@ -2019,9 +2251,8 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
||||
|
||||
// Throw up the client auth dialog and get back the index of the selected
|
||||
// cert
|
||||
nsresult rv =
|
||||
getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsIClientAuthDialogs),
|
||||
NS_CLIENTAUTHDIALOGS_CONTRACTID);
|
||||
rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsIClientAuthDialogs),
|
||||
NS_CLIENTAUTHDIALOGS_CONTRACTID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ support-files =
|
||||
[browser_certViewer.js]
|
||||
skip-if = verify
|
||||
[browser_clientAuth_connection.js]
|
||||
# Any test that has to delete certificates (e.g. as part of cleanup) is
|
||||
# fundamentally incompatible with verify due to how NSS handles deleting
|
||||
# certificates.
|
||||
skip-if = verify
|
||||
[browser_clientAuth_ui.js]
|
||||
[browser_deleteCert_ui.js]
|
||||
[browser_downloadCert_ui.js]
|
||||
|
@ -20,7 +20,9 @@ const DialogState = {
|
||||
RETURN_CERT_NOT_SELECTED: "RETURN_CERT_NOT_SELECTED",
|
||||
};
|
||||
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
|
||||
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
|
||||
|
||||
var gExpectedClientCertificateChoices;
|
||||
|
||||
// Mock implementation of nsIClientAuthDialogs.
|
||||
const gClientAuthDialogs = {
|
||||
@ -93,7 +95,11 @@ const gClientAuthDialogs = {
|
||||
// selectable as well as one of the PGO certs we loaded in `setup`, so we do
|
||||
// some brief checks to confirm this.
|
||||
Assert.notEqual(certList, null, "Cert list should not be null");
|
||||
Assert.equal(certList.length, 2, "2 certificate should be available");
|
||||
Assert.equal(
|
||||
certList.length,
|
||||
gExpectedClientCertificateChoices,
|
||||
`${gExpectedClientCertificateChoices} certificates should be available`
|
||||
);
|
||||
|
||||
for (let cert of certList.enumerate(Ci.nsIX509Cert)) {
|
||||
Assert.notEqual(cert, null, "Cert list should contain nsIX509Certs");
|
||||
@ -129,6 +135,16 @@ add_task(async function setup() {
|
||||
// This CA has all keyUsages. For compatibility with preexisting behavior, it
|
||||
// will be presented for use as a client certificate.
|
||||
await readCertificate("pgo-ca-all-usages.pem", "CTu,CTu,CTu");
|
||||
// This client certificate was issued by an intermediate that was issued by
|
||||
// the test CA. The server only lists the test CA's subject distinguished name
|
||||
// as an acceptible issuer name for client certificates. If the implementation
|
||||
// can determine that the test CA is a root CA for the client certificate and
|
||||
// thus is acceptible to use, it should be included in the chooseCertificate
|
||||
// callback. At the beginning of this test (speaking of this file as a whole),
|
||||
// the client is not aware of the intermediate, and so it is not available in
|
||||
// the callback.
|
||||
await readCertificate("client-cert-via-intermediate.pem", ",,");
|
||||
gExpectedClientCertificateChoices = 2;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -165,7 +181,7 @@ async function testHelper(prefValue, expectedURL, options = undefined) {
|
||||
let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
|
||||
Assert.ok(
|
||||
loadedURL.startsWith(expectedURL),
|
||||
"Expected and actual URLs should match"
|
||||
`Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
|
||||
);
|
||||
Assert.equal(
|
||||
gClientAuthDialogs.chooseCertificateCalled,
|
||||
@ -236,3 +252,36 @@ add_task(async function testClearPrivateBrowsingState() {
|
||||
// when the last private window closes).
|
||||
sdr.logoutAndTeardown();
|
||||
});
|
||||
|
||||
// Test that 3rd party certificates are taken into account when filtering client
|
||||
// certificates based on the acceptible CA list sent by the server.
|
||||
add_task(async function testCertFilteringWithIntermediate() {
|
||||
let intermediateBytes = await OS.File.read(
|
||||
getTestFilePath("intermediate.pem")
|
||||
).then(
|
||||
data => {
|
||||
let decoder = new TextDecoder();
|
||||
let pem = decoder.decode(data);
|
||||
let base64 = pemToBase64(pem);
|
||||
let bin = atob(base64);
|
||||
let bytes = [];
|
||||
for (let i = 0; i < bin.length; i++) {
|
||||
bytes.push(bin.charCodeAt(i));
|
||||
}
|
||||
return bytes;
|
||||
},
|
||||
error => {
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
let nssComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsINSSComponent);
|
||||
nssComponent.addEnterpriseIntermediate(intermediateBytes);
|
||||
gExpectedClientCertificateChoices = 3;
|
||||
gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
|
||||
await testHelper("Ask Every Time", "https://requireclientcert.example.com/");
|
||||
sdr.logoutAndTeardown();
|
||||
// This will reset the added intermediate.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["security.enterprise_roots.enabled", true]],
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIUZ/wVsZBVUjlx32bqA0sXgHFnIU8wDQYJKoZIhvcNAQEL
|
||||
BQAwQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
|
||||
MBMGA1UECwwMSW50ZXJtZWRpYXRlMCIYDzIwMTgxMTI3MDAwMDAwWhgPMjAyMTAy
|
||||
MDQwMDAwMDBaMCcxJTAjBgNVBAMMHGNsaWVudCBjZXJ0IHZpYSBpbnRlcm1lZGlh
|
||||
dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
|
||||
BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
|
||||
p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
|
||||
7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
|
||||
kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
|
||||
aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
|
||||
Ne2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQA9RUkBQgMo9f6cUsGbOmA+wEQeCvGBaLgdlIfRpCcjm7cXGbwi8U072bo+
|
||||
fYw1Q9mKlN/S/RXCaixalxo2A4lVHJshDfM+SJ5wcY2OVKa2OxY3N/WMI3DlrfKK
|
||||
FM6Sj2MHNvKAdTwyXZodSl7VOF0tdoYXL7x2Ac5Vfl0VMMxbBM1YrWUYPsZRc0qN
|
||||
qSDQoB4CHB8cPxhGXx5rHgK9Pi3H4ZkjVNr0ZD9Tt1wso+G84FiIwQ/GKEIEtZ7B
|
||||
VOm+rjvAXivPNx3neAvIVNIG2rfLTKbbCURvBytJ/PWuzDdN8JWxDA1wqEpxC5Wh
|
||||
dMEsPmw3ChdQpqNN858qUSjKHWQf
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,3 @@
|
||||
issuer:/CN=Temporary Certificate Authority/OU=Intermediate
|
||||
subject:client cert via intermediate
|
||||
extension:extKeyUsage:clientAuth
|
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWjCCAkKgAwIBAgIUe9ala4zEws3CecubLRMUez2xnVowDQYJKoZIhvcNAQEL
|
||||
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
|
||||
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
|
||||
MFowQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
|
||||
MBMGA1UECwwMSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
|
||||
BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
|
||||
a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
|
||||
uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
|
||||
9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
|
||||
mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsG
|
||||
A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAJ3KXbElUMtO7THRTY8wKYzit
|
||||
cEuA1lwhO7qR7AkAjJIRcb+9qgT0gU8MesLxY1E6m5bpgWZB/pRRSaj702ahIlo/
|
||||
2cLy9rahM/dU7u792m+KT7Phd5NOAPQIEuIJZzu/ux5OFZbsCE67xKJXEQ56pxwJ
|
||||
H4fmsWaoDR8TtDBRONLsXVFy3gpfjhXOcdPEer9qmRz17pBRsB1M1ft3V4qnPJA5
|
||||
VoxlNucTsdK2fwy/KYuuujnPGEI4ERcjElOoSsxVizFlf/LrFe3ibVVxfpGKMDvO
|
||||
fsrffq2XhA2DnLYOlefJwiYS0Sz8thQN66cvdu489LMX0Lox8zFJgCvUJW01aA==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,4 @@
|
||||
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
subject:/CN=Temporary Certificate Authority/OU=Intermediate
|
||||
extension:basicConstraints:cA,
|
||||
extension:keyUsage:keyCertSign,cRLSign
|
@ -13,6 +13,7 @@ BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
# when they're automatically generated.)
|
||||
#test_certificates = (
|
||||
# 'ca.pem',
|
||||
# 'client-cert-via-intermediate.pem',
|
||||
# 'code-ee.pem',
|
||||
# 'ee-from-expired-ca.pem',
|
||||
# 'ee-from-untrusted-ca.pem',
|
||||
@ -23,6 +24,7 @@ BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
# 'has-non-empty-subject.pem',
|
||||
# 'has-o.pem',
|
||||
# 'has-ou.pem',
|
||||
# 'intermediate.pem',
|
||||
# 'invalid.pem',
|
||||
# 'longOID.pem',
|
||||
# 'md5-ee.pem',
|
||||
|
@ -1,10 +1,10 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDgzCCAmugAwIBAgIUOZ+zojIZfz27KhBgkhPh1KNJKbYwDQYJKoZIhvcNAQEL
|
||||
BQAwajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
|
||||
MIIDgzCCAmugAwIBAgIUDQg2Gy5G9+g/H9GCUkYVM1qnf8IwDQYJKoZIhvcNAQEL
|
||||
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
|
||||
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
|
||||
MFowajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
|
||||
MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
|
||||
ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
|
||||
iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
|
||||
4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
|
||||
@ -12,10 +12,10 @@ iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
|
||||
Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
|
||||
77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
|
||||
I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
|
||||
AgH+MA0GCSqGSIb3DQEBCwUAA4IBAQC0MDDRTjbHbKLpm6LLLMQ7odjuq3q/JZBr
|
||||
YmjWFTd84v0yUUzmwYNCSwq1X69mYkopOaz2xPVNc7maj0jeJY0QvWT8q4pIMbaX
|
||||
9lRAJBWY0WNXATJqZrdgc2JGfwHIIFRn1V+AUFX3mGv23ev2G5RHxepbh288Kaxu
|
||||
kHtX9A2xDPGV4oFo/BhfRjImA2WHfqkY9Wcpm9BBD8SesZ9TSOLvyZT+8dVBeO0z
|
||||
Rv6IPiV4Wc2Pg2AiZqTPDH9jZsKezxURUorKWh9eatmpIVpQAsKueXXBZfwKpZo5
|
||||
9SodlHNv4QL1Nxdu+yIcL6kgeClKLRclBGaj7FF7d6228CB/G6Dk
|
||||
AgH+MA0GCSqGSIb3DQEBCwUAA4IBAQBKtiqhwcyJShE77Uekj13k1483kc5hyNPN
|
||||
z3nDXWGA03M0IBYB6z5Fec/EuZp853N/MN1mnmHxWDsFH5C13a2aratr6bMrxaRf
|
||||
a+c2lvM023sulllKIYpLJSA67h7+qbbNv0hgQzyyE3Rjdjngh8b6ZCjm/D0GO/u6
|
||||
T8O+pzbekAWidE8TYPVP36cFzfzKxYTm7O5J2t9ilqFFw4NNHaQQt1JP+yk4B0MJ
|
||||
Gp+VBRDMMBTIZzR9AO1w85HMa/WwDs7Jj1vuxz0vjMhVfBMlgAzc5lum3Zde9mlp
|
||||
sAwLvmgPnNKL+bogu7YcE/xee9cM/lOW1AfiJlszssozcLKLz6/7
|
||||
-----END CERTIFICATE-----
|
||||
|
@ -1,4 +1,4 @@
|
||||
issuer:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
subject:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
extension:basicConstraints:cA,
|
||||
extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
|
||||
|
@ -1,10 +1,10 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDgzCCAmugAwIBAgIUacGJyguVOrRdJ0sThPQuT28Rb+0wDQYJKoZIhvcNAQEL
|
||||
BQAwajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
|
||||
MIIDgzCCAmugAwIBAgIUM3Lmh+4Pw8Pfiu1leYvuN3iWevEwDQYJKoZIhvcNAQEL
|
||||
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
|
||||
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
|
||||
MFowajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
|
||||
MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
|
||||
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
|
||||
ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
|
||||
iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
|
||||
4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
|
||||
@ -12,10 +12,10 @@ iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
|
||||
Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
|
||||
77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
|
||||
I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
|
||||
AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQB1BJFL+ylvUUeKwIK/CLHjIi91qV7eQ4sw
|
||||
U4puUjrrMep8DqHEhISSHOo6JREr82JWsQ82+qjiNlZCcSv28twEYqdkCb067tep
|
||||
fmgQ+/jGVNfFAzI+m9RDbad54O7i6tI4+dYoq7RoZkFiyTAAIWZ0ic9vifryABsa
|
||||
SvmdQ5loxdhdSY24VDE/emnvBXYknWk7+QbxauoH0bPTsJvuuoutorGxDPN3s+cv
|
||||
6LkX8Dlgg8wemSOQY1ZP1eY9jWEvVCKoDMeLKOcuuzeUryLjs/KOR1h1VEFTm97c
|
||||
L01TfBaLzVX3vFAQjmriF4Bd5/FM0jCcjFSVDVZ4GmzVX7XUVhyd
|
||||
AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBzEAci/+VfRVPmd0Gc0g+1iwf2lGeGaN4o
|
||||
cvqKQQHdh6D0dqOFqXYeNsOELWKwgIRx8OX1MlvRQMH58ZB8bY1a+drIPALkIuWv
|
||||
ORWVLdNscJ+IT3wsscCWP6t3O64bBLbTonW1odcdVxez1uHBvRYZXYSc5U/D55PE
|
||||
Tf+ZU2yy6BzlJ2IftwS4M3Virny+SEE0lcBwP0m0Vz7+08K7apW2Yh/38VLukd6a
|
||||
Kv7JkcgoK/IeZXazlmG0Bxs+IpWlF239HZ4EvpZIxQBbcS68iNoqqL6OB9yycLay
|
||||
KzjejGBHyKXvtUKSEIOt84KFoEKio/cdYlv3xlfQ/LSe0AVdlJR5
|
||||
-----END CERTIFICATE-----
|
||||
|
@ -1,4 +1,4 @@
|
||||
issuer:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
subject:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
|
||||
extension:basicConstraints:cA,
|
||||
extension:keyUsage:keyCertSign,cRLSign
|
||||
|
Loading…
Reference in New Issue
Block a user