diff options
author | Maximilian Hils <git@maximilianhils.com> | 2019-11-23 01:16:16 +0100 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2019-11-23 18:02:45 +0100 |
commit | e768f5ba83dd92d380f3e03da5da6c68edb3f45b (patch) | |
tree | 7f0c0e5f662da9043f4c5233de6fe9264217bbed /mitmproxy | |
parent | 0f868e992489eee54aadd7afc9a2a7212385f9be (diff) | |
download | mitmproxy-e768f5ba83dd92d380f3e03da5da6c68edb3f45b.tar.gz mitmproxy-e768f5ba83dd92d380f3e03da5da6c68edb3f45b.tar.bz2 mitmproxy-e768f5ba83dd92d380f3e03da5da6c68edb3f45b.zip |
use OpenSSL's hostname validation
Diffstat (limited to 'mitmproxy')
-rw-r--r-- | mitmproxy/net/tls.py | 51 |
1 files changed, 21 insertions, 30 deletions
diff --git a/mitmproxy/net/tls.py b/mitmproxy/net/tls.py index d68a008f..2c9f6c85 100644 --- a/mitmproxy/net/tls.py +++ b/mitmproxy/net/tls.py @@ -7,14 +7,13 @@ import os import struct import threading import typing -from ssl import match_hostname, CertificateError import certifi from OpenSSL import SSL from kaitaistruct import KaitaiStream -import mitmproxy.options # noqa -from mitmproxy import exceptions, certs +import mitmproxy.options +from mitmproxy import certs, exceptions from mitmproxy.contrib.kaitaistruct import tls_client_hello from mitmproxy.net import check @@ -237,33 +236,11 @@ def create_client_context( depth: int, is_cert_verified: bool ) -> bool: - if is_cert_verified and depth == 0: - # Verify hostname of leaf certificate. - cert = certs.Cert(x509) - try: - crt: typing.Dict[str, typing.Any] = dict( - subjectAltName=[("DNS", x.decode("ascii", "strict")) for x in cert.altnames] - ) - if cert.cn: - crt["subject"] = [[["commonName", cert.cn.decode("ascii", "strict")]]] - if sni: - # SNI hostnames allow support of IDN by using ASCII-Compatible Encoding - # Conversion algorithm is in RFC 3490 which is implemented by idna codec - # https://docs.python.org/3/library/codecs.html#text-encodings - # https://tools.ietf.org/html/rfc6066#section-3 - # https://tools.ietf.org/html/rfc4985#section-3 - hostname = sni.encode("idna").decode("ascii") - else: - hostname = "no-hostname" - match_hostname(crt, hostname) - except (ValueError, CertificateError) as e: - conn.cert_error = exceptions.InvalidCertificateException( - "Certificate verification error for {}: {}".format( - sni or repr(address), - str(e) - ) - ) - is_cert_verified = False + if is_cert_verified and depth == 0 and not sni: + conn.cert_error = exceptions.InvalidCertificateException( + f"Certificate verification error for {address}: Cannot validate hostname, SNI missing." + ) + is_cert_verified = False elif is_cert_verified: pass else: @@ -285,6 +262,20 @@ def create_client_context( **sslctx_kwargs, ) + if sni: + # Manually enable hostname verification on the context object. + # https://wiki.openssl.org/index.php/Hostname_validation + param = SSL._lib.SSL_CTX_get0_param(context._context) + # Matching on the CN is disabled in both Chrome and Firefox, so we disable it, too. + # https://www.chromestatus.com/feature/4981025180483584 + SSL._lib.X509_VERIFY_PARAM_set_hostflags( + param, + SSL._lib.X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | SSL._lib.X509_CHECK_FLAG_NEVER_CHECK_SUBJECT + ) + SSL._openssl_assert( + SSL._lib.X509_VERIFY_PARAM_set1_host(param, sni.encode("idna"), 0) == 1 + ) + # Client Certs if cert: try: |