cae166
# HG changeset patch
cae166
# User Daiki Ueno <dueno@redhat.com>
cae166
# Date 1573203603 -3600
cae166
#      Fri Nov 08 10:00:03 2019 +0100
cae166
# Node ID c08947c6af5789510641ad17373e2612361e4ec6
cae166
# Parent  e766899c72a517976f5f4abfec2a56712841e411
cae166
Bug 1566131, check policy against hash algorithms used for ServerKeyExchange, r=mt
cae166
cae166
Summary: This adds necessary policy checks in `ssl3_ComputeCommonKeyHash()`, right before calculating hashes.  Note that it currently doesn't check MD5 as it still needs to be allowed in TLS 1.1 or earlier and many tests fail if we change that.
cae166
cae166
Reviewers: mt
cae166
cae166
Reviewed By: mt
cae166
cae166
Bug #: 1566131
cae166
cae166
Differential Revision: https://phabricator.services.mozilla.com/D45867
cae166
cae166
diff --git a/gtests/ssl_gtest/ssl_dhe_unittest.cc b/gtests/ssl_gtest/ssl_dhe_unittest.cc
cae166
--- a/gtests/ssl_gtest/ssl_dhe_unittest.cc
cae166
+++ b/gtests/ssl_gtest/ssl_dhe_unittest.cc
cae166
@@ -682,4 +682,100 @@ TEST_P(TlsConnectTls12, ConnectInconsist
cae166
   ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
cae166
 }
cae166
 
cae166
+static void CheckSkeSigScheme(
cae166
+    std::shared_ptr<TlsHandshakeRecorder>& capture_ske,
cae166
+    uint16_t expected_scheme) {
cae166
+  TlsParser parser(capture_ske->buffer());
cae166
+  EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_p";
cae166
+  EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_q";
cae166
+  EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_Ys";
cae166
+
cae166
+  uint32_t tmp;
cae166
+  EXPECT_TRUE(parser.Read(&tmp, 2)) << " read sig_scheme";
cae166
+  EXPECT_EQ(expected_scheme, static_cast<uint16_t>(tmp));
cae166
+}
cae166
+
cae166
+TEST_P(TlsConnectTls12, ConnectSigAlgEnabledByPolicyDhe) {
cae166
+  EnableOnlyDheCiphers();
cae166
+
cae166
+  const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
cae166
+                                                   ssl_sig_rsa_pkcs1_sha384};
cae166
+
cae166
+  EnsureTlsSetup();
cae166
+  client_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  server_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
cae166
+      server_, kTlsHandshakeServerKeyExchange);
cae166
+
cae166
+  StartConnect();
cae166
+  client_->Handshake();  // Send ClientHello
cae166
+
cae166
+  // Enable SHA-1 by policy.
cae166
+  SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, NSS_USE_ALG_IN_SSL_KX, 0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+  rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
cae166
+                              0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+
cae166
+  Handshake();  // Remainder of handshake
cae166
+  // The server should now report that it is connected
cae166
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
cae166
+
cae166
+  CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha1);
cae166
+}
cae166
+
cae166
+TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicyDhe) {
cae166
+  EnableOnlyDheCiphers();
cae166
+
cae166
+  const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
cae166
+                                                   ssl_sig_rsa_pkcs1_sha384};
cae166
+
cae166
+  EnsureTlsSetup();
cae166
+  client_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  server_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
cae166
+      server_, kTlsHandshakeServerKeyExchange);
cae166
+
cae166
+  StartConnect();
cae166
+  client_->Handshake();  // Send ClientHello
cae166
+
cae166
+  // Disable SHA-1 by policy after sending ClientHello so that CH
cae166
+  // includes SHA-1 signature scheme.
cae166
+  SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 0, NSS_USE_ALG_IN_SSL_KX);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+  rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
cae166
+                              0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+
cae166
+  Handshake();  // Remainder of handshake
cae166
+  // The server should now report that it is connected
cae166
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
cae166
+
cae166
+  CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
cae166
+}
cae166
+
cae166
+TEST_P(TlsConnectPre12, ConnectSigAlgDisabledByPolicyDhePre12) {
cae166
+  EnableOnlyDheCiphers();
cae166
+
cae166
+  EnsureTlsSetup();
cae166
+  StartConnect();
cae166
+  client_->Handshake();  // Send ClientHello
cae166
+
cae166
+  // Disable SHA-1 by policy.  This will cause the connection fail as
cae166
+  // TLS 1.1 or earlier uses combined SHA-1 + MD5 signature.
cae166
+  SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 0, NSS_USE_ALG_IN_SSL_KX);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+  rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
cae166
+                              0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+
cae166
+  server_->ExpectSendAlert(kTlsAlertHandshakeFailure);
cae166
+  client_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
cae166
+
cae166
+  // Remainder of handshake
cae166
+  Handshake();
cae166
+
cae166
+  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
cae166
+}
cae166
+
cae166
 }  // namespace nss_test
cae166
diff --git a/gtests/ssl_gtest/ssl_ecdh_unittest.cc b/gtests/ssl_gtest/ssl_ecdh_unittest.cc
cae166
--- a/gtests/ssl_gtest/ssl_ecdh_unittest.cc
cae166
+++ b/gtests/ssl_gtest/ssl_ecdh_unittest.cc
cae166
@@ -666,6 +666,80 @@ TEST_P(TlsConnectTls12, ConnectIncorrect
cae166
   client_->CheckErrorCode(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
cae166
 }
cae166
 
cae166
+static void CheckSkeSigScheme(
cae166
+    std::shared_ptr<TlsHandshakeRecorder> &capture_ske,
cae166
+    uint16_t expected_scheme) {
cae166
+  TlsParser parser(capture_ske->buffer());
cae166
+  uint32_t tmp = 0;
cae166
+  EXPECT_TRUE(parser.Read(&tmp, 1)) << " read curve_type";
cae166
+  EXPECT_EQ(3U, tmp) << "curve type has to be 3";
cae166
+  EXPECT_TRUE(parser.Skip(2)) << " read namedcurve";
cae166
+  EXPECT_TRUE(parser.SkipVariable(1)) << " read public";
cae166
+
cae166
+  EXPECT_TRUE(parser.Read(&tmp, 2)) << " read sig_scheme";
cae166
+  EXPECT_EQ(expected_scheme, static_cast<uint16_t>(tmp));
cae166
+}
cae166
+
cae166
+TEST_P(TlsConnectTls12, ConnectSigAlgEnabledByPolicy) {
cae166
+  EnsureTlsSetup();
cae166
+  client_->DisableAllCiphers();
cae166
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
cae166
+
cae166
+  const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
cae166
+                                                   ssl_sig_rsa_pkcs1_sha384};
cae166
+
cae166
+  client_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  server_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
cae166
+      server_, kTlsHandshakeServerKeyExchange);
cae166
+
cae166
+  StartConnect();
cae166
+  client_->Handshake();  // Send ClientHello
cae166
+
cae166
+  // Enable SHA-1 by policy.
cae166
+  SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, NSS_USE_ALG_IN_SSL_KX, 0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+  rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
cae166
+                              0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+
cae166
+  Handshake();  // Remainder of handshake
cae166
+  // The server should now report that it is connected
cae166
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
cae166
+
cae166
+  CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha1);
cae166
+}
cae166
+
cae166
+TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicy) {
cae166
+  EnsureTlsSetup();
cae166
+  client_->DisableAllCiphers();
cae166
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
cae166
+
cae166
+  const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
cae166
+                                                   ssl_sig_rsa_pkcs1_sha384};
cae166
+
cae166
+  client_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  server_->SetSignatureSchemes(schemes.data(), schemes.size());
cae166
+  auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
cae166
+      server_, kTlsHandshakeServerKeyExchange);
cae166
+
cae166
+  StartConnect();
cae166
+  client_->Handshake();  // Send ClientHello
cae166
+
cae166
+  // Disable SHA-1 by policy.
cae166
+  SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 0, NSS_USE_ALG_IN_SSL_KX);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+  rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
cae166
+                              0);
cae166
+  ASSERT_EQ(SECSuccess, rv);
cae166
+
cae166
+  Handshake();  // Remainder of handshake
cae166
+  // The server should now report that it is connected
cae166
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
cae166
+
cae166
+  CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
cae166
+}
cae166
+
cae166
 INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest,
cae166
                         ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
cae166
                                            TlsConnectTestBase::kTlsV11Plus));
cae166
diff --git a/gtests/ssl_gtest/tls_connect.h b/gtests/ssl_gtest/tls_connect.h
cae166
--- a/gtests/ssl_gtest/tls_connect.h
cae166
+++ b/gtests/ssl_gtest/tls_connect.h
cae166
@@ -164,7 +164,7 @@ class TlsConnectTestBase : public ::test
cae166
   // ssl_extension_unittest.cc.
cae166
   const std::vector<SECOidTag> algorithms_ = {SEC_OID_APPLY_SSL_POLICY,
cae166
                                               SEC_OID_ANSIX9_DSA_SIGNATURE,
cae166
-                                              SEC_OID_CURVE25519};
cae166
+                                              SEC_OID_CURVE25519, SEC_OID_SHA1};
cae166
   std::vector<std::tuple<SECOidTag, uint32_t>> saved_policies_;
cae166
 
cae166
  private:
cae166
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
cae166
--- a/lib/ssl/ssl3con.c
cae166
+++ b/lib/ssl/ssl3con.c
cae166
@@ -1454,8 +1454,14 @@ ssl3_ComputeCommonKeyHash(SSLHashType ha
cae166
 {
cae166
     SECStatus rv;
cae166
     SECOidTag hashOID;
cae166
+    PRUint32 policy;
cae166
 
cae166
     if (hashAlg == ssl_hash_none) {
cae166
+        if ((NSS_GetAlgorithmPolicy(SEC_OID_SHA1, &policy) == SECSuccess) &&
cae166
+            !(policy & NSS_USE_ALG_IN_SSL_KX)) {
cae166
+            ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
cae166
+            return SECFailure;
cae166
+        }
cae166
         rv = PK11_HashBuf(SEC_OID_MD5, hashes->u.s.md5, hashBuf, bufLen);
cae166
         if (rv != SECSuccess) {
cae166
             ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);
cae166
@@ -1469,6 +1475,11 @@ ssl3_ComputeCommonKeyHash(SSLHashType ha
cae166
         hashes->len = MD5_LENGTH + SHA1_LENGTH;
cae166
     } else {
cae166
         hashOID = ssl3_HashTypeToOID(hashAlg);
cae166
+        if ((NSS_GetAlgorithmPolicy(hashOID, &policy) == SECSuccess) &&
cae166
+            !(policy & NSS_USE_ALG_IN_SSL_KX)) {
cae166
+            ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
cae166
+            return SECFailure;
cae166
+        }
cae166
         hashes->len = HASH_ResultLenByOidTag(hashOID);
cae166
         if (hashes->len == 0 || hashes->len > sizeof(hashes->u.raw)) {
cae166
             ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);