summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Thomson <mt@lowentropy.net>2019-09-06 19:59:11 +1000
committerMartin Thomson <mt@lowentropy.net>2019-09-06 19:59:11 +1000
commitd2a64b472badb3dc3817305705f0031ba21ce13f (patch)
tree7e3adfda1bef316c3e7537998f9d28d99015ee19
parentc4baec4b51bd6b5d1bf3032bc97341e52958ed2f (diff)
downloadnss-hg-d2a64b472badb3dc3817305705f0031ba21ce13f.tar.gz
Bug 1549225 - Up front Signature Scheme validation, r=ueno
Summary: This patch started as an attempt to ensure that a DSA signature scheme would not be advertised if we weren't willing to negotiate versions less than TLS 1.3. Then I realized that we didn't do the same for PKCS#1 RSA. Then I realized that we were still willing to try to establish connections when we had a certificate that we couldn't use. Then I realized that ssl3_config_match_init() wasn't being run consistently. On resumption, we only ran it when we were PARANOID. That's silly because we weren't checking policies. Then I realized that we were allowing ECDSA certificates to be used when the named group in the certificate was disabled. We weren't enforcing that consistently either. However, I also discovered that the check we have wouldn't work without a tweak because in TLS 1.3 the named group is part of the signature scheme; the configured named groups are only used prior to TLS 1.3 when selecting ECDSA/ECDH certificates. So that sounds like a lot of changes but what it boils down to is more robust checking of the configuration prior to starting a connection. As a result, we should be offering fewer options that we're unwilling or unable to follow through on. A good number of tests needed tweaking as a result because we were relying on getting past the checks in those tests. No real problems were found as a result; this just moves failures that might arise from misconfiguration a little earlier in the process. Differential Revision: https://phabricator.services.mozilla.com/D45966
-rw-r--r--gtests/ssl_gtest/ssl_auth_unittest.cc120
-rw-r--r--gtests/ssl_gtest/ssl_ciphersuite_unittest.cc19
-rw-r--r--gtests/ssl_gtest/ssl_extension_unittest.cc2
-rw-r--r--gtests/ssl_gtest/ssl_fuzz_unittest.cc2
-rw-r--r--gtests/ssl_gtest/tls_esni_unittest.cc2
-rw-r--r--lib/ssl/ssl3con.c212
-rw-r--r--lib/ssl/ssl3exthandle.c11
-rw-r--r--lib/ssl/sslimpl.h3
-rw-r--r--lib/ssl/tls13con.c10
9 files changed, 303 insertions, 78 deletions
diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc
index f190484b9..c1a810d04 100644
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -783,6 +783,7 @@ TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureScheme) {
1024);
}
+// Client should refuse to connect without a usable signature scheme.
TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureSchemeOnly) {
static const SSLSignatureScheme kSignatureScheme[] = {
ssl_sig_rsa_pkcs1_sha256};
@@ -790,7 +791,21 @@ TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureSchemeOnly) {
Reset(TlsAgent::kServerRsa, "rsa");
client_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
- server_->SetSignatureSchemes(kSignatureScheme,
+ client_->SetupClientAuth();
+ client_->StartConnect();
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
+ client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+}
+
+// Though the client has a usable signature scheme, when a certificate is
+// requested, it can't produce one.
+TEST_P(TlsConnectTls13, ClientAuthPkcs1AndEcdsaScheme) {
+ static const SSLSignatureScheme kSignatureScheme[] = {
+ ssl_sig_rsa_pkcs1_sha256, ssl_sig_ecdsa_secp256r1_sha256};
+
+ Reset(TlsAgent::kServerRsa, "rsa");
+ client_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
client_->SetupClientAuth();
server_->RequestClientAuth(true);
@@ -1433,6 +1448,109 @@ TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPss) {
&ServerCertDataRsaPss));
}
+// A server should refuse to even start a handshake with
+// misconfigured certificate and signature scheme.
+TEST_P(TlsConnectTls12Plus, MisconfiguredCertScheme) {
+ Reset(TlsAgent::kServerDsa);
+ static const SSLSignatureScheme kScheme[] = {ssl_sig_ecdsa_secp256r1_sha256};
+ server_->SetSignatureSchemes(kScheme, PR_ARRAY_SIZE(kScheme));
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
+ // TLS 1.2 disables cipher suites, which leads to a different error.
+ server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+ } else {
+ server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+ }
+ client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
+// In TLS 1.2, disabling an EC group causes ECDSA to be invalid.
+TEST_P(TlsConnectTls12, Tls12CertDisabledGroup) {
+ Reset(TlsAgent::kServerEcdsa256);
+ static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519};
+ server_->ConfigNamedGroups(k25519);
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+ client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
+// In TLS 1.3, ECDSA configuration only depends on the signature scheme.
+TEST_P(TlsConnectTls13, Tls13CertDisabledGroup) {
+ Reset(TlsAgent::kServerEcdsa256);
+ static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519};
+ server_->ConfigNamedGroups(k25519);
+ Connect();
+}
+
+// A client should refuse to even start a handshake with only DSA.
+TEST_P(TlsConnectTls13, Tls13DsaOnlyClient) {
+ static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256};
+ client_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa));
+ client_->StartConnect();
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
+ client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+}
+
+TEST_P(TlsConnectTls13, Tls13DsaOnlyServer) {
+ Reset(TlsAgent::kServerDsa);
+ static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256};
+ server_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa));
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+ client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
+TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyClient) {
+ static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256};
+ client_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1));
+ client_->StartConnect();
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
+ client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+}
+
+TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyServer) {
+ static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256};
+ server_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1));
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+ client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
+TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedClient) {
+ EnsureTlsSetup();
+ static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256,
+ ssl_sig_rsa_pss_rsae_sha256};
+ client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
+ auto capture =
+ MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
+ Connect();
+ // We should only have the one signature algorithm advertised.
+ static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8,
+ ssl_sig_rsa_pss_rsae_sha256 & 0xff};
+ ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)),
+ capture->extension());
+}
+
+TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedServer) {
+ EnsureTlsSetup();
+ static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256,
+ ssl_sig_rsa_pss_rsae_sha256};
+ server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
+ auto capture = MakeTlsFilter<TlsExtensionCapture>(
+ server_, ssl_signature_algorithms_xtn, true);
+ capture->SetHandshakeTypes({kTlsHandshakeCertificateRequest});
+ capture->EnableDecryption();
+ server_->RequestClientAuth(false); // So we get a CertificateRequest.
+ Connect();
+ // We should only have the one signature algorithm advertised.
+ static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8,
+ ssl_sig_rsa_pss_rsae_sha256 & 0xff};
+ ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)),
+ capture->extension());
+}
+
// variant, version, certificate, auth type, signature scheme
typedef std::tuple<SSLProtocolVariant, uint16_t, std::string, SSLAuthType,
SSLSignatureScheme>
diff --git a/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc b/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
index 175a2d132..7739fe76f 100644
--- a/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -56,6 +56,9 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
std::vector<SSLNamedGroup> groups = {group_};
+ if (cert_group_ != ssl_grp_none) {
+ groups.push_back(cert_group_);
+ }
client_->ConfigNamedGroups(groups);
server_->ConfigNamedGroups(groups);
kea_type_ = SSLInt_GetKEAType(group_);
@@ -69,34 +72,47 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
switch (sig_scheme_) {
case ssl_sig_rsa_pss_rsae_sha256:
+ std::cerr << "Signature scheme: rsa_pss_rsae_sha256" << std::endl;
+ Reset(TlsAgent::kServerRsaSign);
+ auth_type_ = ssl_auth_rsa_sign;
+ break;
case ssl_sig_rsa_pss_rsae_sha384:
+ std::cerr << "Signature scheme: rsa_pss_rsae_sha384" << std::endl;
Reset(TlsAgent::kServerRsaSign);
auth_type_ = ssl_auth_rsa_sign;
break;
case ssl_sig_rsa_pss_rsae_sha512:
// You can't fit SHA-512 PSS in a 1024-bit key.
+ std::cerr << "Signature scheme: rsa_pss_rsae_sha512" << std::endl;
Reset(TlsAgent::kRsa2048);
auth_type_ = ssl_auth_rsa_sign;
break;
case ssl_sig_rsa_pss_pss_sha256:
+ std::cerr << "Signature scheme: rsa_pss_pss_sha256" << std::endl;
Reset(TlsAgent::kServerRsaPss);
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_rsa_pss_pss_sha384:
+ std::cerr << "Signature scheme: rsa_pss_pss_sha384" << std::endl;
Reset("rsa_pss384");
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_rsa_pss_pss_sha512:
+ std::cerr << "Signature scheme: rsa_pss_pss_sha512" << std::endl;
Reset("rsa_pss512");
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_ecdsa_secp256r1_sha256:
+ std::cerr << "Signature scheme: ecdsa_secp256r1_sha256" << std::endl;
Reset(TlsAgent::kServerEcdsa256);
auth_type_ = ssl_auth_ecdsa;
+ cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_sig_ecdsa_secp384r1_sha384:
+ std::cerr << "Signature scheme: ecdsa_secp384r1_sha384" << std::endl;
Reset(TlsAgent::kServerEcdsa384);
auth_type_ = ssl_auth_ecdsa;
+ cert_group_ = ssl_grp_ec_secp384r1;
break;
default:
ADD_FAILURE() << "Unsupported signature scheme: " << sig_scheme_;
@@ -112,9 +128,11 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
break;
case ssl_auth_ecdsa:
Reset(TlsAgent::kServerEcdsa256);
+ cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_auth_ecdh_ecdsa:
Reset(TlsAgent::kServerEcdhEcdsa);
+ cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_auth_ecdh_rsa:
Reset(TlsAgent::kServerEcdhRsa);
@@ -192,6 +210,7 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
SSLAuthType auth_type_;
SSLKEAType kea_type_;
SSLNamedGroup group_;
+ SSLNamedGroup cert_group_ = ssl_grp_none;
SSLSignatureScheme sig_scheme_;
SSLCipherSuiteInfo csinfo_;
};
diff --git a/gtests/ssl_gtest/ssl_extension_unittest.cc b/gtests/ssl_gtest/ssl_extension_unittest.cc
index 92632ba87..d7f350c8c 100644
--- a/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -652,7 +652,7 @@ TEST_P(TlsExtensionTest12, SignatureAlgorithmDisableDSA) {
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
client_->SetSignatureSchemes(schemes.data(), schemes.size());
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
- server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
// Check if no DSA algorithms are advertised.
diff --git a/gtests/ssl_gtest/ssl_fuzz_unittest.cc b/gtests/ssl_gtest/ssl_fuzz_unittest.cc
index bb78bc3d1..b222f15cb 100644
--- a/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -252,4 +252,4 @@ INSTANTIATE_TEST_CASE_P(
FuzzDatagram, TlsFuzzTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
-}
+} // namespace nss_test
diff --git a/gtests/ssl_gtest/tls_esni_unittest.cc b/gtests/ssl_gtest/tls_esni_unittest.cc
index 84becf3a6..90dccc525 100644
--- a/gtests/ssl_gtest/tls_esni_unittest.cc
+++ b/gtests/ssl_gtest/tls_esni_unittest.cc
@@ -472,4 +472,4 @@ TEST_P(TlsConnectTls13, EsniButTLS12Server) {
ASSERT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
ssl_tls13_encrypted_sni_xtn));
}
-}
+} // namespace nss_test
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index 4646c94d3..f58cff04e 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -737,7 +737,7 @@ ssl_KEAEnabled(const sslSocket *ss, SSLKEAType keaType)
}
static PRBool
-ssl_HasCert(const sslSocket *ss, SSLAuthType authType)
+ssl_HasCert(const sslSocket *ss, PRUint16 maxVersion, SSLAuthType authType)
{
PRCList *cursor;
if (authType == ssl_auth_null || authType == ssl_auth_psk || authType == ssl_auth_tls13_any) {
@@ -757,8 +757,13 @@ ssl_HasCert(const sslSocket *ss, SSLAuthType authType)
* enabled, so this will essentially do nothing (unless we implement
* curve configuration). However, once we have seen the
* supported_groups extension and this is called from config_match(),
- * this will filter out certificates with an unsupported curve. */
- if ((authType == ssl_auth_ecdsa ||
+ * this will filter out certificates with an unsupported curve.
+ *
+ * If we might negotiate TLS 1.3, skip this test as group configuration
+ * doesn't affect choices in TLS 1.3.
+ */
+ if (maxVersion < SSL_LIBRARY_VERSION_TLS_1_3 &&
+ (authType == ssl_auth_ecdsa ||
authType == ssl_auth_ecdh_ecdsa ||
authType == ssl_auth_ecdh_rsa) &&
!ssl_NamedGroupEnabled(ss, cert->namedCurve)) {
@@ -767,7 +772,114 @@ ssl_HasCert(const sslSocket *ss, SSLAuthType authType)
return PR_TRUE;
}
if (authType == ssl_auth_rsa_sign) {
- return ssl_HasCert(ss, ssl_auth_rsa_pss);
+ return ssl_HasCert(ss, maxVersion, ssl_auth_rsa_pss);
+ }
+ return PR_FALSE;
+}
+
+/* Check that a signature scheme is accepted.
+ * Both by policy and by having a token that supports it. */
+static PRBool
+ssl_SignatureSchemeAccepted(PRUint16 minVersion,
+ SSLSignatureScheme scheme)
+{
+ /* Disable RSA-PSS schemes if there are no tokens to verify them. */
+ if (ssl_IsRsaPssSignatureScheme(scheme)) {
+ if (!PK11_TokenExists(auth_alg_defs[ssl_auth_rsa_pss])) {
+ return PR_FALSE;
+ }
+ } else if (ssl_IsRsaPkcs1SignatureScheme(scheme)) {
+ /* Disable PKCS#1 signatures if we are limited to TLS 1.3. */
+ if (minVersion >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return PR_FALSE;
+ }
+ } else if (ssl_IsDsaSignatureScheme(scheme)) {
+ /* DSA: not in TLS 1.3, and check policy. */
+ if (minVersion >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return PR_FALSE;
+ }
+ PRUint32 dsaPolicy;
+ SECStatus rv = NSS_GetAlgorithmPolicy(SEC_OID_ANSIX9_DSA_SIGNATURE,
+ &dsaPolicy);
+ if (rv == SECSuccess && (dsaPolicy & NSS_USE_ALG_IN_SSL_KX) == 0) {
+ return PR_FALSE;
+ }
+ }
+
+ /* Hash policy. */
+ PRUint32 hashPolicy;
+ SSLHashType hashType = ssl_SignatureSchemeToHashType(scheme);
+ SECOidTag hashOID = ssl3_HashTypeToOID(hashType);
+ SECStatus rv = NSS_GetAlgorithmPolicy(hashOID, &hashPolicy);
+ if (rv == SECSuccess && (hashPolicy & NSS_USE_ALG_IN_SSL_KX) == 0) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+static SECStatus
+ssl_CheckSignatureSchemes(sslSocket *ss)
+{
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
+ return SECSuccess;
+ }
+
+ /* If this is a server using TLS 1.3, we just need to have one signature
+ * scheme for which we have a usable certificate.
+ *
+ * Note: Certificates for earlier TLS versions are checked along with the
+ * cipher suite in ssl3_config_match_init. */
+ if (ss->sec.isServer && ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ PRBool foundCert = PR_FALSE;
+ for (unsigned int i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
+ SSLAuthType authType =
+ ssl_SignatureSchemeToAuthType(ss->ssl3.signatureSchemes[i]);
+ if (ssl_HasCert(ss, ss->vrange.max, authType)) {
+ foundCert = PR_TRUE;
+ break;
+ }
+ }
+ if (!foundCert) {
+ PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+ return SECFailure;
+ }
+ }
+
+ /* Ensure that there is a signature scheme that can be accepted.*/
+ for (unsigned int i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
+ if (ssl_SignatureSchemeAccepted(ss->vrange.min,
+ ss->ssl3.signatureSchemes[i])) {
+ return SECSuccess;
+ }
+ }
+ PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+ return SECFailure;
+}
+
+/* For a server, check that a signature scheme that can be used with the
+ * provided authType is both enabled and usable. */
+static PRBool
+ssl_HasSignatureScheme(const sslSocket *ss, SSLAuthType authType)
+{
+ PORT_Assert(ss->sec.isServer);
+ PORT_Assert(ss->ssl3.hs.preliminaryInfo & ssl_preinfo_version);
+ PORT_Assert(authType != ssl_auth_null);
+ PORT_Assert(authType != ssl_auth_tls13_any);
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2 ||
+ authType == ssl_auth_rsa_decrypt ||
+ authType == ssl_auth_ecdh_rsa ||
+ authType == ssl_auth_ecdh_ecdsa) {
+ return PR_TRUE;
+ }
+ for (unsigned int i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
+ SSLSignatureScheme scheme = ss->ssl3.signatureSchemes[i];
+ SSLAuthType schemeAuthType = ssl_SignatureSchemeToAuthType(scheme);
+ PRBool acceptable = authType == schemeAuthType ||
+ (schemeAuthType == ssl_auth_rsa_pss &&
+ authType == ssl_auth_rsa_sign);
+ if (acceptable && ssl_SignatureSchemeAccepted(ss->version, scheme)) {
+ return PR_TRUE;
+ }
}
return PR_FALSE;
}
@@ -798,6 +910,9 @@ ssl3_config_match_init(sslSocket *ss)
if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
return 0;
}
+ if (ssl_CheckSignatureSchemes(ss) != SECSuccess) {
+ return 0; /* Code already set. */
+ }
ssl_FilterSupportedGroups(ss);
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
@@ -820,10 +935,11 @@ ssl3_config_match_init(sslSocket *ss)
authType = kea_defs[cipher_def->key_exchange_alg].authKeyType;
if (authType != ssl_auth_null && authType != ssl_auth_tls13_any) {
- if (ss->sec.isServer && !ssl_HasCert(ss, authType)) {
+ if (ss->sec.isServer &&
+ !(ssl_HasCert(ss, ss->vrange.max, authType) &&
+ ssl_HasSignatureScheme(ss, authType))) {
suite->isPresent = PR_FALSE;
- }
- if (!PK11_TokenExists(auth_alg_defs[authType])) {
+ } else if (!PK11_TokenExists(auth_alg_defs[authType])) {
suite->isPresent = PR_FALSE;
}
}
@@ -887,7 +1003,7 @@ ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
return PR_FALSE;
}
- if (ss->sec.isServer && !ssl_HasCert(ss, kea_def->authKeyType)) {
+ if (ss->sec.isServer && !ssl_HasCert(ss, vrange->max, kea_def->authKeyType)) {
return PR_FALSE;
}
@@ -4156,6 +4272,9 @@ ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid,
if (ssl_IsRsaPkcs1SignatureScheme(scheme)) {
return PR_FALSE;
}
+ if (ssl_IsDsaSignatureScheme(scheme)) {
+ return PR_FALSE;
+ }
/* With TLS 1.3, EC keys should have been selected based on calling
* ssl_SignatureSchemeFromSpki(), reject them otherwise. */
return spkiOid != SEC_OID_ANSIX962_EC_PUBLIC_KEY;
@@ -4270,6 +4389,7 @@ ssl_SignatureSchemeFromSpki(const CERTSubjectPublicKeyInfo *spki,
return SECSuccess;
}
+/* Check that a signature scheme is enabled by configuration. */
PRBool
ssl_SignatureSchemeEnabled(const sslSocket *ss, SSLSignatureScheme scheme)
{
@@ -8052,6 +8172,7 @@ ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites,
}
}
}
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
return SECFailure;
}
@@ -8076,6 +8197,14 @@ ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
PRUint16 selected;
SECStatus rv;
+ /* Ensure that only valid cipher suites are enabled. */
+ if (ssl3_config_match_init(ss) == 0) {
+ /* No configured cipher is both supported by PK11 and allowed.
+ * This is a configuration error, so report handshake failure.*/
+ FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
+ return SECFailure;
+ }
+
rv = ssl3_NegotiateCipherSuiteInner(ss, suites, ss->version, &selected);
if (rv != SECSuccess) {
return SECFailure;
@@ -8205,13 +8334,6 @@ ssl3_ServerCallSNICallback(sslSocket *ss)
ret = SSL_SNI_SEND_ALERT;
break;
}
- if (ssl3_config_match_init(ss) == 0) {
- /* no ciphers are working/supported */
- errCode = PORT_GetError();
- desc = handshake_failure;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
/* Need to tell the client that application has picked
* the name from the offered list and reconfigured the socket.
* Don't do this if we negotiated ESNI.
@@ -8845,9 +8967,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
if (sid)
do {
ssl3CipherSuiteCfg *suite;
-#ifdef PARANOID
SSLVersionRange vrange = { ss->version, ss->version };
-#endif
suite = ss->cipherSuites;
/* Find the entry for the cipher suite used in the cached session. */
@@ -8858,18 +8978,18 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
PORT_Assert(j > 0);
if (j == 0)
break;
-#ifdef PARANOID
+
/* Double check that the cached cipher suite is still enabled,
* implemented, and allowed by policy. Might have been disabled.
- * The product policy won't change during the process lifetime.
- * Implemented ("isPresent") shouldn't change for servers.
*/
+ if (ssl3_config_match_init(ss) == 0) {
+ desc = handshake_failure;
+ errCode = PORT_GetError();
+ goto alert_loser;
+ }
if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss))
break;
-#else
- if (!suite->enabled)
- break;
-#endif
+
/* Double check that the cached cipher suite is in the client's
* list. If it isn't, fall through and start a new session. */
for (i = 0; i + 1 < suites->len; i += 2) {
@@ -8887,21 +9007,12 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
}
}
} while (0);
-/* START A NEW SESSION */
-
-#ifndef PARANOID
- /* Look for a matching cipher suite. */
- if (ssl3_config_match_init(ss) == 0) {
- desc = internal_error;
- errCode = PORT_GetError(); /* error code is already set. */
- goto alert_loser;
- }
-#endif
+ /* START A NEW SESSION */
rv = ssl3_NegotiateCipherSuite(ss, suites, PR_TRUE);
if (rv != SECSuccess) {
desc = handshake_failure;
- errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
+ errCode = PORT_GetError();
goto alert_loser;
}
@@ -9669,10 +9780,9 @@ ssl3_SendServerKeyExchange(sslSocket *ss)
}
SECStatus
-ssl3_EncodeSigAlgs(const sslSocket *ss, sslBuffer *buf)
+ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint16 minVersion, sslBuffer *buf)
{
unsigned int lengthOffset;
- unsigned int i;
PRBool found = PR_FALSE;
SECStatus rv;
@@ -9681,33 +9791,13 @@ ssl3_EncodeSigAlgs(const sslSocket *ss, sslBuffer *buf)
return SECFailure;
}
- for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
- PRUint32 policy = 0;
- SSLHashType hashType = ssl_SignatureSchemeToHashType(
- ss->ssl3.signatureSchemes[i]);
- SECOidTag hashOID = ssl3_HashTypeToOID(hashType);
-
- /* Skip RSA-PSS schemes if there are no tokens to verify them. */
- if (ssl_IsRsaPssSignatureScheme(ss->ssl3.signatureSchemes[i]) &&
- !PK11_TokenExists(auth_alg_defs[ssl_auth_rsa_pss])) {
- continue;
- }
-
- /* Skip DSA scheme if it is disabled by policy. */
- if (ssl_IsDsaSignatureScheme(ss->ssl3.signatureSchemes[i]) &&
- (NSS_GetAlgorithmPolicy(SEC_OID_ANSIX9_DSA_SIGNATURE, &policy) ==
- SECSuccess) &&
- !(policy & NSS_USE_ALG_IN_SSL_KX)) {
- continue;
- }
-
- if ((NSS_GetAlgorithmPolicy(hashOID, &policy) != SECSuccess) ||
- (policy & NSS_USE_ALG_IN_SSL_KX)) {
+ for (unsigned int i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
+ if (ssl_SignatureSchemeAccepted(minVersion,
+ ss->ssl3.signatureSchemes[i])) {
rv = sslBuffer_AppendNumber(buf, ss->ssl3.signatureSchemes[i], 2);
if (rv != SECSuccess) {
return SECFailure;
}
-
found = PR_TRUE;
}
}
@@ -9753,7 +9843,7 @@ ssl3_SendCertificateRequest(sslSocket *ss)
length = 1 + certTypesLength + 2 + calen;
if (isTLS12) {
- rv = ssl3_EncodeSigAlgs(ss, &sigAlgsBuf);
+ rv = ssl3_EncodeSigAlgs(ss, ss->version, &sigAlgsBuf);
if (rv != SECSuccess) {
return rv;
}
diff --git a/lib/ssl/ssl3exthandle.c b/lib/ssl/ssl3exthandle.c
index f4b50f8a7..206cb00e4 100644
--- a/lib/ssl/ssl3exthandle.c
+++ b/lib/ssl/ssl3exthandle.c
@@ -1636,13 +1636,18 @@ SECStatus
ssl3_SendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added)
{
- SECStatus rv;
-
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
return SECSuccess;
}
- rv = ssl3_EncodeSigAlgs(ss, buf);
+ PRUint16 minVersion;
+ if (ss->sec.isServer) {
+ minVersion = ss->version; /* CertificateRequest */
+ } else {
+ minVersion = ss->vrange.min; /* ClientHello */
+ }
+
+ SECStatus rv = ssl3_EncodeSigAlgs(ss, minVersion, buf);
if (rv != SECSuccess) {
return SECFailure;
}
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index e12b20112..a9ea7679e 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -1681,7 +1681,8 @@ SECStatus ssl_SetAuthKeyBits(sslSocket *ss, const SECKEYPublicKey *pubKey);
SECStatus ssl3_AuthCertificate(sslSocket *ss);
SECStatus ssl_ReadCertificateStatus(sslSocket *ss, PRUint8 *b,
PRUint32 length);
-SECStatus ssl3_EncodeSigAlgs(const sslSocket *ss, sslBuffer *buf);
+SECStatus ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint16 minVersion,
+ sslBuffer *buf);
SECStatus ssl_GetCertificateRequestCAs(const sslSocket *ss,
unsigned int *calenp,
const SECItem **namesp,
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index ec7af24b2..a587e3890 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -1731,18 +1731,10 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
}
-#ifndef PARANOID
- /* Look for a matching cipher suite. */
- if (ssl3_config_match_init(ss) == 0) { /* no ciphers are working/supported by PK11 */
- FATAL_ERROR(ss, PORT_GetError(), internal_error);
- goto loser;
- }
-#endif
-
/* Negotiate cipher suite. */
rv = ssl3_NegotiateCipherSuite(ss, suites, PR_FALSE);
if (rv != SECSuccess) {
- FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
+ FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
goto loser;
}