diff options
author | Maximilian Hils <git@maximilianhils.com> | 2015-11-04 21:33:32 +0100 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2015-11-04 21:33:32 +0100 |
commit | 810c2f2414f706aee92fd3ac749998c1c492bbf5 (patch) | |
tree | 99020599d4112f6014c6968ef648cb801572d21d /netlib | |
parent | 9d12425d5ee942ee3d954a9324c31b74f466d520 (diff) | |
parent | 5af9df326aef1cf72be7fd5390df239fb6b906c7 (diff) | |
download | mitmproxy-810c2f2414f706aee92fd3ac749998c1c492bbf5.tar.gz mitmproxy-810c2f2414f706aee92fd3ac749998c1c492bbf5.tar.bz2 mitmproxy-810c2f2414f706aee92fd3ac749998c1c492bbf5.zip |
Merge remote-tracking branch 'origin/hostname-validation'
Diffstat (limited to 'netlib')
-rw-r--r-- | netlib/certutils.py | 5 | ||||
-rw-r--r-- | netlib/tcp.py | 37 |
2 files changed, 35 insertions, 7 deletions
diff --git a/netlib/certutils.py b/netlib/certutils.py index 69530245..e6d71c39 100644 --- a/netlib/certutils.py +++ b/netlib/certutils.py @@ -438,6 +438,11 @@ class SSLCert(object): @property def altnames(self): + """ + Returns: + All DNS altnames. + """ + # tcp.TCPClient.convert_to_ssl assumes that this property only contains DNS altnames for hostname verification. altnames = [] for i in range(self.x509.get_extension_count()): ext = self.x509.get_extension(i) diff --git a/netlib/tcp.py b/netlib/tcp.py index ef5ab4b6..8e46d4f6 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -11,6 +11,7 @@ import binascii from six.moves import range import certifi +from backports import ssl_match_hostname import six import OpenSSL from OpenSSL import SSL @@ -595,9 +596,14 @@ class TCPClient(_Connection): ca_path: Path to a directory of trusted CA certificates prepared using the c_rehash tool ca_pemfile: Path to a PEM formatted trusted CA certificate """ + verification_mode = sslctx_kwargs.get('verify_options', None) + if verification_mode == SSL.VERIFY_PEER and not sni: + raise TlsException("Cannot validate certificate hostname without SNI") + context = self.create_ssl_context( alpn_protos=alpn_protos, - **sslctx_kwargs) + **sslctx_kwargs + ) self.connection = SSL.Connection(context, self.connection) if sni: self.sni = sni @@ -610,15 +616,32 @@ class TCPClient(_Connection): raise InvalidCertificateException("SSL handshake error: %s" % repr(v)) else: raise TlsException("SSL handshake error: %s" % repr(v)) + else: + # Fix for pre v1.0 OpenSSL, which doesn't throw an exception on + # certificate validation failure + if verification_mode == SSL.VERIFY_PEER and self.ssl_verification_error is not None: + raise InvalidCertificateException("SSL handshake error: certificate verify failed") - # Fix for pre v1.0 OpenSSL, which doesn't throw an exception on - # certificate validation failure - verification_mode = sslctx_kwargs.get('verify_options', None) - if self.ssl_verification_error is not None and verification_mode == SSL.VERIFY_PEER: - raise InvalidCertificateException("SSL handshake error: certificate verify failed") + self.cert = certutils.SSLCert(self.connection.get_peer_certificate()) + + # Validate TLS Hostname + try: + crt = dict( + subjectAltName=[("DNS", x.decode("ascii", "strict")) for x in self.cert.altnames] + ) + if self.cert.cn: + crt["subject"] = [[["commonName", self.cert.cn.decode("ascii", "strict")]]] + if sni: + hostname = sni.decode("ascii", "strict") + else: + hostname = "no-hostname" + ssl_match_hostname.match_hostname(crt, hostname) + except (ValueError, ssl_match_hostname.CertificateError) as e: + self.ssl_verification_error = dict(depth=0, errno="Invalid Hostname") + if verification_mode == SSL.VERIFY_PEER: + raise InvalidCertificateException("Presented certificate for {} is not valid: {}".format(sni, str(e))) self.ssl_established = True - self.cert = certutils.SSLCert(self.connection.get_peer_certificate()) self.rfile.set_descriptor(self.connection) self.wfile.set_descriptor(self.connection) |