bug 1009161 - mozilla::pkix: allow the Netscape certificate type extension if more standardized information is present r=briansmith

This commit is contained in:
David Keeler 2014-08-25 09:25:36 -07:00
parent c6070203fb
commit 0eb37ae230
11 changed files with 120 additions and 17 deletions

View File

@ -0,0 +1,27 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
// 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/.
//
// While the Netscape certificate type extension is not a standard and has been
// discouraged from use for quite some time, it is still encountered. Thus, we
// handle it slightly differently from other unknown extensions.
// If it is not marked critical, we ignore it.
// If it is marked critical:
// If the basic constraints and extended key usage extensions are also
// present, we ignore it, because they are standardized and should convey the
// same information.
// Otherwise, we reject it with an error indicating an unknown critical
// extension.
"use strict";
function run_test() {
do_get_profile();
add_tls_server_setup("BadCertServer");
add_connection_test("nsCertTypeNotCritical.example.com", Cr.NS_OK);
add_connection_test("nsCertTypeCriticalWithExtKeyUsage.example.com", Cr.NS_OK);
add_connection_test("nsCertTypeCritical.example.com",
getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION));
run_next_test();
}

View File

@ -53,6 +53,9 @@ const BadCertHost sBadCertHosts[] =
{ "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" },
{ "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" },
{ "test-mode.pinning.example.com", "otherIssuerEE" },
{ "nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical" },
{ "nsCertTypeCriticalWithExtKeyUsage.example.com", "nsCertTypeCriticalWithExtKeyUsage" },
{ "nsCertTypeCritical.example.com", "nsCertTypeCritical" },
{ nullptr, nullptr }
};

View File

@ -172,6 +172,37 @@ function make_EE {
SERIALNO=$(($SERIALNO + 1))
}
function make_EE_with_nsCertType {
NICKNAME="${1}"
SUBJECT="${2}"
CA="${3}"
SUBJECT_ALT_NAME="${4}"
NS_CERT_TYPE_CRITICAL="${5}"
EXTRA_ARGS="${6}"
# This adds the Netscape certificate type extension with the "sslServer"
# bit asserted. Its criticality depends on if "y" or "n" was passed as
# an argument to this function.
CERT_RESPONSES="n\n\ny\n1\n8\n$NS_CERT_TYPE_CRITICAL\n"
cert_already_exists $NICKNAME
if [ $ALREADY_EXISTS -eq 1 ]; then
echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
return
fi
echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-n $NICKNAME \
-s "$SUBJECT" \
-8 $SUBJECT_ALT_NAME \
-c $CA \
-t ",," \
-m $SERIALNO \
-5 \
$COMMON_ARGS \
$EXTRA_ARGS
SERIALNO=$(($SERIALNO + 1))
}
function make_delegated {
CERT_RESPONSES="n\n\ny\n"
NICKNAME="${1}"
@ -251,4 +282,8 @@ make_INT self-signed-EE-with-cA-true 'CN=Test Self-signed End-entity with CA tru
make_delegated badKeysizeDelegatedSigner 'CN=Bad Keysize Delegated Responder' testCA "--extKeyUsage ocspResponder -g 1008"
make_EE_with_nsCertType nsCertTypeCritical 'CN=nsCertType Critical' testCA "localhost,*.example.com" "y"
make_EE_with_nsCertType nsCertTypeNotCritical 'CN=nsCertType Not Critical' testCA "localhost,*.example.com" "n"
make_EE_with_nsCertType nsCertTypeCriticalWithExtKeyUsage 'CN=nsCertType Critical With extKeyUsage' testCA "localhost,*.example.com" "y" "--extKeyUsage serverAuth"
cleanup

View File

@ -62,6 +62,7 @@ using std::placeholders::_3;
extern class Placeholder1 { } _1;
extern class Placeholder2 { } _2;
extern class Placeholder3 { } _3;
extern class Placeholder4 { } _4;
template <typename V> V& ref(V& v) { return v; }
template <typename V> const V& cref(const V& v) { return v; }
@ -113,17 +114,18 @@ private:
void operator=(const Bind4&) /*= delete*/;
};
template <typename R, typename C1, typename P1, typename P2, typename P3>
class BindToMemberFunction3
template <typename R, typename C1, typename P1, typename P2, typename P3,
typename P4>
class BindToMemberFunction4
{
public:
typedef R (C1::*F)(P1&, P2&, P3&);
BindToMemberFunction3(F f, C1* that) : f(f), that(that) { }
R operator()(P1& p1, P2& p2, P3& p3) const { return (that->*f)(p1, p2, p3); }
typedef R (C1::*F)(P1&, P2&, P3, P4&);
BindToMemberFunction4(F f, C1* that) : f(f), that(that) { }
R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const { return (that->*f)(p1, p2, p3, p4); }
private:
const F f;
C1* const that;
void operator=(const BindToMemberFunction3&) /*= delete*/;
void operator=(const BindToMemberFunction4&) /*= delete*/;
};
} // namespace internal
@ -151,12 +153,13 @@ bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2,
return internal::Bind4<R, P1, const B1, const B2, B3, B4>(f, b1, b2, b3, b4);
}
template <typename R, typename C1, typename P1, typename P2, typename P3>
inline internal::BindToMemberFunction3<R, C1, P1, P2, P3>
bind(R (C1::*f)(P1&, P2&, P3&), C1* that, Placeholder1&, Placeholder2&,
Placeholder3&)
template <typename R, typename C1, typename P1, typename P2, typename P3,
typename P4>
inline internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>
bind(R (C1::*f)(P1&, P2&, P3, P4&), C1* that, Placeholder1&, Placeholder2&,
Placeholder3, Placeholder4&)
{
return internal::BindToMemberFunction3<R, C1, P1, P2, P3>(f, that);
return internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>(f, that);
}
#endif

View File

@ -31,6 +31,7 @@ namespace mozilla { namespace pkix {
Placeholder1 _1;
Placeholder2 _2;
Placeholder3 _3;
Placeholder4 _4;
} } // namespace mozilla::pkix

View File

@ -152,10 +152,37 @@ BackCert::Init()
if (version == der::Version::v3 || version == der::Version::v4) {
rv = der::OptionalExtensions(tbsCertificate, CSC | 3,
bind(&BackCert::RememberExtension, this, _1,
_2, _3));
_2, _3, _4));
if (rv != Success) {
return rv;
}
// The Netscape Certificate Type extension is an obsolete
// Netscape-proprietary mechanism that we ignore in favor of the standard
// extensions. However, some CAs have issued certificates with the Netscape
// Cert Type extension marked critical. Thus, for compatibility reasons, we
// "understand" this extension by ignoring it when it is not critical, and
// by ensuring that the equivalent standardized extensions are present when
// it is marked critical, based on the assumption that the information in
// the Netscape Cert Type extension is consistent with the information in
// the standard extensions.
//
// Here is a mapping between the Netscape Cert Type extension and the
// standard extensions:
//
// Netscape Cert Type | BasicConstraints.cA | Extended Key Usage
// --------------------+-----------------------+----------------------
// SSL Server | false | id_kp_serverAuth
// SSL Client | false | id_kp_clientAuth
// S/MIME Client | false | id_kp_emailProtection
// Object Signing | false | id_kp_codeSigning
// SSL Server CA | true | id_pk_serverAuth
// SSL Client CA | true | id_kp_clientAuth
// S/MIME CA | true | id_kp_emailProtection
// Object Signing CA | true | id_kp_codeSigning
if (criticalNetscapeCertificateType.GetLength() > 0 &&
(basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) {
return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
}
}
return der::End(tbsCertificate);
@ -165,7 +192,7 @@ BackCert::Init()
// to limitations in our std::bind polyfill.
Result
BackCert::RememberExtension(Reader& extnID, const Input& extnValue,
/*out*/ bool& understood)
bool critical, /*out*/ bool& understood)
{
understood = false;
@ -205,6 +232,10 @@ BackCert::RememberExtension(Reader& extnID, const Input& extnValue,
static const uint8_t id_pe_authorityInfoAccess[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
// python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
static const uint8_t Netscape_certificate_type[] = {
0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
};
Input* out = nullptr;
@ -238,6 +269,8 @@ BackCert::RememberExtension(Reader& extnID, const Input& extnValue,
out = &inhibitAnyPolicy;
} else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
out = &authorityInfoAccess;
} else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
out = &criticalNetscapeCertificateType;
}
if (out) {

View File

@ -570,7 +570,7 @@ OptionalExtensions(Reader& input, uint8_t tag,
}
bool understood = false;
rv = extensionHandler(extnID, extnValue, understood);
rv = extensionHandler(extnID, extnValue, critical, understood);
if (rv != Success) {
return rv;
}

View File

@ -156,7 +156,7 @@ static inline Result ResponseData(
const DERArray& certs);
static inline Result SingleResponse(Reader& input, Context& context);
static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue,
/*out*/ bool& understood);
bool critical, /*out*/ bool& understood);
static inline Result CertID(Reader& input,
const Context& context,
/*out*/ bool& match);
@ -824,7 +824,7 @@ KeyHash(TrustDomain& trustDomain, const Input subjectPublicKeyInfo,
Result
ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/,
/*out*/ bool& understood)
bool /*critical*/, /*out*/ bool& understood)
{
understood = false;
return Success;

View File

@ -134,9 +134,10 @@ private:
Input keyUsage;
Input nameConstraints;
Input subjectAltName;
Input criticalNetscapeCertificateType;
Result RememberExtension(Reader& extnID, const Input& extnValue,
/*out*/ bool& understood);
bool critical, /*out*/ bool& understood);
BackCert(const BackCert&) /* = delete */;
void operator=(const BackCert&); /* = delete */;