diff options
author | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-27 01:16:55 +0000 |
---|---|---|
committer | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-27 01:16:55 +0000 |
commit | 48d2b7c54479f23c092bbaf923fd47db3b8e2f91 (patch) | |
tree | 203c77e451dc34b3b4639e5bc06875697d62f3c9 | |
parent | c13eaf685337e162971078b2e4bb0ba90103ce5b (diff) | |
download | chromium_src-48d2b7c54479f23c092bbaf923fd47db3b8e2f91.zip chromium_src-48d2b7c54479f23c092bbaf923fd47db3b8e2f91.tar.gz chromium_src-48d2b7c54479f23c092bbaf923fd47db3b8e2f91.tar.bz2 |
Add tests for TLS fallback on connection reset and close.
The reset tests currently fail on OpenSSL and do not work on Android. But
otherwise this gives us slightly better test coverage here.
BUG=372849
Review URL: https://codereview.chromium.org/342793003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280188 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/test/spawned_test_server/base_test_server.cc | 19 | ||||
-rw-r--r-- | net/test/spawned_test_server/base_test_server.h | 12 | ||||
-rwxr-xr-x | net/tools/testserver/testserver.py | 18 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 247 | ||||
-rw-r--r-- | third_party/tlslite/README.chromium | 2 | ||||
-rw-r--r-- | third_party/tlslite/patches/intolerance_options.patch | 192 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/handshakesettings.py | 24 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/tlsconnection.py | 35 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/tlsrecordlayer.py | 8 |
9 files changed, 430 insertions, 127 deletions
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc index 016c930..2ee9adc 100644 --- a/net/test/spawned_test_server/base_test_server.cc +++ b/net/test/spawned_test_server/base_test_server.cc @@ -72,6 +72,21 @@ void GetCiphersList(int cipher, base::ListValue* values) { values->Append(new base::StringValue("3des")); } +base::StringValue* GetTLSIntoleranceType( + BaseTestServer::SSLOptions::TLSIntoleranceType type) { + switch (type) { + case BaseTestServer::SSLOptions::TLS_INTOLERANCE_ALERT: + return new base::StringValue("alert"); + case BaseTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE: + return new base::StringValue("close"); + case BaseTestServer::SSLOptions::TLS_INTOLERANCE_RESET: + return new base::StringValue("reset"); + default: + NOTREACHED(); + return new base::StringValue(""); + } +} + } // namespace BaseTestServer::SSLOptions::SSLOptions() @@ -83,6 +98,7 @@ BaseTestServer::SSLOptions::SSLOptions() bulk_ciphers(SSLOptions::BULK_CIPHER_ANY), record_resume(false), tls_intolerant(TLS_INTOLERANT_NONE), + tls_intolerance_type(TLS_INTOLERANCE_ALERT), fallback_scsv_enabled(false), staple_ocsp_response(false), enable_npn(false) {} @@ -97,6 +113,7 @@ BaseTestServer::SSLOptions::SSLOptions( bulk_ciphers(SSLOptions::BULK_CIPHER_ANY), record_resume(false), tls_intolerant(TLS_INTOLERANT_NONE), + tls_intolerance_type(TLS_INTOLERANCE_ALERT), fallback_scsv_enabled(false), staple_ocsp_response(false), enable_npn(false) {} @@ -437,6 +454,8 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const { if (ssl_options_.tls_intolerant != SSLOptions::TLS_INTOLERANT_NONE) { arguments->Set("tls-intolerant", new base::FundamentalValue(ssl_options_.tls_intolerant)); + arguments->Set("tls-intolerance-type", GetTLSIntoleranceType( + ssl_options_.tls_intolerance_type)); } if (ssl_options_.fallback_scsv_enabled) arguments->Set("fallback-scsv", base::Value::CreateNullValue()); diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h index 5c47ebc..a5e3287 100644 --- a/net/test/spawned_test_server/base_test_server.h +++ b/net/test/spawned_test_server/base_test_server.h @@ -111,6 +111,14 @@ class BaseTestServer { TLS_INTOLERANT_TLS1_2 = 3, // Intolerant of TLS 1.2 or higher. }; + // Values which control how the server reacts in response to a ClientHello + // it is intolerant of. + enum TLSIntoleranceType { + TLS_INTOLERANCE_ALERT = 0, // Send a handshake_failure alert. + TLS_INTOLERANCE_CLOSE = 1, // Close the connection. + TLS_INTOLERANCE_RESET = 2, // Send a TCP reset. + }; + // Initialize a new SSLOptions using CERT_OK as the certificate. SSLOptions(); @@ -171,6 +179,10 @@ class BaseTestServer { // negotiates an intolerant TLS version in order to test version fallback. TLSIntolerantLevel tls_intolerant; + // If |tls_intolerant| is not TLS_INTOLERANT_NONE, how the server reacts to + // an intolerant TLS version. + TLSIntoleranceType tls_intolerance_type; + // fallback_scsv_enabled, if true, causes the server to process the // TLS_FALLBACK_SCSV cipher suite. This cipher suite is sent by Chrome // when performing TLS version fallback in response to an SSL handshake diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 33faf46..83920d8 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -154,7 +154,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, def __init__(self, server_address, request_hander_class, pem_cert_and_key, ssl_client_auth, ssl_client_cas, ssl_client_cert_types, ssl_bulk_ciphers, ssl_key_exchanges, enable_npn, - record_resume_info, tls_intolerant, signed_cert_timestamps, + record_resume_info, tls_intolerant, + tls_intolerance_type, signed_cert_timestamps, fallback_scsv_enabled, ocsp_response): self.cert_chain = tlslite.api.X509CertChain() self.cert_chain.parsePemList(pem_cert_and_key) @@ -172,10 +173,6 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, self.next_protos = ['http/1.1'] else: self.next_protos = None - if tls_intolerant == 0: - self.tls_intolerant = None - else: - self.tls_intolerant = (3, tls_intolerant) self.signed_cert_timestamps = signed_cert_timestamps self.fallback_scsv_enabled = fallback_scsv_enabled self.ocsp_response = ocsp_response @@ -199,6 +196,9 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers if ssl_key_exchanges is not None: self.ssl_handshake_settings.keyExchangeNames = ssl_key_exchanges + if tls_intolerant != 0: + self.ssl_handshake_settings.tlsIntolerant = (3, tls_intolerant) + self.ssl_handshake_settings.tlsIntoleranceType = tls_intolerance_type if record_resume_info: # If record_resume_info is true then we'll replace the session cache with @@ -223,7 +223,6 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, reqCAs=self.ssl_client_cas, reqCertTypes=self.ssl_client_cert_types, nextProtos=self.next_protos, - tlsIntolerant=self.tls_intolerant, signedCertTimestamps= self.signed_cert_timestamps, fallbackSCSV=self.fallback_scsv_enabled, @@ -1982,6 +1981,7 @@ class ServerRunner(testserver_base.TestServerRunner): self.options.enable_npn, self.options.record_resume, self.options.tls_intolerant, + self.options.tls_intolerance_type, self.options.signed_cert_timestamps_tls_ext.decode( "base64"), self.options.fallback_scsv, @@ -2128,6 +2128,12 @@ class ServerRunner(testserver_base.TestServerRunner): 'aborted. 2 means TLS 1.1 or higher will be ' 'aborted. 3 means TLS 1.2 or higher will be ' 'aborted.') + self.option_parser.add_option('--tls-intolerance-type', + dest='tls_intolerance_type', + default="alert", + help='Controls how the server reacts to a ' + 'TLS version it is intolerant to. Valid ' + 'values are "alert", "close", and "reset".') self.option_parser.add_option('--signed-cert-timestamps-tls-ext', dest='signed_cert_timestamps_tls_ext', default='', diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 16eda0e..c770041 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -6571,83 +6571,6 @@ TEST_F(HTTPSRequestTest, HTTPSExpiredTest) { } } -// Tests TLSv1.1 -> TLSv1 fallback. Verifies that we don't fall back more -// than necessary. -TEST_F(HTTPSRequestTest, TLSv1Fallback) { - // The OpenSSL library in use may not support TLS 1.1. -#if !defined(USE_OPENSSL) - EXPECT_GT(kDefaultSSLVersionMax, SSL_PROTOCOL_VERSION_TLS1); -#endif - if (kDefaultSSLVersionMax <= SSL_PROTOCOL_VERSION_TLS1) - return; - - SpawnedTestServer::SSLOptions ssl_options( - SpawnedTestServer::SSLOptions::CERT_OK); - ssl_options.tls_intolerant = - SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; - SpawnedTestServer test_server( - SpawnedTestServer::TYPE_HTTPS, - ssl_options, - base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); - ASSERT_TRUE(test_server.Start()); - - TestDelegate d; - TestURLRequestContext context(true); - context.Init(); - d.set_allow_certificate_errors(true); - URLRequest r( - test_server.GetURL(std::string()), DEFAULT_PRIORITY, &d, &context); - r.Start(); - - base::RunLoop().Run(); - - EXPECT_EQ(1, d.response_started_count()); - EXPECT_NE(0, d.bytes_received()); - EXPECT_EQ(static_cast<int>(SSL_CONNECTION_VERSION_TLS1), - SSLConnectionStatusToVersion(r.ssl_info().connection_status)); - EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_VERSION_FALLBACK); -} - -// Tests that we don't fallback with servers that implement TLS_FALLBACK_SCSV. -#if defined(USE_OPENSSL) -TEST_F(HTTPSRequestTest, DISABLED_FallbackSCSV) { -#else -TEST_F(HTTPSRequestTest, FallbackSCSV) { -#endif - SpawnedTestServer::SSLOptions ssl_options( - SpawnedTestServer::SSLOptions::CERT_OK); - // Configure HTTPS server to be intolerant of TLS >= 1.0 in order to trigger - // a version fallback. - ssl_options.tls_intolerant = - SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; - // Have the server process TLS_FALLBACK_SCSV so that version fallback - // connections are rejected. - ssl_options.fallback_scsv_enabled = true; - - SpawnedTestServer test_server( - SpawnedTestServer::TYPE_HTTPS, - ssl_options, - base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); - ASSERT_TRUE(test_server.Start()); - - TestDelegate d; - TestURLRequestContext context(true); - context.Init(); - d.set_allow_certificate_errors(true); - URLRequest r( - test_server.GetURL(std::string()), DEFAULT_PRIORITY, &d, &context); - r.Start(); - - base::RunLoop().Run(); - - EXPECT_EQ(1, d.response_started_count()); - // ERR_SSL_VERSION_OR_CIPHER_MISMATCH is how the server simulates version - // intolerance. If the fallback SCSV is processed when the original error - // that caused the fallback should be returned, which should be - // ERR_SSL_VERSION_OR_CIPHER_MISMATCH. - EXPECT_EQ(ERR_SSL_VERSION_OR_CIPHER_MISMATCH, r.status().error()); -} - // This tests that a load of www.google.com with a certificate error sets // the |certificate_errors_are_fatal| flag correctly. This flag will cause // the interstitial to be fatal. @@ -6824,34 +6747,6 @@ TEST_F(HTTPSRequestTest, HSTSPreservesPosts) { TestLoadTimingCacheHitNoNetwork(load_timing_info); } -TEST_F(HTTPSRequestTest, SSLv3Fallback) { - SpawnedTestServer::SSLOptions ssl_options( - SpawnedTestServer::SSLOptions::CERT_OK); - ssl_options.tls_intolerant = - SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; - SpawnedTestServer test_server( - SpawnedTestServer::TYPE_HTTPS, - ssl_options, - base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); - ASSERT_TRUE(test_server.Start()); - - TestDelegate d; - TestURLRequestContext context(true); - context.Init(); - d.set_allow_certificate_errors(true); - URLRequest r( - test_server.GetURL(std::string()), DEFAULT_PRIORITY, &d, &context); - r.Start(); - - base::RunLoop().Run(); - - EXPECT_EQ(1, d.response_started_count()); - EXPECT_NE(0, d.bytes_received()); - EXPECT_EQ(static_cast<int>(SSL_CONNECTION_VERSION_SSL3), - SSLConnectionStatusToVersion(r.ssl_info().connection_status)); - EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_VERSION_FALLBACK); -} - namespace { class SSLClientAuthTestDelegate : public TestDelegate { @@ -7075,6 +6970,148 @@ TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) { } } +class HTTPSFallbackTest : public testing::Test { + public: + HTTPSFallbackTest() : context_(true) { + context_.Init(); + delegate_.set_allow_certificate_errors(true); + } + virtual ~HTTPSFallbackTest() {} + + protected: + void DoFallbackTest(const SpawnedTestServer::SSLOptions& ssl_options) { + DCHECK(!request_); + SpawnedTestServer test_server( + SpawnedTestServer::TYPE_HTTPS, + ssl_options, + base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + request_.reset(new URLRequest( + test_server.GetURL(std::string()), DEFAULT_PRIORITY, + &delegate_, &context_)); + request_->Start(); + + base::RunLoop().Run(); + } + + void ExpectConnection(int version) { + EXPECT_EQ(1, delegate_.response_started_count()); + EXPECT_NE(0, delegate_.bytes_received()); + EXPECT_EQ(version, SSLConnectionStatusToVersion( + request_->ssl_info().connection_status)); + EXPECT_TRUE(request_->ssl_info().connection_status & + SSL_CONNECTION_VERSION_FALLBACK); + } + + void ExpectFailure(int error) { + EXPECT_EQ(1, delegate_.response_started_count()); + EXPECT_FALSE(request_->status().is_success()); + EXPECT_EQ(URLRequestStatus::FAILED, request_->status().status()); + EXPECT_EQ(error, request_->status().error()); + } + + private: + TestDelegate delegate_; + TestURLRequestContext context_; + scoped_ptr<URLRequest> request_; +}; + +// Tests TLSv1.1 -> TLSv1 fallback. Verifies that we don't fall back more +// than necessary. +TEST_F(HTTPSFallbackTest, TLSv1Fallback) { + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + ExpectConnection(SSL_CONNECTION_VERSION_TLS1); +} + +// This test is disabled on Android because the remote test server doesn't cause +// a TCP reset. +#if !defined(OS_ANDROID) +// Tests fallback to TLS 1.0 on connection reset. +TEST_F(HTTPSFallbackTest, TLSv1FallbackReset) { + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; + ssl_options.tls_intolerance_type = + SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_RESET; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + ExpectConnection(SSL_CONNECTION_VERSION_TLS1); +} +#endif // !OS_ANDROID + +// Tests that we don't fallback with servers that implement TLS_FALLBACK_SCSV. +#if defined(USE_OPENSSL) +TEST_F(HTTPSFallbackTest, DISABLED_FallbackSCSV) { +#else +TEST_F(HTTPSFallbackTest, FallbackSCSV) { +#endif + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + // Configure HTTPS server to be intolerant of TLS >= 1.0 in order to trigger + // a version fallback. + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; + // Have the server process TLS_FALLBACK_SCSV so that version fallback + // connections are rejected. + ssl_options.fallback_scsv_enabled = true; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + + // ERR_SSL_VERSION_OR_CIPHER_MISMATCH is how the server simulates version + // intolerance. If the fallback SCSV is processed when the original error + // that caused the fallback should be returned, which should be + // ERR_SSL_VERSION_OR_CIPHER_MISMATCH. + ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); +} + +// Tests that the SSLv3 fallback triggers on alert. +TEST_F(HTTPSFallbackTest, SSLv3Fallback) { + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + ExpectConnection(SSL_CONNECTION_VERSION_SSL3); +} + +// Tests that the SSLv3 fallback triggers on closed connections. +TEST_F(HTTPSFallbackTest, SSLv3FallbackClosed) { + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; + ssl_options.tls_intolerance_type = + SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + ExpectConnection(SSL_CONNECTION_VERSION_SSL3); +} + +// This test is disabled on Android because the remote test server doesn't cause +// a TCP reset. It also does not pass on OpenSSL. https://crbug.com/372849 +#if !defined(OS_ANDROID) && !defined(USE_OPENSSL) +// Tests that a reset connection does not fallback down to SSL3. +TEST_F(HTTPSFallbackTest, SSLv3NoFallbackReset) { + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_OK); + ssl_options.tls_intolerant = + SpawnedTestServer::SSLOptions::TLS_INTOLERANT_ALL; + ssl_options.tls_intolerance_type = + SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_RESET; + + ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); + ExpectFailure(ERR_CONNECTION_RESET); +} +#endif // !OS_ANDROID && !USE_OPENSSL + class HTTPSSessionTest : public testing::Test { public: HTTPSSessionTest() : default_context_(true) { diff --git a/third_party/tlslite/README.chromium b/third_party/tlslite/README.chromium index 5487bfc8..ab0b941 100644 --- a/third_party/tlslite/README.chromium +++ b/third_party/tlslite/README.chromium @@ -35,3 +35,5 @@ Local Modifications: certificate_types field of CertificateRequest. - patches/ignore_write_failure.patch: Don't invalidate sessions on write failures. +- patches/intolerance_options.patch: Add an option to further control + simulated TLS version intolerance. diff --git a/third_party/tlslite/patches/intolerance_options.patch b/third_party/tlslite/patches/intolerance_options.patch new file mode 100644 index 0000000..0bee1d6 --- /dev/null +++ b/third_party/tlslite/patches/intolerance_options.patch @@ -0,0 +1,192 @@ +diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlslite/tlslite/handshakesettings.py +index e0bc0e6..0d4ccf2 100644 +--- a/third_party/tlslite/tlslite/handshakesettings.py ++++ b/third_party/tlslite/tlslite/handshakesettings.py +@@ -18,6 +18,7 @@ ALL_MAC_NAMES = ["sha", "md5"] + KEY_EXCHANGE_NAMES = ["rsa", "dhe_rsa", "srp_sha", "srp_sha_rsa", "dh_anon"] + CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"] + CERTIFICATE_TYPES = ["x509"] ++TLS_INTOLERANCE_TYPES = ["alert", "close", "reset"] + + class HandshakeSettings(object): + """This class encapsulates various parameters that can be used with +@@ -92,6 +93,21 @@ class HandshakeSettings(object): + The default is (3,2). (WARNING: Some servers may (improperly) + reject clients which offer support for TLS 1.1. In this case, + try lowering maxVersion to (3,1)). ++ ++ @type tlsIntolerant: tuple ++ @ivar tlsIntolerant: The TLS ClientHello version which the server ++ simulates intolerance of. ++ ++ If tlsIntolerant is not None, the server will simulate TLS version ++ intolerance by aborting the handshake in response to all TLS versions ++ tlsIntolerant or higher. ++ ++ @type tlsIntoleranceType: str ++ @ivar tlsIntoleranceType: How the server should react when simulating TLS ++ intolerance. ++ ++ The allowed values are "alert" (return a fatal handshake_failure alert), ++ "close" (abruptly close the connection), and "reset" (send a TCP reset). + + @type useExperimentalTackExtension: bool + @ivar useExperimentalTackExtension: Whether to enabled TACK support. +@@ -109,6 +125,8 @@ class HandshakeSettings(object): + self.certificateTypes = CERTIFICATE_TYPES + self.minVersion = (3,0) + self.maxVersion = (3,2) ++ self.tlsIntolerant = None ++ self.tlsIntoleranceType = 'alert' + self.useExperimentalTackExtension = False + + # Validates the min/max fields, and certificateTypes +@@ -124,6 +142,8 @@ class HandshakeSettings(object): + other.certificateTypes = self.certificateTypes + other.minVersion = self.minVersion + other.maxVersion = self.maxVersion ++ other.tlsIntolerant = self.tlsIntolerant ++ other.tlsIntoleranceType = self.tlsIntoleranceType + + if not cipherfactory.tripleDESPresent: + other.cipherNames = [e for e in self.cipherNames if e != "3des"] +@@ -165,6 +185,10 @@ class HandshakeSettings(object): + if s not in CERTIFICATE_TYPES: + raise ValueError("Unknown certificate type: '%s'" % s) + ++ if other.tlsIntoleranceType not in TLS_INTOLERANCE_TYPES: ++ raise ValueError( ++ "Unknown TLS intolerance type: '%s'" % other.tlsIntoleranceType) ++ + if other.minVersion > other.maxVersion: + raise ValueError("Versions set incorrectly") + +diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py +index 044ad59..7c1572f 100644 +--- a/third_party/tlslite/tlslite/tlsconnection.py ++++ b/third_party/tlslite/tlslite/tlsconnection.py +@@ -1065,7 +1065,7 @@ class TLSConnection(TLSRecordLayer): + reqCAs = None, reqCertTypes = None, + tacks=None, activationFlags=0, + nextProtos=None, anon=False, +- tlsIntolerant=None, signedCertTimestamps=None, ++ signedCertTimestamps=None, + fallbackSCSV=False, ocspResponse=None): + """Perform a handshake in the role of server. + +@@ -1139,11 +1139,6 @@ class TLSConnection(TLSRecordLayer): + clients through the Next-Protocol Negotiation Extension, + if they support it. + +- @type tlsIntolerant: (int, int) or None +- @param tlsIntolerant: If tlsIntolerant is not None, the server will +- simulate TLS version intolerance by returning a fatal handshake_failure +- alert to all TLS versions tlsIntolerant or higher. +- + @type signedCertTimestamps: str + @param signedCertTimestamps: A SignedCertificateTimestampList (as a + binary 8-bit string) that will be sent as a TLS extension whenever +@@ -1175,7 +1170,7 @@ class TLSConnection(TLSRecordLayer): + certChain, privateKey, reqCert, sessionCache, settings, + checker, reqCAs, reqCertTypes, + tacks=tacks, activationFlags=activationFlags, +- nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, ++ nextProtos=nextProtos, anon=anon, + signedCertTimestamps=signedCertTimestamps, + fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): + pass +@@ -1187,7 +1182,6 @@ class TLSConnection(TLSRecordLayer): + reqCAs=None, reqCertTypes=None, + tacks=None, activationFlags=0, + nextProtos=None, anon=False, +- tlsIntolerant=None, + signedCertTimestamps=None, + fallbackSCSV=False, + ocspResponse=None +@@ -1210,7 +1204,6 @@ class TLSConnection(TLSRecordLayer): + reqCAs=reqCAs, reqCertTypes=reqCertTypes, + tacks=tacks, activationFlags=activationFlags, + nextProtos=nextProtos, anon=anon, +- tlsIntolerant=tlsIntolerant, + signedCertTimestamps=signedCertTimestamps, + fallbackSCSV=fallbackSCSV, + ocspResponse=ocspResponse) +@@ -1223,7 +1216,7 @@ class TLSConnection(TLSRecordLayer): + settings, reqCAs, reqCertTypes, + tacks, activationFlags, + nextProtos, anon, +- tlsIntolerant, signedCertTimestamps, fallbackSCSV, ++ signedCertTimestamps, fallbackSCSV, + ocspResponse): + + self._handshakeStart(client=False) +@@ -1261,7 +1254,7 @@ class TLSConnection(TLSRecordLayer): + # Handle ClientHello and resumption + for result in self._serverGetClientHello(settings, certChain,\ + verifierDB, sessionCache, +- anon, tlsIntolerant, fallbackSCSV): ++ anon, fallbackSCSV): + if result in (0,1): yield result + elif result == None: + self._handshakeDone(resumed=True) +@@ -1376,7 +1369,7 @@ class TLSConnection(TLSRecordLayer): + + + def _serverGetClientHello(self, settings, certChain, verifierDB, +- sessionCache, anon, tlsIntolerant, fallbackSCSV): ++ sessionCache, anon, fallbackSCSV): + #Initialize acceptable cipher suites + cipherSuites = [] + if verifierDB: +@@ -1413,11 +1406,21 @@ class TLSConnection(TLSRecordLayer): + yield result + + #If simulating TLS intolerance, reject certain TLS versions. +- elif (tlsIntolerant is not None and +- clientHello.client_version >= tlsIntolerant): +- for result in self._sendError(\ ++ elif (settings.tlsIntolerant is not None and ++ clientHello.client_version >= settings.tlsIntolerant): ++ if settings.tlsIntoleranceType == "alert": ++ for result in self._sendError(\ + AlertDescription.handshake_failure): +- yield result ++ yield result ++ elif settings.tlsIntoleranceType == "close": ++ self._abruptClose() ++ raise TLSUnsupportedError("Simulating version intolerance") ++ elif settings.tlsIntoleranceType == "reset": ++ self._abruptClose(reset=True) ++ raise TLSUnsupportedError("Simulating version intolerance") ++ else: ++ raise ValueError("Unknown intolerance type: '%s'" % ++ settings.tlsIntoleranceType) + + #If client's version is too high, propose my highest version + elif clientHello.client_version > settings.maxVersion: +diff --git a/third_party/tlslite/tlslite/tlsrecordlayer.py b/third_party/tlslite/tlslite/tlsrecordlayer.py +index 370dc9a..23c2a2f 100644 +--- a/third_party/tlslite/tlslite/tlsrecordlayer.py ++++ b/third_party/tlslite/tlslite/tlsrecordlayer.py +@@ -19,6 +19,7 @@ from .constants import * + from .utils.cryptomath import getRandomBytes + + import socket ++import struct + import errno + import traceback + +@@ -523,6 +524,13 @@ class TLSRecordLayer(object): + self._shutdown(False) + raise TLSLocalAlert(alert, errorStr) + ++ def _abruptClose(self, reset=False): ++ if reset: ++ #Set an SO_LINGER timeout of 0 to send a TCP RST. ++ self.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, ++ struct.pack('ii', 1, 0)) ++ self._shutdown(False) ++ + def _sendMsgs(self, msgs): + randomizeFirstBlock = True + for msg in msgs: diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlslite/tlslite/handshakesettings.py index e0bc0e6..0d4ccf2 100644 --- a/third_party/tlslite/tlslite/handshakesettings.py +++ b/third_party/tlslite/tlslite/handshakesettings.py @@ -18,6 +18,7 @@ ALL_MAC_NAMES = ["sha", "md5"] KEY_EXCHANGE_NAMES = ["rsa", "dhe_rsa", "srp_sha", "srp_sha_rsa", "dh_anon"] CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"] CERTIFICATE_TYPES = ["x509"] +TLS_INTOLERANCE_TYPES = ["alert", "close", "reset"] class HandshakeSettings(object): """This class encapsulates various parameters that can be used with @@ -92,6 +93,21 @@ class HandshakeSettings(object): The default is (3,2). (WARNING: Some servers may (improperly) reject clients which offer support for TLS 1.1. In this case, try lowering maxVersion to (3,1)). + + @type tlsIntolerant: tuple + @ivar tlsIntolerant: The TLS ClientHello version which the server + simulates intolerance of. + + If tlsIntolerant is not None, the server will simulate TLS version + intolerance by aborting the handshake in response to all TLS versions + tlsIntolerant or higher. + + @type tlsIntoleranceType: str + @ivar tlsIntoleranceType: How the server should react when simulating TLS + intolerance. + + The allowed values are "alert" (return a fatal handshake_failure alert), + "close" (abruptly close the connection), and "reset" (send a TCP reset). @type useExperimentalTackExtension: bool @ivar useExperimentalTackExtension: Whether to enabled TACK support. @@ -109,6 +125,8 @@ class HandshakeSettings(object): self.certificateTypes = CERTIFICATE_TYPES self.minVersion = (3,0) self.maxVersion = (3,2) + self.tlsIntolerant = None + self.tlsIntoleranceType = 'alert' self.useExperimentalTackExtension = False # Validates the min/max fields, and certificateTypes @@ -124,6 +142,8 @@ class HandshakeSettings(object): other.certificateTypes = self.certificateTypes other.minVersion = self.minVersion other.maxVersion = self.maxVersion + other.tlsIntolerant = self.tlsIntolerant + other.tlsIntoleranceType = self.tlsIntoleranceType if not cipherfactory.tripleDESPresent: other.cipherNames = [e for e in self.cipherNames if e != "3des"] @@ -165,6 +185,10 @@ class HandshakeSettings(object): if s not in CERTIFICATE_TYPES: raise ValueError("Unknown certificate type: '%s'" % s) + if other.tlsIntoleranceType not in TLS_INTOLERANCE_TYPES: + raise ValueError( + "Unknown TLS intolerance type: '%s'" % other.tlsIntoleranceType) + if other.minVersion > other.maxVersion: raise ValueError("Versions set incorrectly") diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py index 044ad59..7c1572f 100644 --- a/third_party/tlslite/tlslite/tlsconnection.py +++ b/third_party/tlslite/tlslite/tlsconnection.py @@ -1065,7 +1065,7 @@ class TLSConnection(TLSRecordLayer): reqCAs = None, reqCertTypes = None, tacks=None, activationFlags=0, nextProtos=None, anon=False, - tlsIntolerant=None, signedCertTimestamps=None, + signedCertTimestamps=None, fallbackSCSV=False, ocspResponse=None): """Perform a handshake in the role of server. @@ -1139,11 +1139,6 @@ class TLSConnection(TLSRecordLayer): clients through the Next-Protocol Negotiation Extension, if they support it. - @type tlsIntolerant: (int, int) or None - @param tlsIntolerant: If tlsIntolerant is not None, the server will - simulate TLS version intolerance by returning a fatal handshake_failure - alert to all TLS versions tlsIntolerant or higher. - @type signedCertTimestamps: str @param signedCertTimestamps: A SignedCertificateTimestampList (as a binary 8-bit string) that will be sent as a TLS extension whenever @@ -1175,7 +1170,7 @@ class TLSConnection(TLSRecordLayer): certChain, privateKey, reqCert, sessionCache, settings, checker, reqCAs, reqCertTypes, tacks=tacks, activationFlags=activationFlags, - nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, + nextProtos=nextProtos, anon=anon, signedCertTimestamps=signedCertTimestamps, fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): pass @@ -1187,7 +1182,6 @@ class TLSConnection(TLSRecordLayer): reqCAs=None, reqCertTypes=None, tacks=None, activationFlags=0, nextProtos=None, anon=False, - tlsIntolerant=None, signedCertTimestamps=None, fallbackSCSV=False, ocspResponse=None @@ -1210,7 +1204,6 @@ class TLSConnection(TLSRecordLayer): reqCAs=reqCAs, reqCertTypes=reqCertTypes, tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon, - tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse) @@ -1223,7 +1216,7 @@ class TLSConnection(TLSRecordLayer): settings, reqCAs, reqCertTypes, tacks, activationFlags, nextProtos, anon, - tlsIntolerant, signedCertTimestamps, fallbackSCSV, + signedCertTimestamps, fallbackSCSV, ocspResponse): self._handshakeStart(client=False) @@ -1261,7 +1254,7 @@ class TLSConnection(TLSRecordLayer): # Handle ClientHello and resumption for result in self._serverGetClientHello(settings, certChain,\ verifierDB, sessionCache, - anon, tlsIntolerant, fallbackSCSV): + anon, fallbackSCSV): if result in (0,1): yield result elif result == None: self._handshakeDone(resumed=True) @@ -1376,7 +1369,7 @@ class TLSConnection(TLSRecordLayer): def _serverGetClientHello(self, settings, certChain, verifierDB, - sessionCache, anon, tlsIntolerant, fallbackSCSV): + sessionCache, anon, fallbackSCSV): #Initialize acceptable cipher suites cipherSuites = [] if verifierDB: @@ -1413,11 +1406,21 @@ class TLSConnection(TLSRecordLayer): yield result #If simulating TLS intolerance, reject certain TLS versions. - elif (tlsIntolerant is not None and - clientHello.client_version >= tlsIntolerant): - for result in self._sendError(\ + elif (settings.tlsIntolerant is not None and + clientHello.client_version >= settings.tlsIntolerant): + if settings.tlsIntoleranceType == "alert": + for result in self._sendError(\ AlertDescription.handshake_failure): - yield result + yield result + elif settings.tlsIntoleranceType == "close": + self._abruptClose() + raise TLSUnsupportedError("Simulating version intolerance") + elif settings.tlsIntoleranceType == "reset": + self._abruptClose(reset=True) + raise TLSUnsupportedError("Simulating version intolerance") + else: + raise ValueError("Unknown intolerance type: '%s'" % + settings.tlsIntoleranceType) #If client's version is too high, propose my highest version elif clientHello.client_version > settings.maxVersion: diff --git a/third_party/tlslite/tlslite/tlsrecordlayer.py b/third_party/tlslite/tlslite/tlsrecordlayer.py index 370dc9a..23c2a2f 100644 --- a/third_party/tlslite/tlslite/tlsrecordlayer.py +++ b/third_party/tlslite/tlslite/tlsrecordlayer.py @@ -19,6 +19,7 @@ from .constants import * from .utils.cryptomath import getRandomBytes import socket +import struct import errno import traceback @@ -523,6 +524,13 @@ class TLSRecordLayer(object): self._shutdown(False) raise TLSLocalAlert(alert, errorStr) + def _abruptClose(self, reset=False): + if reset: + #Set an SO_LINGER timeout of 0 to send a TCP RST. + self.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, + struct.pack('ii', 1, 0)) + self._shutdown(False) + def _sendMsgs(self, msgs): randomizeFirstBlock = True for msg in msgs: |