tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ssl_versionpolicy_unittest.cc (12989B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nss.h"
      8 #include "secerr.h"
      9 #include "ssl.h"
     10 #include "ssl3prot.h"
     11 #include "sslerr.h"
     12 #include "sslproto.h"
     13 
     14 #include "gtest_utils.h"
     15 #include "nss_scoped_ptrs.h"
     16 #include "tls_connect.h"
     17 #include "tls_filter.h"
     18 #include "tls_parser.h"
     19 
     20 #include <iostream>
     21 
     22 namespace nss_test {
     23 
     24 std::string GetSSLVersionString(uint16_t v) {
     25  switch (v) {
     26    case SSL_LIBRARY_VERSION_3_0:
     27      return "ssl3";
     28    case SSL_LIBRARY_VERSION_TLS_1_0:
     29      return "tls1.0";
     30    case SSL_LIBRARY_VERSION_TLS_1_1:
     31      return "tls1.1";
     32    case SSL_LIBRARY_VERSION_TLS_1_2:
     33      return "tls1.2";
     34    case SSL_LIBRARY_VERSION_TLS_1_3:
     35      return "tls1.3";
     36    case SSL_LIBRARY_VERSION_NONE:
     37      return "NONE";
     38  }
     39  if (v < SSL_LIBRARY_VERSION_3_0) {
     40    return "undefined-too-low";
     41  }
     42  return "undefined-too-high";
     43 }
     44 
     45 inline std::ostream& operator<<(std::ostream& stream,
     46                                const SSLVersionRange& vr) {
     47  return stream << GetSSLVersionString(vr.min) << ","
     48                << GetSSLVersionString(vr.max);
     49 }
     50 
     51 class VersionRangeWithLabel {
     52 public:
     53  VersionRangeWithLabel(const std::string& txt, const SSLVersionRange& vr)
     54      : label_(txt), vr_(vr) {}
     55  VersionRangeWithLabel(const std::string& txt, uint16_t start, uint16_t end)
     56      : label_(txt) {
     57    vr_.min = start;
     58    vr_.max = end;
     59  }
     60  VersionRangeWithLabel(const std::string& label) : label_(label) {
     61    vr_.min = vr_.max = SSL_LIBRARY_VERSION_NONE;
     62  }
     63 
     64  void WriteStream(std::ostream& stream) const {
     65    stream << " " << label_ << ": " << vr_;
     66  }
     67 
     68  uint16_t min() const { return vr_.min; }
     69  uint16_t max() const { return vr_.max; }
     70  SSLVersionRange range() const { return vr_; }
     71 
     72 private:
     73  std::string label_;
     74  SSLVersionRange vr_;
     75 };
     76 
     77 inline std::ostream& operator<<(std::ostream& stream,
     78                                const VersionRangeWithLabel& vrwl) {
     79  vrwl.WriteStream(stream);
     80  return stream;
     81 }
     82 
     83 typedef std::tuple<SSLProtocolVariant,  // variant
     84                   uint16_t,            // policy min
     85                   uint16_t,            // policy max
     86                   uint16_t,            // input min
     87                   uint16_t>            // input max
     88    PolicyVersionRangeInput;
     89 
     90 class TestPolicyVersionRange
     91    : public TlsConnectTestBase,
     92      public ::testing::WithParamInterface<PolicyVersionRangeInput> {
     93 public:
     94  TestPolicyVersionRange()
     95      : TlsConnectTestBase(std::get<0>(GetParam()), 0),
     96        variant_(std::get<0>(GetParam())),
     97        policy_("policy", std::get<1>(GetParam()), std::get<2>(GetParam())),
     98        input_("input", std::get<3>(GetParam()), std::get<4>(GetParam())),
     99        library_("supported-by-library",
    100                 ((variant_ == ssl_variant_stream)
    101                      ? SSL_LIBRARY_VERSION_MIN_SUPPORTED_STREAM
    102                      : SSL_LIBRARY_VERSION_MIN_SUPPORTED_DATAGRAM),
    103                 SSL_LIBRARY_VERSION_MAX_SUPPORTED) {
    104    TlsConnectTestBase::SkipVersionChecks();
    105  }
    106 
    107  void SetPolicy(const SSLVersionRange& policy) {
    108    NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0);
    109 
    110    SECStatus rv;
    111    rv = NSS_OptionSet(NSS_TLS_VERSION_MIN_POLICY, policy.min);
    112    ASSERT_EQ(SECSuccess, rv);
    113    rv = NSS_OptionSet(NSS_TLS_VERSION_MAX_POLICY, policy.max);
    114    ASSERT_EQ(SECSuccess, rv);
    115    rv = NSS_OptionSet(NSS_DTLS_VERSION_MIN_POLICY, policy.min);
    116    ASSERT_EQ(SECSuccess, rv);
    117    rv = NSS_OptionSet(NSS_DTLS_VERSION_MAX_POLICY, policy.max);
    118    ASSERT_EQ(SECSuccess, rv);
    119  }
    120 
    121  void CreateDummySocket(std::shared_ptr<DummyPrSocket>* dummy_socket,
    122                         ScopedPRFileDesc* ssl_fd) {
    123    (*dummy_socket).reset(new DummyPrSocket("dummy", variant_));
    124    *ssl_fd = (*dummy_socket)->CreateFD();
    125    if (variant_ == ssl_variant_stream) {
    126      SSL_ImportFD(nullptr, ssl_fd->get());
    127    } else {
    128      DTLS_ImportFD(nullptr, ssl_fd->get());
    129    }
    130  }
    131 
    132  bool GetOverlap(const SSLVersionRange& r1, const SSLVersionRange& r2,
    133                  SSLVersionRange* overlap) {
    134    if (r1.min == SSL_LIBRARY_VERSION_NONE ||
    135        r1.max == SSL_LIBRARY_VERSION_NONE ||
    136        r2.min == SSL_LIBRARY_VERSION_NONE ||
    137        r2.max == SSL_LIBRARY_VERSION_NONE) {
    138      return false;
    139    }
    140 
    141    SSLVersionRange temp;
    142    temp.min = PR_MAX(r1.min, r2.min);
    143    temp.max = PR_MIN(r1.max, r2.max);
    144 
    145    if (temp.min > temp.max) {
    146      return false;
    147    }
    148 
    149    *overlap = temp;
    150    return true;
    151  }
    152 
    153  bool IsValidInputForVersionRangeSet(SSLVersionRange* expectedEffectiveRange) {
    154    if (input_.min() <= SSL_LIBRARY_VERSION_3_0 &&
    155        input_.max() >= SSL_LIBRARY_VERSION_TLS_1_3) {
    156      // This is always invalid input, independent of policy
    157      return false;
    158    }
    159 
    160    if (input_.min() < library_.min() || input_.max() > library_.max() ||
    161        input_.min() > input_.max()) {
    162      // Asking for unsupported ranges is invalid input for VersionRangeSet
    163      // APIs, regardless of overlap.
    164      return false;
    165    }
    166 
    167    SSLVersionRange overlap_with_library;
    168    if (!GetOverlap(input_.range(), library_.range(), &overlap_with_library)) {
    169      return false;
    170    }
    171 
    172    SSLVersionRange overlap_with_library_and_policy;
    173    if (!GetOverlap(overlap_with_library, policy_.range(),
    174                    &overlap_with_library_and_policy)) {
    175      return false;
    176    }
    177 
    178    RemoveConflictingVersions(variant_, &overlap_with_library_and_policy);
    179    *expectedEffectiveRange = overlap_with_library_and_policy;
    180    return true;
    181  }
    182 
    183  void RemoveConflictingVersions(SSLProtocolVariant variant,
    184                                 SSLVersionRange* r) {
    185    ASSERT_TRUE(r != nullptr);
    186    if (r->max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
    187        r->min < SSL_LIBRARY_VERSION_TLS_1_0) {
    188      r->min = SSL_LIBRARY_VERSION_TLS_1_0;
    189    }
    190  }
    191 
    192  void SetUp() override {
    193    TlsConnectTestBase::SetUp();
    194    SetPolicy(policy_.range());
    195  }
    196 
    197  void TearDown() override {
    198    TlsConnectTestBase::TearDown();
    199    saved_version_policy_.RestoreOriginalPolicy();
    200  }
    201 
    202 protected:
    203  class VersionPolicy {
    204   public:
    205    VersionPolicy() { SaveOriginalPolicy(); }
    206 
    207    void RestoreOriginalPolicy() {
    208      SECStatus rv;
    209      rv = NSS_OptionSet(NSS_TLS_VERSION_MIN_POLICY, saved_min_tls_);
    210      ASSERT_EQ(SECSuccess, rv);
    211      rv = NSS_OptionSet(NSS_TLS_VERSION_MAX_POLICY, saved_max_tls_);
    212      ASSERT_EQ(SECSuccess, rv);
    213      rv = NSS_OptionSet(NSS_DTLS_VERSION_MIN_POLICY, saved_min_dtls_);
    214      ASSERT_EQ(SECSuccess, rv);
    215      rv = NSS_OptionSet(NSS_DTLS_VERSION_MAX_POLICY, saved_max_dtls_);
    216      ASSERT_EQ(SECSuccess, rv);
    217    }
    218 
    219   private:
    220    void SaveOriginalPolicy() {
    221      SECStatus rv;
    222      rv = NSS_OptionGet(NSS_TLS_VERSION_MIN_POLICY, &saved_min_tls_);
    223      ASSERT_EQ(SECSuccess, rv);
    224      rv = NSS_OptionGet(NSS_TLS_VERSION_MAX_POLICY, &saved_max_tls_);
    225      ASSERT_EQ(SECSuccess, rv);
    226      rv = NSS_OptionGet(NSS_DTLS_VERSION_MIN_POLICY, &saved_min_dtls_);
    227      ASSERT_EQ(SECSuccess, rv);
    228      rv = NSS_OptionGet(NSS_DTLS_VERSION_MAX_POLICY, &saved_max_dtls_);
    229      ASSERT_EQ(SECSuccess, rv);
    230    }
    231 
    232    int32_t saved_min_tls_;
    233    int32_t saved_max_tls_;
    234    int32_t saved_min_dtls_;
    235    int32_t saved_max_dtls_;
    236  };
    237 
    238  VersionPolicy saved_version_policy_;
    239 
    240  SSLProtocolVariant variant_;
    241  const VersionRangeWithLabel policy_;
    242  const VersionRangeWithLabel input_;
    243  const VersionRangeWithLabel library_;
    244 };
    245 
    246 static const uint16_t kExpandedVersionsArr[] = {
    247    /* clang-format off */
    248    SSL_LIBRARY_VERSION_3_0 - 1,
    249    SSL_LIBRARY_VERSION_3_0,
    250    SSL_LIBRARY_VERSION_TLS_1_0,
    251    SSL_LIBRARY_VERSION_TLS_1_1,
    252    SSL_LIBRARY_VERSION_TLS_1_2,
    253 #ifndef NSS_DISABLE_TLS_1_3
    254    SSL_LIBRARY_VERSION_TLS_1_3,
    255 #endif
    256    SSL_LIBRARY_VERSION_MAX_SUPPORTED + 1
    257    /* clang-format on */
    258 };
    259 static ::testing::internal::ParamGenerator<uint16_t> kExpandedVersions =
    260    ::testing::ValuesIn(kExpandedVersionsArr);
    261 
    262 TEST_P(TestPolicyVersionRange, TestAllTLSVersionsAndPolicyCombinations) {
    263  ASSERT_TRUE(variant_ == ssl_variant_stream ||
    264              variant_ == ssl_variant_datagram)
    265      << "testing unsupported ssl variant";
    266 
    267  std::cerr << "testing: " << variant_ << policy_ << input_ << library_
    268            << std::endl;
    269 
    270  SSLVersionRange supported_range;
    271  SECStatus rv = SSL_VersionRangeGetSupported(variant_, &supported_range);
    272  VersionRangeWithLabel supported("SSL_VersionRangeGetSupported",
    273                                  supported_range);
    274 
    275  std::cerr << supported << std::endl;
    276 
    277  std::shared_ptr<DummyPrSocket> dummy_socket;
    278  ScopedPRFileDesc ssl_fd;
    279  CreateDummySocket(&dummy_socket, &ssl_fd);
    280 
    281  SECStatus rv_socket;
    282  SSLVersionRange overlap_policy_and_lib;
    283  if (!GetOverlap(policy_.range(), library_.range(), &overlap_policy_and_lib)) {
    284    EXPECT_EQ(SECFailure, rv)
    285        << "expected SSL_VersionRangeGetSupported to fail with invalid policy";
    286 
    287    SSLVersionRange enabled_range;
    288    rv = SSL_VersionRangeGetDefault(variant_, &enabled_range);
    289    EXPECT_EQ(SECFailure, rv)
    290        << "expected SSL_VersionRangeGetDefault to fail with invalid policy";
    291 
    292    SSLVersionRange enabled_range_on_socket;
    293    rv_socket = SSL_VersionRangeGet(ssl_fd.get(), &enabled_range_on_socket);
    294    EXPECT_EQ(SECFailure, rv_socket)
    295        << "expected SSL_VersionRangeGet to fail with invalid policy";
    296 
    297    ConnectExpectFail();
    298    return;
    299  }
    300 
    301  EXPECT_EQ(SECSuccess, rv)
    302      << "expected SSL_VersionRangeGetSupported to succeed with valid policy";
    303 
    304  EXPECT_TRUE(supported_range.min != SSL_LIBRARY_VERSION_NONE &&
    305              supported_range.max != SSL_LIBRARY_VERSION_NONE)
    306      << "expected SSL_VersionRangeGetSupported to return real values with "
    307         "valid policy";
    308 
    309  RemoveConflictingVersions(variant_, &overlap_policy_and_lib);
    310  VersionRangeWithLabel overlap_info("overlap", overlap_policy_and_lib);
    311 
    312  EXPECT_TRUE(supported_range == overlap_policy_and_lib)
    313      << "expected range from GetSupported to be identical with calculated "
    314         "overlap "
    315      << overlap_info;
    316 
    317  // We don't know which versions are "enabled by default" by the library,
    318  // therefore we don't know if there's overlap between the default
    319  // and the policy, and therefore, we don't if TLS connections should
    320  // be successful or fail in this combination.
    321  // Therefore we don't test if we can connect, without having configured a
    322  // version range explicitly.
    323 
    324  // Now start testing with supplied input.
    325 
    326  SSLVersionRange expected_effective_range;
    327  bool is_valid_input =
    328      IsValidInputForVersionRangeSet(&expected_effective_range);
    329 
    330  SSLVersionRange temp_input = input_.range();
    331  rv = SSL_VersionRangeSetDefault(variant_, &temp_input);
    332  rv_socket = SSL_VersionRangeSet(ssl_fd.get(), &temp_input);
    333 
    334  if (!is_valid_input) {
    335    EXPECT_EQ(SECFailure, rv)
    336        << "expected failure return from SSL_VersionRangeSetDefault";
    337 
    338    EXPECT_EQ(SECFailure, rv_socket)
    339        << "expected failure return from SSL_VersionRangeSet";
    340    return;
    341  }
    342 
    343  EXPECT_EQ(SECSuccess, rv)
    344      << "expected successful return from SSL_VersionRangeSetDefault";
    345 
    346  EXPECT_EQ(SECSuccess, rv_socket)
    347      << "expected successful return from SSL_VersionRangeSet";
    348 
    349  SSLVersionRange effective;
    350  SSLVersionRange effective_socket;
    351 
    352  rv = SSL_VersionRangeGetDefault(variant_, &effective);
    353  EXPECT_EQ(SECSuccess, rv)
    354      << "expected successful return from SSL_VersionRangeGetDefault";
    355 
    356  rv_socket = SSL_VersionRangeGet(ssl_fd.get(), &effective_socket);
    357  EXPECT_EQ(SECSuccess, rv_socket)
    358      << "expected successful return from SSL_VersionRangeGet";
    359 
    360  VersionRangeWithLabel expected_info("expectation", expected_effective_range);
    361  VersionRangeWithLabel effective_info("effectively-enabled", effective);
    362 
    363  EXPECT_TRUE(expected_effective_range == effective)
    364      << "range returned by SSL_VersionRangeGetDefault doesn't match "
    365         "expectation: "
    366      << expected_info << effective_info;
    367 
    368  EXPECT_TRUE(expected_effective_range == effective_socket)
    369      << "range returned by SSL_VersionRangeGet doesn't match "
    370         "expectation: "
    371      << expected_info << effective_info;
    372 
    373  // Because we found overlap between policy and supported versions,
    374  // and because we have used SetDefault to enable at least one version,
    375  // it should be possible to execute an SSL/TLS connection.
    376  Connect();
    377 }
    378 
    379 INSTANTIATE_TEST_SUITE_P(TLSVersionRanges, TestPolicyVersionRange,
    380                         ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
    381                                            kExpandedVersions,
    382                                            kExpandedVersions,
    383                                            kExpandedVersions,
    384                                            kExpandedVersions));
    385 }  // namespace nss_test