aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/proxy
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-12-15 12:46:13 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-12-15 12:46:13 +0100
commit4e635d7a6fa8d437ab4dbf9125ba2ed9533dcf0a (patch)
tree1b3230fcee53bfb7e5a4757dbaa708a85a9955a2 /libmproxy/proxy
parent955c5c87a261ad4083e4b3f1579625a2b53eb912 (diff)
downloadmitmproxy-4e635d7a6fa8d437ab4dbf9125ba2ed9533dcf0a.tar.gz
mitmproxy-4e635d7a6fa8d437ab4dbf9125ba2ed9533dcf0a.tar.bz2
mitmproxy-4e635d7a6fa8d437ab4dbf9125ba2ed9533dcf0a.zip
allow specification of SSL version, only allow TLS1.0+ by default
Diffstat (limited to 'libmproxy/proxy')
-rw-r--r--libmproxy/proxy/config.py42
-rw-r--r--libmproxy/proxy/connection.py5
-rw-r--r--libmproxy/proxy/server.py40
3 files changed, 69 insertions, 18 deletions
diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py
index 3d373a28..84893323 100644
--- a/libmproxy/proxy/config.py
+++ b/libmproxy/proxy/config.py
@@ -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
)
@@ -197,6 +225,20 @@ def ssl_option_group(parser):
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",
help="Don't connect to upstream server to look up certificate details."
diff --git a/libmproxy/proxy/connection.py b/libmproxy/proxy/connection.py
index fd034e8b..1eeae16f 100644
--- a/libmproxy/proxy/connection.py
+++ b/libmproxy/proxy/connection.py
@@ -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):
diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py
index 55e2b30e..7562be89 100644
--- a/libmproxy/proxy/server.py
+++ b/libmproxy/proxy/server.py
@@ -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