aboutsummaryrefslogtreecommitdiffstats
path: root/netlib
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2015-11-04 21:33:32 +0100
committerMaximilian Hils <git@maximilianhils.com>2015-11-04 21:33:32 +0100
commit810c2f2414f706aee92fd3ac749998c1c492bbf5 (patch)
tree99020599d4112f6014c6968ef648cb801572d21d /netlib
parent9d12425d5ee942ee3d954a9324c31b74f466d520 (diff)
parent5af9df326aef1cf72be7fd5390df239fb6b906c7 (diff)
downloadmitmproxy-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.py5
-rw-r--r--netlib/tcp.py37
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)