ueno / rpms / nss

Forked from rpms/nss 5 years ago
Clone
Blob Blame History Raw
diff --git a/gtests/nss_bogo_shim/nss_bogo_shim.cc b/gtests/nss_bogo_shim/nss_bogo_shim.cc
--- a/gtests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/gtests/nss_bogo_shim/nss_bogo_shim.cc
@@ -260,16 +260,22 @@ class TestAgent {
     if (rv != SECSuccess) return false;
 
     SSLVersionRange vrange;
     if (!GetVersionRange(&vrange, ssl_variant_stream)) return false;
 
     rv = SSL_VersionRangeSet(ssl_fd_, &vrange);
     if (rv != SECSuccess) return false;
 
+    SSLVersionRange verify_vrange;
+    rv = SSL_VersionRangeGet(ssl_fd_, &verify_vrange);
+    if (rv != SECSuccess) return false;
+    if (vrange.min != verify_vrange.min || vrange.max != verify_vrange.max)
+      return false;
+
     rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, false);
     if (rv != SECSuccess) return false;
 
     auto alpn = cfg_.get<std::string>("advertise-alpn");
     if (!alpn.empty()) {
       assert(!cfg_.get<bool>("server"));
 
       rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE);
diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -20,16 +20,21 @@ extern "C" {
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 
 extern std::string g_working_dir_path;
 
+static bool SSLVersionRangesAreEqual(SSLVersionRange& vr1,
+                                     SSLVersionRange& vr2) {
+  return vr1.min == vr2.min && vr1.max == vr2.max;
+}
+
 namespace nss_test {
 
 const char* TlsAgent::states[] = {"INIT", "CONNECTING", "CONNECTED", "ERROR"};
 
 const std::string TlsAgent::kClient = "client";    // both sign and encrypt
 const std::string TlsAgent::kRsa2048 = "rsa2048";  // bigger
 const std::string TlsAgent::kServerRsa = "rsa";    // both sign and encrypt
 const std::string TlsAgent::kServerRsaSign = "rsa_sign";
@@ -156,16 +161,26 @@ bool TlsAgent::EnsureTlsSetup(PRFileDesc
     return false;
   }
   dummy_fd.release();  // Now subsumed by ssl_fd_.
 
   SECStatus rv = SSL_VersionRangeSet(ssl_fd(), &vrange_);
   EXPECT_EQ(SECSuccess, rv);
   if (rv != SECSuccess) return false;
 
+  SSLVersionRange verify_vrange;
+  rv = SSL_VersionRangeGet(ssl_fd(), &verify_vrange);
+  EXPECT_EQ(SECSuccess, rv);
+  if (rv != SECSuccess) return false;
+
+  bool ranges_are_equal = SSLVersionRangesAreEqual(vrange_, verify_vrange);
+  EXPECT_TRUE(ranges_are_equal)
+      << "System policy must not restrict the allowed min/max SSL/TLS range";
+  if (!ranges_are_equal) return false;
+
   if (role_ == SERVER) {
     EXPECT_TRUE(ConfigServerCert(name_, true));
 
     rv = SSL_SNISocketConfigHook(ssl_fd(), SniHook, this);
     EXPECT_EQ(SECSuccess, rv);
     if (rv != SECSuccess) return false;
 
     ScopedCERTCertList anchors(CERT_NewCertList());
@@ -400,16 +415,23 @@ void TlsAgent::SetShortHeadersEnabled() 
 
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
   vrange_.min = minver;
   vrange_.max = maxver;
 
   if (ssl_fd()) {
     SECStatus rv = SSL_VersionRangeSet(ssl_fd(), &vrange_);
     EXPECT_EQ(SECSuccess, rv);
+
+    SSLVersionRange verify_vrange;
+    rv = SSL_VersionRangeGet(ssl_fd(), &verify_vrange);
+    EXPECT_EQ(SECSuccess, rv);
+    bool ranges_are_equal = SSLVersionRangesAreEqual(vrange_, verify_vrange);
+    EXPECT_TRUE(ranges_are_equal)
+        << "System policy must not restrict the allowed min/max SSL/TLS range";
   }
 }
 
 void TlsAgent::GetVersionRange(uint16_t* minver, uint16_t* maxver) {
   *minver = vrange_.min;
   *maxver = vrange_.max;
 }
 
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -2202,38 +2202,42 @@ ssl3_GetRangePolicy(SSLProtocolVariant p
         return SECFailure; /* don't accept an invalid policy */
     }
     return SECSuccess;
 }
 
 /*
  * Constrain a single protocol variant's range based on the user policy
  */
-static SECStatus
-ssl3_ConstrainVariantRangeByPolicy(SSLProtocolVariant protocolVariant)
+static void
+ssl3_ConstrainVariantRangeByPolicy(SSLProtocolVariant protocolVariant,
+                                   SSLVersionRange *rangeParam /* in and out */)
 {
     SSLVersionRange vrange;
     SSLVersionRange pvrange;
     SECStatus rv;
 
-    vrange = *VERSIONS_DEFAULTS(protocolVariant);
+    if (!rangeParam) {
+        return;
+    }
+
+    vrange = *rangeParam;
     rv = ssl3_GetRangePolicy(protocolVariant, &pvrange);
     if (rv != SECSuccess) {
-        return SECSuccess; /* we don't have any policy */
+        return; /* we don't have any policy */
     }
     vrange.min = PR_MAX(vrange.min, pvrange.min);
     vrange.max = PR_MIN(vrange.max, pvrange.max);
     if (vrange.max >= vrange.min) {
-        *VERSIONS_DEFAULTS(protocolVariant) = vrange;
+        *rangeParam = vrange;
     } else {
         /* there was no overlap, turn off range altogether */
         pvrange.min = pvrange.max = SSL_LIBRARY_VERSION_NONE;
-        *VERSIONS_DEFAULTS(protocolVariant) = pvrange;
+        *rangeParam = pvrange;
     }
-    return SECSuccess;
 }
 
 static PRBool
 ssl_VersionIsSupportedByPolicy(SSLProtocolVariant protocolVariant,
                                SSL3ProtocolVersion version)
 {
     SSLVersionRange pvrange;
     SECStatus rv;
@@ -2249,60 +2253,59 @@ ssl_VersionIsSupportedByPolicy(SSLProtoc
 
 /*
  *  This is called at SSL init time to constrain the existing range based
  *  on user supplied policy.
  */
 SECStatus
 ssl3_ConstrainRangeByPolicy(void)
 {
-    SECStatus rv;
-    rv = ssl3_ConstrainVariantRangeByPolicy(ssl_variant_stream);
-    if (rv != SECSuccess) {
-        return rv;
+    ssl3_ConstrainVariantRangeByPolicy(ssl_variant_stream,
+                                       VERSIONS_DEFAULTS(ssl_variant_stream));
+    ssl3_ConstrainVariantRangeByPolicy(ssl_variant_datagram,
+                                       VERSIONS_DEFAULTS(ssl_variant_datagram));
+    return SECSuccess;
+}
+
+PRBool
+ssl3_VersionIsSupportedByCode(SSLProtocolVariant protocolVariant,
+                              SSL3ProtocolVersion version)
+{
+    switch (protocolVariant) {
+        case ssl_variant_stream:
+            return (version >= SSL_LIBRARY_VERSION_3_0 &&
+                    version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
+        case ssl_variant_datagram:
+            return (version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
+                    version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
     }
-    rv = ssl3_ConstrainVariantRangeByPolicy(ssl_variant_datagram);
-    if (rv != SECSuccess) {
-        return rv;
-    }
-    return SECSuccess;
+
+    /* Can't get here */
+    PORT_Assert(PR_FALSE);
+    return PR_FALSE;
 }
 
 PRBool
 ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant,
                         SSL3ProtocolVersion version)
 {
     if (!ssl_VersionIsSupportedByPolicy(protocolVariant, version)) {
         return PR_FALSE;
     }
-    switch (protocolVariant) {
-        case ssl_variant_stream:
-            return (version >= SSL_LIBRARY_VERSION_3_0 &&
-                    version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
-        case ssl_variant_datagram:
-            return (version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
-                    version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
-        default:
-            /* Can't get here */
-            PORT_Assert(PR_FALSE);
-            return PR_FALSE;
-    }
+    return ssl3_VersionIsSupportedByCode(protocolVariant, version);
 }
 
-/* Returns PR_TRUE if the given version range is valid and
-** fully supported; otherwise, returns PR_FALSE.
-*/
 static PRBool
 ssl3_VersionRangeIsValid(SSLProtocolVariant protocolVariant,
                          const SSLVersionRange *vrange)
 {
     return vrange &&
            vrange->min <= vrange->max &&
-           ssl3_VersionIsSupported(protocolVariant, vrange->min) &&
-           ssl3_VersionIsSupported(protocolVariant, vrange->max) &&
+           ssl3_VersionIsSupportedByCode(protocolVariant, vrange->min) &&
+           ssl3_VersionIsSupportedByCode(protocolVariant, vrange->max) &&
            (vrange->min > SSL_LIBRARY_VERSION_3_0 ||
             vrange->max < SSL_LIBRARY_VERSION_TLS_1_3);
 }
 
 const SECItem *
 SSL_PeerSignedCertTimestamps(PRFileDesc *fd)
 {
     sslSocket *ss = ssl_FindSocket(fd);
@@ -2329,60 +2332,116 @@ SSL_VersionRangeGetSupported(SSLProtocol
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     switch (protocolVariant) {
         case ssl_variant_stream:
             vrange->min = SSL_LIBRARY_VERSION_3_0;
             vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
-            // We don't allow SSLv3 and TLSv1.3 together.
-            if (vrange->max == SSL_LIBRARY_VERSION_TLS_1_3) {
-                vrange->min = SSL_LIBRARY_VERSION_TLS_1_0;
-            }
+            /* We don't allow SSLv3 and TLSv1.3 together.
+             * However, don't check yet, apply the policy first.
+             * Because if the effective supported range doesn't use TLS 1.3,
+             * then we don't need to increase the minimum. */
             break;
         case ssl_variant_datagram:
             vrange->min = SSL_LIBRARY_VERSION_TLS_1_1;
             vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
             break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
             return SECFailure;
     }
 
+    ssl3_ConstrainVariantRangeByPolicy(protocolVariant, vrange);
+    if (vrange->min == SSL_LIBRARY_VERSION_NONE) {
+        /* Library default and policy don't overlap. */
+        return SECFailure;
+    }
+
+    if (protocolVariant == ssl_variant_stream) {
+        /* We don't allow SSLv3 and TLSv1.3 together */
+        if (vrange->max == SSL_LIBRARY_VERSION_TLS_1_3) {
+            vrange->min = PR_MAX(vrange->min, SSL_LIBRARY_VERSION_TLS_1_0);
+        }
+    }
+
     return SECSuccess;
 }
 
 SECStatus
 SSL_VersionRangeGetDefault(SSLProtocolVariant protocolVariant,
                            SSLVersionRange *vrange)
 {
     if ((protocolVariant != ssl_variant_stream &&
          protocolVariant != ssl_variant_datagram) ||
         !vrange) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     *vrange = *VERSIONS_DEFAULTS(protocolVariant);
+    ssl3_ConstrainVariantRangeByPolicy(protocolVariant, vrange);
+
+    if (vrange->min == SSL_LIBRARY_VERSION_NONE) {
+        /* Library default and policy don't overlap. */
+        return SECFailure;
+    }
 
     return SECSuccess;
 }
 
-SECStatus
-SSL_VersionRangeSetDefault(SSLProtocolVariant protocolVariant,
-                           const SSLVersionRange *vrange)
+static SECStatus
+ssl3_CheckRangeValidAndConstrainByPolicy(SSLProtocolVariant protocolVariant,
+                                         SSLVersionRange *vrange)
 {
     if (!ssl3_VersionRangeIsValid(protocolVariant, vrange)) {
         PORT_SetError(SSL_ERROR_INVALID_VERSION_RANGE);
         return SECFailure;
     }
 
-    *VERSIONS_DEFAULTS(protocolVariant) = *vrange;
-
+    /* Try to adjust the received range using our policy.
+     * If there's overlap, we'll use the (possibly reduced) range.
+     * If there isn't overlap, it's failure. */
+
+    ssl3_ConstrainVariantRangeByPolicy(protocolVariant, vrange);
+    if (vrange->min == SSL_LIBRARY_VERSION_NONE) {
+        return SECFailure;
+    }
+
+    if (protocolVariant == ssl_variant_stream) {
+        /* We don't allow SSLv3 and TLSv1.3 together */
+        if (vrange->max == SSL_LIBRARY_VERSION_TLS_1_3) {
+            vrange->min =
+                PR_MAX(vrange->min, SSL_LIBRARY_VERSION_TLS_1_0);
+        }
+    }
+
+    return SECSuccess;
+}
+
+SECStatus
+SSL_VersionRangeSetDefault(SSLProtocolVariant protocolVariant,
+                           const SSLVersionRange *vrange)
+{
+    SSLVersionRange constrainedRange;
+    SECStatus rv;
+
+    if (!vrange) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    constrainedRange = *vrange;
+    rv = ssl3_CheckRangeValidAndConstrainByPolicy(protocolVariant,
+                                                  &constrainedRange);
+    if (rv != SECSuccess)
+        return rv;
+
+    *VERSIONS_DEFAULTS(protocolVariant) = constrainedRange;
     return SECSuccess;
 }
 
 SECStatus
 SSL_VersionRangeGet(PRFileDesc *fd, SSLVersionRange *vrange)
 {
     sslSocket *ss = ssl_FindSocket(fd);
 
@@ -2406,41 +2465,50 @@ SSL_VersionRangeGet(PRFileDesc *fd, SSLV
     ssl_Release1stHandshakeLock(ss);
 
     return SECSuccess;
 }
 
 SECStatus
 SSL_VersionRangeSet(PRFileDesc *fd, const SSLVersionRange *vrange)
 {
-    sslSocket *ss = ssl_FindSocket(fd);
-
+    SSLVersionRange constrainedRange;
+    sslSocket *ss;
+    SECStatus rv;
+
+    if (!vrange) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    ss = ssl_FindSocket(fd);
     if (!ss) {
         SSL_DBG(("%d: SSL[%d]: bad socket in SSL_VersionRangeSet",
                  SSL_GETPID(), fd));
         return SECFailure;
     }
 
-    if (!ssl3_VersionRangeIsValid(ss->protocolVariant, vrange)) {
-        PORT_SetError(SSL_ERROR_INVALID_VERSION_RANGE);
-        return SECFailure;
-    }
+    constrainedRange = *vrange;
+    rv = ssl3_CheckRangeValidAndConstrainByPolicy(ss->protocolVariant,
+                                                  &constrainedRange);
+    if (rv != SECSuccess)
+        return rv;
 
     ssl_Get1stHandshakeLock(ss);
     ssl_GetSSL3HandshakeLock(ss);
 
     if (ss->ssl3.downgradeCheckVersion &&
         ss->vrange.max > ss->ssl3.downgradeCheckVersion) {
         PORT_SetError(SSL_ERROR_INVALID_VERSION_RANGE);
         ssl_ReleaseSSL3HandshakeLock(ss);
         ssl_Release1stHandshakeLock(ss);
         return SECFailure;
     }
 
-    ss->vrange = *vrange;
+    ss->vrange = constrainedRange;
 
     ssl_ReleaseSSL3HandshakeLock(ss);
     ssl_Release1stHandshakeLock(ss);
 
     return SECSuccess;
 }
 
 SECStatus
diff --git a/gtests/ssl_gtest/Makefile b/gtests/ssl_gtest/Makefile
--- a/gtests/ssl_gtest/Makefile
+++ b/gtests/ssl_gtest/Makefile
@@ -32,16 +32,18 @@ CFLAGS += -I$(CORE_DEPTH)/lib/ssl
 ifdef NSS_SSL_ENABLE_ZLIB
 include $(CORE_DEPTH)/coreconf/zlib.mk
 endif
 
 ifdef NSS_DISABLE_TLS_1_3
 NSS_DISABLE_TLS_1_3=1
 # Run parameterized tests only, for which we can easily exclude TLS 1.3
 CPPSRCS := $(filter-out $(shell grep -l '^TEST_F' $(CPPSRCS)), $(CPPSRCS))
+# But always include ssl_versionpolicy_unittest.cc
+CPPSRCS += ssl_versionpolicy_unittest.cc
 CFLAGS += -DNSS_DISABLE_TLS_1_3
 endif
 
 #######################################################################
 # (5) Execute "global" rules. (OPTIONAL)                              #
 #######################################################################
 
 include $(CORE_DEPTH)/coreconf/rules.mk
diff --git a/gtests/ssl_gtest/manifest.mn b/gtests/ssl_gtest/manifest.mn
--- a/gtests/ssl_gtest/manifest.mn
+++ b/gtests/ssl_gtest/manifest.mn
@@ -33,16 +33,17 @@ CPPSRCS = \
       ssl_hrr_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
+      ssl_versionpolicy_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_parser.cc \
       tls_protect.cc \
       $(NULL)
diff --git a/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc b/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc
new file mode 100644
--- /dev/null
+++ b/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "nss.h"
+#include "secerr.h"
+#include "ssl.h"
+#include "ssl3prot.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+static bool SSLVersionRangesAreEqual(SSLVersionRange &vr1,
+                                     SSLVersionRange &vr2) {
+  return vr1.min == vr2.min && vr1.max == vr2.max;
+}
+
+namespace nss_test {
+
+class TestVersionRangePolicy : public ::testing::Test {
+ protected:
+  PRInt32 savedMinTLS;
+  PRInt32 savedMaxTLS;
+  PRInt32 savedMinDTLS;
+  PRInt32 savedMaxDTLS;
+  PRUint32 savedAlgorithmPolicy;
+
+ public:
+  void SaveOriginalPolicy() {
+    NSS_OptionGet(NSS_TLS_VERSION_MIN_POLICY, &savedMinTLS);
+    NSS_OptionGet(NSS_TLS_VERSION_MAX_POLICY, &savedMaxTLS);
+    NSS_OptionGet(NSS_DTLS_VERSION_MIN_POLICY, &savedMinDTLS);
+    NSS_OptionGet(NSS_DTLS_VERSION_MAX_POLICY, &savedMaxDTLS);
+    NSS_GetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, &savedAlgorithmPolicy);
+  }
+  void SetUsePolicyInSSL() {
+    NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0);
+  }
+  void RestoreOriginalPolicy() {
+    NSS_OptionSet(NSS_TLS_VERSION_MIN_POLICY, savedMinTLS);
+    NSS_OptionSet(NSS_TLS_VERSION_MAX_POLICY, savedMaxTLS);
+    NSS_OptionSet(NSS_DTLS_VERSION_MIN_POLICY, savedMinDTLS);
+    NSS_OptionSet(NSS_DTLS_VERSION_MAX_POLICY, savedMaxDTLS);
+    /* If it wasn't set initially, clear the bit that we set. */
+    if (!(savedAlgorithmPolicy & NSS_USE_POLICY_IN_SSL)) {
+      NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, 0,
+                             NSS_USE_POLICY_IN_SSL);
+    }
+  }
+  void SetTLSPolicy(SSLVersionRange &policy) {
+    NSS_OptionSet(NSS_TLS_VERSION_MIN_POLICY, policy.min);
+    NSS_OptionSet(NSS_TLS_VERSION_MAX_POLICY, policy.max);
+  }
+  void SetDTLSPolicy(SSLVersionRange &policy) {
+    /* SSL3 isn't allowed for DTLS, but isn't a problem to allow by policy */
+    NSS_OptionSet(NSS_DTLS_VERSION_MIN_POLICY, policy.min);
+    NSS_OptionSet(NSS_DTLS_VERSION_MAX_POLICY, policy.max);
+  }
+  std::string version_to_string(PRInt32 v) {
+    switch (v) {
+      case SSL_LIBRARY_VERSION_3_0:
+        return "ssl3";
+      case SSL_LIBRARY_VERSION_TLS_1_0:
+        return "tls1.0";
+      case SSL_LIBRARY_VERSION_TLS_1_1:
+        return "tls1.1";
+      case SSL_LIBRARY_VERSION_TLS_1_2:
+        return "tls1.2";
+      case SSL_LIBRARY_VERSION_TLS_1_3:
+        return "tls1.3";
+      case SSL_LIBRARY_VERSION_NONE:
+        return "NONE";
+    }
+    return "undefined???";
+  }
+  std::string info_str(const SSLVersionRange &policy,
+                       const SSLVersionRange &vrange,
+                       const SSLVersionRange *expectation,
+                       const SSLVersionRange *result, bool testDTLS) {
+    return std::string(testDTLS ? "DTLS" : "TLS") + std::string(" policy: ") +
+           version_to_string(policy.min) + std::string(",") +
+           version_to_string(policy.max) + std::string(" input: ") +
+           version_to_string(vrange.min) + std::string(",") +
+           version_to_string(vrange.max) +
+           (expectation
+                ? (std::string(" expected: ") +
+                   version_to_string(expectation->min) + std::string(",") +
+                   version_to_string(expectation->max))
+                : std::string()) +
+           (result
+                ? (std::string(" result: ") + version_to_string(result->min) +
+                   std::string(",") + version_to_string(result->max))
+                : std::string());
+  }
+  void TestPolicyRangeExpectation(SSLVersionRange &policy,
+                                  SSLVersionRange &vrange,
+                                  SSLVersionRange &expectation, bool testDTLS) {
+    SECStatus rv;
+
+    SetTLSPolicy(policy);
+    rv = SSL_VersionRangeSetDefault(ssl_variant_stream, &vrange);
+    EXPECT_EQ(SECSuccess, rv)
+        << "expected successful return from SSL_VersionRangeSetDefault with: "
+        << info_str(policy, vrange, &expectation, NULL, false);
+
+    SSLVersionRange result;
+    rv = SSL_VersionRangeGetDefault(ssl_variant_stream, &result);
+    EXPECT_EQ(SECSuccess, rv)
+        << "expected successful return from SSL_VersionRangeGetDefault: "
+        << info_str(policy, vrange, &expectation, NULL, false);
+
+    EXPECT_EQ(true, SSLVersionRangesAreEqual(result, expectation))
+        << "range returned by SSL_VersionRangeGetDefault doesn't match "
+           "expectation: "
+        << info_str(policy, vrange, &expectation, &result, false);
+
+    if (testDTLS) {
+      SetDTLSPolicy(policy);
+      rv = SSL_VersionRangeSetDefault(ssl_variant_datagram, &vrange);
+      EXPECT_EQ(SECSuccess, rv)
+          << "expected successful return from SSL_VersionRangeSetDefault with: "
+          << info_str(policy, vrange, &expectation, NULL, true);
+
+      SSLVersionRange result;
+      rv = SSL_VersionRangeGetDefault(ssl_variant_datagram, &result);
+      EXPECT_EQ(SECSuccess, rv)
+          << "expected successful return from SSL_VersionRangeGetDefault: "
+          << info_str(policy, vrange, &expectation, NULL, true);
+
+      EXPECT_EQ(true, SSLVersionRangesAreEqual(result, expectation))
+          << "range returned by SSL_VersionRangeGetDefault doesn't match "
+             "expectation: "
+          << info_str(policy, vrange, &expectation, &result, true);
+    }
+  }
+  void TestPolicyRangeFailure(SSLVersionRange &policy, SSLVersionRange &vrange,
+                              bool testDTLS) {
+    SECStatus rv;
+
+    SetTLSPolicy(policy);
+    rv = SSL_VersionRangeSetDefault(ssl_variant_stream, &vrange);
+    EXPECT_EQ(SECFailure, rv)
+        << "expected failure return from SSL_VersionRangeSetDefault with: "
+        << info_str(policy, vrange, NULL, NULL, false);
+
+    if (testDTLS) {
+      SetDTLSPolicy(policy);
+      rv = SSL_VersionRangeSetDefault(ssl_variant_datagram, &vrange);
+      EXPECT_EQ(SECFailure, rv)
+          << "expected failure return from SSL_VersionRangeSetDefault with: "
+          << info_str(policy, vrange, NULL, NULL, true);
+    }
+  }
+  void Run() {
+    SaveOriginalPolicy();
+    SetUsePolicyInSSL();
+
+#ifndef NSS_DISABLE_TLS_1_3
+    SSLVersionRange range3to13{SSL_LIBRARY_VERSION_3_0,
+                               SSL_LIBRARY_VERSION_TLS_1_3};
+    SSLVersionRange range10to13{SSL_LIBRARY_VERSION_TLS_1_0,
+                                SSL_LIBRARY_VERSION_TLS_1_3};
+    SSLVersionRange range11to13{SSL_LIBRARY_VERSION_TLS_1_1,
+                                SSL_LIBRARY_VERSION_TLS_1_3};
+    SSLVersionRange range12to13{SSL_LIBRARY_VERSION_TLS_1_2,
+                                SSL_LIBRARY_VERSION_TLS_1_3};
+    SSLVersionRange range13to13{SSL_LIBRARY_VERSION_TLS_1_3,
+                                SSL_LIBRARY_VERSION_TLS_1_3};
+#endif
+
+    SSLVersionRange range3to12{SSL_LIBRARY_VERSION_3_0,
+                               SSL_LIBRARY_VERSION_TLS_1_2};
+    SSLVersionRange range10to12{SSL_LIBRARY_VERSION_TLS_1_0,
+                                SSL_LIBRARY_VERSION_TLS_1_2};
+    SSLVersionRange range11to12{SSL_LIBRARY_VERSION_TLS_1_1,
+                                SSL_LIBRARY_VERSION_TLS_1_2};
+    SSLVersionRange range12to12{SSL_LIBRARY_VERSION_TLS_1_2,
+                                SSL_LIBRARY_VERSION_TLS_1_2};
+
+    SSLVersionRange range3to11{SSL_LIBRARY_VERSION_3_0,
+                               SSL_LIBRARY_VERSION_TLS_1_1};
+    SSLVersionRange range10to11{SSL_LIBRARY_VERSION_TLS_1_0,
+                                SSL_LIBRARY_VERSION_TLS_1_1};
+    SSLVersionRange range11to11{SSL_LIBRARY_VERSION_TLS_1_1,
+                                SSL_LIBRARY_VERSION_TLS_1_1};
+
+    SSLVersionRange range3to10{SSL_LIBRARY_VERSION_3_0,
+                               SSL_LIBRARY_VERSION_TLS_1_0};
+    SSLVersionRange range10to10{SSL_LIBRARY_VERSION_TLS_1_0,
+                                SSL_LIBRARY_VERSION_TLS_1_0};
+
+    SSLVersionRange range3to3{SSL_LIBRARY_VERSION_3_0, SSL_LIBRARY_VERSION_3_0};
+
+// When testing SSL3 or TLS1.0, we set "test DTLS" to false.
+
+#ifndef NSS_DISABLE_TLS_1_3
+    // Invalid range input (cannot enable both SSL3 and TLS1.3)
+    TestPolicyRangeFailure(range3to13, range3to13, false);
+#endif
+
+    // No overlap between policy and range input
+    TestPolicyRangeFailure(range11to11, range10to10, false);
+    TestPolicyRangeFailure(range11to11, range12to12, true);
+    TestPolicyRangeFailure(range10to12, range3to3, false);
+#ifndef NSS_DISABLE_TLS_1_3
+    TestPolicyRangeFailure(range10to12, range13to13, true);
+#endif
+
+    // straightforward overlap tests
+
+    TestPolicyRangeExpectation(range3to11, range10to12, range10to11, false);
+    TestPolicyRangeExpectation(range10to12, range10to12, range10to12, false);
+
+    TestPolicyRangeExpectation(range11to12, range10to12, range11to12, false);
+    TestPolicyRangeExpectation(range11to12, range11to12, range11to12, true);
+
+    TestPolicyRangeExpectation(range12to12, range10to12, range12to12, false);
+
+    TestPolicyRangeExpectation(range3to12, range3to3, range3to3, false);
+    TestPolicyRangeExpectation(range3to12, range3to10, range3to10, false);
+    TestPolicyRangeExpectation(range3to12, range3to11, range3to11, false);
+    TestPolicyRangeExpectation(range3to12, range3to12, range3to12, false);
+    TestPolicyRangeExpectation(range3to12, range10to10, range10to10, false);
+    TestPolicyRangeExpectation(range3to12, range10to11, range10to11, false);
+    TestPolicyRangeExpectation(range3to12, range10to12, range10to12, false);
+    TestPolicyRangeExpectation(range3to12, range11to11, range11to11, true);
+    TestPolicyRangeExpectation(range3to12, range11to12, range11to12, true);
+    TestPolicyRangeExpectation(range3to12, range12to12, range12to12, true);
+
+    TestPolicyRangeExpectation(range10to12, range3to10, range10to10, false);
+    TestPolicyRangeExpectation(range10to12, range3to11, range10to11, false);
+    TestPolicyRangeExpectation(range10to12, range3to12, range10to12, false);
+    TestPolicyRangeExpectation(range10to12, range10to10, range10to10, false);
+    TestPolicyRangeExpectation(range10to12, range10to11, range10to11, false);
+    TestPolicyRangeExpectation(range10to12, range10to12, range10to12, false);
+    TestPolicyRangeExpectation(range10to12, range11to11, range11to11, true);
+    TestPolicyRangeExpectation(range10to12, range11to12, range11to12, true);
+    TestPolicyRangeExpectation(range10to12, range12to12, range12to12, true);
+
+#ifndef NSS_DISABLE_TLS_1_3
+    TestPolicyRangeExpectation(range3to12, range10to13, range10to12, false);
+    TestPolicyRangeExpectation(range10to13, range10to13, range10to13, false);
+
+    TestPolicyRangeExpectation(range11to13, range10to13, range11to13, false);
+    TestPolicyRangeExpectation(range11to13, range11to13, range11to13, true);
+
+    TestPolicyRangeExpectation(range12to13, range10to13, range12to13, false);
+    TestPolicyRangeExpectation(range12to13, range11to13, range12to13, true);
+    TestPolicyRangeExpectation(range12to13, range12to13, range12to13, true);
+
+    TestPolicyRangeExpectation(range3to13, range3to3, range3to3, false);
+    TestPolicyRangeExpectation(range3to13, range3to10, range3to10, false);
+    TestPolicyRangeExpectation(range3to13, range3to11, range3to11, false);
+    TestPolicyRangeExpectation(range3to13, range3to12, range3to12, false);
+    TestPolicyRangeExpectation(range3to13, range10to10, range10to10, false);
+    TestPolicyRangeExpectation(range3to13, range10to11, range10to11, false);
+    TestPolicyRangeExpectation(range3to13, range10to12, range10to12, false);
+    TestPolicyRangeExpectation(range3to13, range10to13, range10to13, false);
+    TestPolicyRangeExpectation(range3to13, range11to11, range11to11, true);
+    TestPolicyRangeExpectation(range3to13, range11to12, range11to12, true);
+    TestPolicyRangeExpectation(range3to13, range12to12, range12to12, true);
+    TestPolicyRangeExpectation(range3to13, range12to13, range12to13, true);
+    TestPolicyRangeExpectation(range3to13, range13to13, range13to13, true);
+#endif
+
+    RestoreOriginalPolicy();
+  }
+};
+
+TEST_F(TestVersionRangePolicy, TestVersionRangesAndCryptoPolicyInteraction) {
+  Run();
+}
+
+}  // namespace nss_test