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