From e89e035d4a2c52aff77fdb3eaa01e8bdb1539a17 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Tue, 11 Mar 2014 13:02:10 +1300 Subject: [PATCH] Certificate forwarding. --- libmproxy/proxy/config.py | 28 ++++++++++++++++++---------- libmproxy/proxy/server.py | 32 ++++++++++++++++++-------------- test/test_server.py | 7 +++++++ test/tservers.py | 2 ++ 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index cf382dc76a..ed0787daa2 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -13,7 +13,7 @@ class ProxyConfig: def __init__(self, confdir=CONF_DIR, clientcerts=None, no_upstream_cert=False, body_size_limit=None, get_upstream_server=None, http_form_in="absolute", http_form_out="relative", authenticator=None, - ciphers=None, certs=None + ciphers=None, certs=None, certforward = False ): self.ciphers = ciphers self.clientcerts = clientcerts @@ -25,6 +25,7 @@ def __init__(self, confdir=CONF_DIR, clientcerts=None, self.authenticator = authenticator self.confdir = os.path.expanduser(confdir) self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME) + self.certforward = certforward def process_proxy_options(parser, options): @@ -93,15 +94,17 @@ def process_proxy_options(parser, options): certs.append(parts) return ProxyConfig( - clientcerts=options.clientcerts, - body_size_limit=body_size_limit, - no_upstream_cert=options.no_upstream_cert, - get_upstream_server=get_upstream_server, - http_form_in=http_form_in, - http_form_out=http_form_out, - authenticator=authenticator, - ciphers=options.ciphers, + clientcerts = options.clientcerts, + body_size_limit = body_size_limit, + no_upstream_cert = options.no_upstream_cert, + get_upstream_server = get_upstream_server, + confdir = options.confdir, + http_form_in = http_form_in, + http_form_out = http_form_out, + authenticator = authenticator, + ciphers = options.ciphers, certs = certs, + certforward = options.certforward, ) @@ -124,4 +127,9 @@ def ssl_option_group(parser): "--ciphers", action="store", type=str, dest="ciphers", default=None, help="SSL cipher specification." - ) \ No newline at end of file + ) + group.add_argument( + "--cert-forward", action="store_true", + dest="certforward", default=False, + help="Simply forward SSL certificates from upstream." + ) diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index a5b95fb7b6..4723104eb4 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -188,7 +188,8 @@ def establish_ssl(self, client=False, server=False): self.client_conn.convert_to_ssl( cert, key, handle_sni = self.handle_sni, - cipher_list = self.config.ciphers + cipher_list = self.config.ciphers, + dhparams = self.config.certstore.dhparams ) def server_reconnect(self, no_ssl=False): @@ -217,18 +218,21 @@ def log(self, msg, subs=()): self.channel.tell("log", Log(msg)) def find_cert(self): - host = self.server_conn.address.host - sans = [] - if not self.config.no_upstream_cert or not self.server_conn.ssl_established: - upstream_cert = self.server_conn.cert - if upstream_cert.cn: - host = upstream_cert.cn.decode("utf8").encode("idna") - sans = upstream_cert.altnames - - ret = self.config.certstore.get_cert(host, sans) - if not ret: - raise ProxyError(502, "Unable to generate dummy cert.") - return ret + if self.config.certforward and self.server_conn.ssl_established: + return self.server_conn.cert, self.config.certstore.gen_pkey(self.server_conn.cert) + else: + host = self.server_conn.address.host + sans = [] + if not self.config.no_upstream_cert or not self.server_conn.ssl_established: + upstream_cert = self.server_conn.cert + if upstream_cert.cn: + host = upstream_cert.cn.decode("utf8").encode("idna") + sans = upstream_cert.altnames + + ret = self.config.certstore.get_cert(host, sans) + if not ret: + raise ProxyError(502, "Unable to generate dummy cert.") + return ret def handle_sni(self, connection): """ @@ -251,4 +255,4 @@ def handle_sni(self, connection): # An unhandled exception in this method will core dump PyOpenSSL, so # make dang sure it doesn't happen. except Exception, e: # pragma: no cover - pass \ No newline at end of file + pass diff --git a/test/test_server.py b/test/test_server.py index 43ef546d12..71ca39fc0b 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -390,3 +390,10 @@ class TestIncompleteResponse(tservers.HTTPProxTest): def test_incomplete(self): assert self.pathod("200").status_code == 502 + +class TestCertForward(tservers.HTTPProxTest): + certforward = True + ssl = True + def test_app_err(self): + tutils.raises("handshake error", self.pathod, "200:b@100") + diff --git a/test/tservers.py b/test/tservers.py index 9ad3b735de..7548ebc4d4 100644 --- a/test/tservers.py +++ b/test/tservers.py @@ -81,6 +81,7 @@ class ProxTestBase(object): authenticator = None masterclass = TestMaster externalapp = False + certforward = False @classmethod def setupAll(cls): cls.server = libpathod.test.Daemon(ssl=cls.ssl, ssloptions=cls.ssloptions) @@ -91,6 +92,7 @@ def setupAll(cls): no_upstream_cert = cls.no_upstream_cert, confdir = cls.confdir, authenticator = cls.authenticator, + certforward = cls.certforward, **pconf ) tmaster = cls.masterclass(config)