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