mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-02 18:57:22 +00:00
allow specification of SSL version, only allow TLS1.0+ by default
This commit is contained in:
parent
955c5c87a2
commit
4e635d7a6f
4 changed files with 71 additions and 22 deletions
|
|
@ -188,7 +188,7 @@ class LiveConnection(object):
|
|||
(
|
||||
ssl != self.c.server_conn.ssl_established
|
||||
or
|
||||
(sni is not None and sni != self.c.sni)
|
||||
(sni is not None and sni != self.c.server_conn.sni)
|
||||
)
|
||||
)
|
||||
address_mismatch = (address != self.c.server_conn.address)
|
||||
|
|
@ -219,10 +219,8 @@ class LiveConnection(object):
|
|||
|
||||
self.c.set_server_address(address)
|
||||
self.c.establish_server_connection(ask=False)
|
||||
if sni:
|
||||
self.c.sni = sni
|
||||
if ssl:
|
||||
self.c.establish_ssl(server=True)
|
||||
self.c.establish_ssl(server=True, sni=sni)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import absolute_import
|
||||
import os
|
||||
import re
|
||||
from OpenSSL import SSL
|
||||
from netlib import http_auth, certutils, tcp
|
||||
from .. import utils, platform, version
|
||||
from .primitives import RegularProxyMode, TransparentProxyMode, UpstreamProxyMode, ReverseProxyMode, Socks5ProxyMode
|
||||
|
|
@ -47,6 +48,8 @@ class ProxyConfig:
|
|||
ciphers=None,
|
||||
certs=[],
|
||||
certforward=False,
|
||||
ssl_version_client="secure",
|
||||
ssl_version_server="secure",
|
||||
ssl_ports=TRANSPARENT_SSL_PORTS
|
||||
):
|
||||
self.host = host
|
||||
|
|
@ -80,9 +83,32 @@ class ProxyConfig:
|
|||
for spec, cert in certs:
|
||||
self.certstore.add_cert_file(spec, cert)
|
||||
self.certforward = certforward
|
||||
self.openssl_client_method, self.openssl_client_options = version_to_openssl(ssl_version_client)
|
||||
self.openssl_server_method, self.openssl_server_options = version_to_openssl(ssl_version_server)
|
||||
self.ssl_ports = ssl_ports
|
||||
|
||||
|
||||
sslversion_choices = ("all", "secure", "SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2")
|
||||
|
||||
|
||||
def version_to_openssl(version):
|
||||
"""
|
||||
Convert a reasonable SSL version specification into the format OpenSSL expects.
|
||||
Don't ask...
|
||||
https://bugs.launchpad.net/pyopenssl/+bug/1020632/comments/3
|
||||
"""
|
||||
if version == "all":
|
||||
return SSL.SSLv23_METHOD, None
|
||||
elif version == "secure":
|
||||
# SSLv23_METHOD + NO_SSLv2 + NO_SSLv3 == TLS 1.0+
|
||||
# TLSv1_METHOD would be TLS 1.0 only
|
||||
return SSL.SSLv23_METHOD, (SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
|
||||
elif version in sslversion_choices:
|
||||
return getattr(SSL, "%s_METHOD" % version), None
|
||||
else:
|
||||
raise ValueError("Invalid SSL version: %s" % version)
|
||||
|
||||
|
||||
def process_proxy_options(parser, options):
|
||||
body_size_limit = utils.parse_size(options.body_size_limit)
|
||||
|
||||
|
|
@ -165,6 +191,8 @@ def process_proxy_options(parser, options):
|
|||
ciphers=options.ciphers,
|
||||
certs=certs,
|
||||
certforward=options.certforward,
|
||||
ssl_version_client=options.ssl_version_client,
|
||||
ssl_version_server=options.ssl_version_server,
|
||||
ssl_ports=ssl_ports
|
||||
)
|
||||
|
||||
|
|
@ -196,6 +224,20 @@ def ssl_option_group(parser):
|
|||
dest="certforward", default=False,
|
||||
help="Simply forward SSL certificates from upstream."
|
||||
)
|
||||
group.add_argument(
|
||||
"--ssl-version-client", dest="ssl_version_client",
|
||||
default="secure", action="store",
|
||||
choices=sslversion_choices,
|
||||
help="Set supported SSL/TLS version for client connections. "
|
||||
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure."
|
||||
)
|
||||
group.add_argument(
|
||||
"--ssl-version-server", dest="ssl_version_server",
|
||||
default="secure", action="store",
|
||||
choices=sslversion_choices,
|
||||
help="Set supported SSL/TLS version for server connections. "
|
||||
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure."
|
||||
)
|
||||
group.add_argument(
|
||||
"--no-upstream-cert", default=False,
|
||||
action="store_true", dest="no_upstream_cert",
|
||||
|
|
|
|||
|
|
@ -144,13 +144,14 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
|
|||
self.wfile.write(message)
|
||||
self.wfile.flush()
|
||||
|
||||
def establish_ssl(self, clientcerts, sni):
|
||||
def establish_ssl(self, clientcerts, sni, **kwargs):
|
||||
clientcert = None
|
||||
if clientcerts:
|
||||
path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem"
|
||||
if os.path.exists(path):
|
||||
clientcert = path
|
||||
self.convert_to_ssl(cert=clientcert, sni=sni)
|
||||
self.convert_to_ssl(cert=clientcert, sni=sni, **kwargs)
|
||||
self.sni = sni
|
||||
self.timestamp_ssl_setup = utils.timestamp()
|
||||
|
||||
def finish(self):
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ class ConnectionHandler:
|
|||
self.channel = channel
|
||||
|
||||
self.conntype = "http"
|
||||
self.sni = None
|
||||
|
||||
def handle(self):
|
||||
try:
|
||||
|
|
@ -127,7 +126,6 @@ class ConnectionHandler:
|
|||
self.server_conn.address.port)])
|
||||
self.channel.tell("serverdisconnect", self)
|
||||
self.server_conn = None
|
||||
self.sni = None
|
||||
|
||||
def set_server_address(self, address):
|
||||
"""
|
||||
|
|
@ -165,7 +163,7 @@ class ConnectionHandler:
|
|||
except tcp.NetLibError, v:
|
||||
raise ProxyError(502, v)
|
||||
|
||||
def establish_ssl(self, client=False, server=False):
|
||||
def establish_ssl(self, client=False, server=False, sni=None):
|
||||
"""
|
||||
Establishes SSL on the existing connection(s) to the server or the client,
|
||||
as specified by the parameters.
|
||||
|
|
@ -177,7 +175,7 @@ class ConnectionHandler:
|
|||
if client:
|
||||
subs.append("with client")
|
||||
if server:
|
||||
subs.append("with server (sni: %s)" % self.sni)
|
||||
subs.append("with server (sni: %s)" % sni)
|
||||
self.log("Establish SSL", "debug", subs)
|
||||
|
||||
if server:
|
||||
|
|
@ -186,7 +184,12 @@ class ConnectionHandler:
|
|||
if self.server_conn.ssl_established:
|
||||
raise ProxyError(502, "SSL to Server already established.")
|
||||
try:
|
||||
self.server_conn.establish_ssl(self.config.clientcerts, self.sni)
|
||||
self.server_conn.establish_ssl(
|
||||
self.config.clientcerts,
|
||||
sni,
|
||||
method=self.config.openssl_server_method,
|
||||
options=self.config.openssl_server_options
|
||||
)
|
||||
except tcp.NetLibError as v:
|
||||
raise ProxyError(502, repr(v))
|
||||
if client:
|
||||
|
|
@ -196,6 +199,8 @@ class ConnectionHandler:
|
|||
try:
|
||||
self.client_conn.convert_to_ssl(
|
||||
cert, key,
|
||||
method=self.config.openssl_client_method,
|
||||
options=self.config.openssl_client_options,
|
||||
handle_sni=self.handle_sni,
|
||||
cipher_list=self.config.ciphers,
|
||||
dhparams=self.config.certstore.dhparams,
|
||||
|
|
@ -204,11 +209,11 @@ class ConnectionHandler:
|
|||
except tcp.NetLibError as v:
|
||||
raise ProxyError(400, repr(v))
|
||||
|
||||
def server_reconnect(self):
|
||||
def server_reconnect(self, new_sni=False):
|
||||
address = self.server_conn.address
|
||||
had_ssl = self.server_conn.ssl_established
|
||||
state = self.server_conn.state
|
||||
sni = self.sni
|
||||
sni = new_sni or self.server_conn.sni
|
||||
self.log("(server reconnect follows)", "debug")
|
||||
self.del_server_connection()
|
||||
self.set_server_address(address)
|
||||
|
|
@ -219,8 +224,7 @@ class ConnectionHandler:
|
|||
self.server_conn.state = state
|
||||
|
||||
if had_ssl:
|
||||
self.sni = sni
|
||||
self.establish_ssl(server=True)
|
||||
self.establish_ssl(server=True, sni=sni)
|
||||
|
||||
def finish(self):
|
||||
self.client_conn.finish()
|
||||
|
|
@ -245,8 +249,8 @@ class ConnectionHandler:
|
|||
if upstream_cert.cn:
|
||||
host = upstream_cert.cn.decode("utf8").encode("idna")
|
||||
sans = upstream_cert.altnames
|
||||
elif self.sni:
|
||||
sans = [self.sni]
|
||||
elif self.server_conn.sni:
|
||||
sans = [self.server_conn.sni]
|
||||
|
||||
ret = self.config.certstore.get_cert(host, sans)
|
||||
if not ret:
|
||||
|
|
@ -261,15 +265,19 @@ class ConnectionHandler:
|
|||
"""
|
||||
try:
|
||||
sn = connection.get_servername()
|
||||
if sn and sn != self.sni:
|
||||
self.sni = sn.decode("utf8").encode("idna")
|
||||
self.log("SNI received: %s" % self.sni, "debug")
|
||||
self.server_reconnect() # reconnect to upstream server with SNI
|
||||
if not sn:
|
||||
return
|
||||
sni = sn.decode("utf8").encode("idna")
|
||||
|
||||
if sni != self.server_conn.sni:
|
||||
self.log("SNI received: %s" % sni, "debug")
|
||||
self.server_reconnect(sni) # reconnect to upstream server with SNI
|
||||
# Now, change client context to reflect changed certificate:
|
||||
cert, key, chain_file = self.find_cert()
|
||||
new_context = self.client_conn._create_ssl_context(
|
||||
cert, key,
|
||||
method=SSL.TLSv1_METHOD,
|
||||
method=self.config.openssl_client_method,
|
||||
options=self.config.openssl_client_options,
|
||||
cipher_list=self.config.ciphers,
|
||||
dhparams=self.config.certstore.dhparams,
|
||||
chain_file=chain_file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue