diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlslite/tlslite/handshakesettings.py index 7998e2e..2e9e06d 100644 --- a/third_party/tlslite/tlslite/handshakesettings.py +++ b/third_party/tlslite/tlslite/handshakesettings.py @@ -19,6 +19,7 @@ ALL_MAC_NAMES = ["sha", "sha256", "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 @@ -91,6 +92,21 @@ class HandshakeSettings(object): version, a protocol_version alert will be signalled. The default is (3,3). (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. @@ -108,6 +124,8 @@ class HandshakeSettings(object): self.certificateTypes = CERTIFICATE_TYPES self.minVersion = (3,1) self.maxVersion = (3,3) + self.tlsIntolerant = None + self.tlsIntoleranceType = 'alert' self.useExperimentalTackExtension = False # Validates the min/max fields, and certificateTypes @@ -123,6 +141,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"] @@ -164,6 +184,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 f8547d5..4f3ba1c 100644 --- a/third_party/tlslite/tlslite/tlsconnection.py +++ b/third_party/tlslite/tlslite/tlsconnection.py @@ -1073,7 +1073,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. @@ -1147,11 +1147,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 @@ -1183,7 +1178,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 @@ -1195,7 +1190,6 @@ class TLSConnection(TLSRecordLayer): reqCAs=None, reqCertTypes=None, tacks=None, activationFlags=0, nextProtos=None, anon=False, - tlsIntolerant=None, signedCertTimestamps=None, fallbackSCSV=False, ocspResponse=None @@ -1218,7 +1212,6 @@ class TLSConnection(TLSRecordLayer): reqCAs=reqCAs, reqCertTypes=reqCertTypes, tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon, - tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse) @@ -1231,7 +1224,7 @@ class TLSConnection(TLSRecordLayer): settings, reqCAs, reqCertTypes, tacks, activationFlags, nextProtos, anon, - tlsIntolerant, signedCertTimestamps, fallbackSCSV, + signedCertTimestamps, fallbackSCSV, ocspResponse): self._handshakeStart(client=False) @@ -1269,7 +1262,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) @@ -1384,7 +1377,7 @@ class TLSConnection(TLSRecordLayer): def _serverGetClientHello(self, settings, certChain, verifierDB, - sessionCache, anon, tlsIntolerant, fallbackSCSV): + sessionCache, anon, fallbackSCSV): #Initialize acceptable cipher suites cipherSuites = [] if verifierDB: @@ -1421,11 +1414,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 3584726..eda11e6 100644 --- a/third_party/tlslite/tlslite/tlsrecordlayer.py +++ b/third_party/tlslite/tlslite/tlsrecordlayer.py @@ -20,6 +20,7 @@ from .constants import * from .utils.cryptomath import getRandomBytes import socket +import struct import errno import traceback @@ -527,6 +528,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: