From a34a4831843d5745e200c1df58672973a57aebb0 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 2 Mar 2014 15:14:22 +1300 Subject: Adapt for new pathod and netlib APIs. --- libmproxy/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index b6480822..0203ba86 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -48,7 +48,7 @@ class ProxyConfig: self.forward_proxy = forward_proxy self.transparent_proxy = transparent_proxy self.authenticator = authenticator - self.certstore = certutils.CertStore() + self.certstore = certutils.CertStore(cacert) class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject): @@ -422,7 +422,7 @@ class ConnectionHandler: host = upstream_cert.cn.decode("utf8").encode("idna") sans = upstream_cert.altnames - ret = self.config.certstore.get_cert(host, sans, self.config.cacert) + ret = self.config.certstore.get_cert(host, sans) if not ret: raise ProxyError(502, "Unable to generate dummy cert.") return ret -- cgit v1.2.3 From f373ac5b6c443d0e633323e39b846fbe78822c2c Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 2 Mar 2014 17:27:24 +1300 Subject: Improve explicit certificate specification - Support cert/key in the same PEM file - Rationalize arguments, expand tests, clean up a bit --- libmproxy/proxy.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 0203ba86..9ff8887d 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -4,6 +4,7 @@ from netlib import tcp, http, certutils, http_auth import utils, version, platform, controller, stateobject TRANSPARENT_SSL_PORTS = [443, 8443] +CA_CERT_NAME = "mitmproxy-ca.pem" class AddressPriority(object): @@ -37,9 +38,10 @@ class Log: class ProxyConfig: - def __init__(self, certfile=None, cacert=None, clientcerts=None, no_upstream_cert=False, body_size_limit=None, + def __init__(self, certfile=None, keyfile=None, cacert=None, clientcerts=None, no_upstream_cert=False, body_size_limit=None, reverse_proxy=None, forward_proxy=None, transparent_proxy=None, authenticator=None): self.certfile = certfile + self.keyfile = keyfile self.cacert = cacert self.clientcerts = clientcerts self.no_upstream_cert = no_upstream_cert @@ -381,7 +383,7 @@ class ConnectionHandler: if self.client_conn.ssl_established: raise ProxyError(502, "SSL to Client already established.") dummycert = self.find_cert() - self.client_conn.convert_to_ssl(dummycert, self.config.certfile or self.config.cacert, + self.client_conn.convert_to_ssl(dummycert, self.config.keyfile or self.config.cacert, handle_sni=self.handle_sni) def server_reconnect(self, no_ssl=False): @@ -498,12 +500,17 @@ class DummyServer: # Command-line utils -def certificate_option_group(parser): +def ssl_option_group(parser): group = parser.add_argument_group("SSL") group.add_argument( - "--cert", action="store", - type=str, dest="cert", default=None, - help="User-created SSL certificate file." + "--certfile", action="store", + type=str, dest="certfile", default=None, + help="SSL certificate in PEM format, optionally with the key in the same file." + ) + group.add_argument( + "--keyfile", action="store", + type=str, dest="keyfile", default=None, + help="Key matching certfile." ) group.add_argument( "--client-certs", action="store", @@ -513,12 +520,20 @@ def certificate_option_group(parser): def process_proxy_options(parser, options): - if options.cert: - options.cert = os.path.expanduser(options.cert) - if not os.path.exists(options.cert): - return parser.error("Manually created certificate does not exist: %s" % options.cert) + if options.certfile: + options.certfile = os.path.expanduser(options.certfile) + if not os.path.exists(options.certfile): + return parser.error("Certificate file does not exist: %s" % options.certfile) + + if options.keyfile: + options.keyfile = os.path.expanduser(options.keyfile) + if not os.path.exists(options.keyfile): + return parser.error("Key file does not exist: %s" % options.keyfile) + + if options.certfile and not options.keyfile: + options.keyfile = options.certfile - cacert = os.path.join(options.confdir, "mitmproxy-ca.pem") + cacert = os.path.join(options.confdir, CA_CERT_NAME) cacert = os.path.expanduser(cacert) if not os.path.exists(cacert): certutils.dummy_ca(cacert) @@ -575,7 +590,8 @@ def process_proxy_options(parser, options): authenticator = http_auth.NullProxyAuth(None) return ProxyConfig( - certfile=options.cert, + certfile=options.certfile, + keyfile=options.keyfile, cacert=cacert, clientcerts=options.clientcerts, body_size_limit=body_size_limit, -- cgit v1.2.3 From 875f5f8cb65a254c40816e7cda0e4be96384ac16 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 2 Mar 2014 17:35:41 +1300 Subject: Cipher specification. --- libmproxy/proxy.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 9ff8887d..b787386a 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -38,8 +38,12 @@ class Log: class ProxyConfig: - def __init__(self, certfile=None, keyfile=None, cacert=None, clientcerts=None, no_upstream_cert=False, body_size_limit=None, - reverse_proxy=None, forward_proxy=None, transparent_proxy=None, authenticator=None): + def __init__(self, certfile=None, keyfile=None, cacert=None, clientcerts=None, + no_upstream_cert=False, body_size_limit=None, reverse_proxy=None, + forward_proxy=None, transparent_proxy=None, authenticator=None, + ciphers=None + ): + self.ciphers = ciphers self.certfile = certfile self.keyfile = keyfile self.cacert = cacert @@ -383,8 +387,13 @@ class ConnectionHandler: if self.client_conn.ssl_established: raise ProxyError(502, "SSL to Client already established.") dummycert = self.find_cert() - self.client_conn.convert_to_ssl(dummycert, self.config.keyfile or self.config.cacert, - handle_sni=self.handle_sni) + print self.config.ciphers + self.client_conn.convert_to_ssl( + dummycert, + self.config.keyfile or self.config.cacert, + handle_sni = self.handle_sni, + cipher_list = self.config.ciphers + ) def server_reconnect(self, no_ssl=False): address = self.server_conn.address @@ -517,6 +526,11 @@ def ssl_option_group(parser): type=str, dest="clientcerts", default=None, help="Client certificate directory." ) + group.add_argument( + "--ciphers", action="store", + type=str, dest="ciphers", default=None, + help="SSL cipher specification." + ) def process_proxy_options(parser, options): @@ -599,5 +613,6 @@ def process_proxy_options(parser, options): reverse_proxy=rp, forward_proxy=fp, transparent_proxy=trans, - authenticator=authenticator + authenticator=authenticator, + ciphers=options.ciphers, ) -- cgit v1.2.3 From 32af66881465ae98a53665c8ddd42c02aaf492f7 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 2 Mar 2014 22:15:53 +1300 Subject: Minor cleanups. --- libmproxy/proxy.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index b787386a..b0fb9449 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -387,7 +387,6 @@ class ConnectionHandler: if self.client_conn.ssl_established: raise ProxyError(502, "SSL to Client already established.") dummycert = self.find_cert() - print self.config.ciphers self.client_conn.convert_to_ssl( dummycert, self.config.keyfile or self.config.cacert, @@ -469,7 +468,6 @@ class ProxyServerError(Exception): class ProxyServer(tcp.TCPServer): allow_reuse_address = True bound = True - def __init__(self, config, port, host='', server_version=version.NAMEVERSION): """ Raises ProxyServerError if there's a startup problem. -- cgit v1.2.3 From d65f2215cb9191a24b36ad6a4fcbf474798d3b2d Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Wed, 5 Mar 2014 17:25:12 +1300 Subject: Much more sophisticated cert handling - Specify per-domain certificates and keys - Certs are no longer regenerated for SANs - And more. :) --- libmproxy/proxy.py | 98 +++++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 56 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index b0fb9449..1eebba07 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -4,9 +4,12 @@ from netlib import tcp, http, certutils, http_auth import utils, version, platform, controller, stateobject TRANSPARENT_SSL_PORTS = [443, 8443] +CONF_BASENAME = "mitmproxy" +CONF_DIR = "~/.mitmproxy" CA_CERT_NAME = "mitmproxy-ca.pem" + class AddressPriority(object): """ Enum that signifies the priority of the given address when choosing the destination host. @@ -38,15 +41,12 @@ class Log: class ProxyConfig: - def __init__(self, certfile=None, keyfile=None, cacert=None, clientcerts=None, + def __init__(self, confdir=CONF_DIR, clientcerts=None, no_upstream_cert=False, body_size_limit=None, reverse_proxy=None, forward_proxy=None, transparent_proxy=None, authenticator=None, - ciphers=None + ciphers=None, certs=None ): self.ciphers = ciphers - self.certfile = certfile - self.keyfile = keyfile - self.cacert = cacert self.clientcerts = clientcerts self.no_upstream_cert = no_upstream_cert self.body_size_limit = body_size_limit @@ -54,7 +54,9 @@ class ProxyConfig: self.forward_proxy = forward_proxy self.transparent_proxy = transparent_proxy self.authenticator = authenticator - self.certstore = certutils.CertStore(cacert) + self.confdir = os.path.expanduser(confdir) + self.certstore = certutils.CertStore.from_store(confdir, CONF_BASENAME) + class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject): @@ -386,10 +388,9 @@ class ConnectionHandler: if client: if self.client_conn.ssl_established: raise ProxyError(502, "SSL to Client already established.") - dummycert = self.find_cert() + cert, key = self.find_cert() self.client_conn.convert_to_ssl( - dummycert, - self.config.keyfile or self.config.cacert, + cert, key, handle_sni = self.handle_sni, cipher_list = self.config.ciphers ) @@ -420,22 +421,18 @@ class ConnectionHandler: self.channel.tell("log", Log(msg)) def find_cert(self): - if self.config.certfile: - with open(self.config.certfile, "rb") as f: - return certutils.SSLCert.from_pem(f.read()) - 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 + 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): """ @@ -451,9 +448,9 @@ class ConnectionHandler: self.server_reconnect() # reconnect to upstream server with SNI # Now, change client context to reflect changed certificate: new_context = SSL.Context(SSL.TLSv1_METHOD) - new_context.use_privatekey_file(self.config.certfile or self.config.cacert) - dummycert = self.find_cert() - new_context.use_certificate(dummycert.x509) + cert, key = self.find_cert() + new_context.use_privatekey_file(key) + new_context.use_certificate(cert.X509) connection.set_context(new_context) # An unhandled exception in this method will core dump PyOpenSSL, so # make dang sure it doesn't happen. @@ -510,14 +507,12 @@ class DummyServer: def ssl_option_group(parser): group = parser.add_argument_group("SSL") group.add_argument( - "--certfile", action="store", - type=str, dest="certfile", default=None, - help="SSL certificate in PEM format, optionally with the key in the same file." - ) - group.add_argument( - "--keyfile", action="store", - type=str, dest="keyfile", default=None, - help="Key matching certfile." + "--cert", dest='certs', default=[], type=str, + metavar = "SPEC", action="append", + help='Add an SSL certificate. SPEC is of the form "[domain=]path". '\ + 'The domain may include a wildcard, and is equal to "*" if not specified. '\ + 'The file at path is a certificate in PEM format. If a private key is included in the PEM, '\ + 'it is used, else the default key in the conf dir is used. Can be passed multiple times.' ) group.add_argument( "--client-certs", action="store", @@ -532,23 +527,6 @@ def ssl_option_group(parser): def process_proxy_options(parser, options): - if options.certfile: - options.certfile = os.path.expanduser(options.certfile) - if not os.path.exists(options.certfile): - return parser.error("Certificate file does not exist: %s" % options.certfile) - - if options.keyfile: - options.keyfile = os.path.expanduser(options.keyfile) - if not os.path.exists(options.keyfile): - return parser.error("Key file does not exist: %s" % options.keyfile) - - if options.certfile and not options.keyfile: - options.keyfile = options.certfile - - cacert = os.path.join(options.confdir, CA_CERT_NAME) - cacert = os.path.expanduser(cacert) - if not os.path.exists(cacert): - certutils.dummy_ca(cacert) body_size_limit = utils.parse_size(options.body_size_limit) if options.reverse_proxy and options.transparent_proxy: return parser.error("Can't set both reverse proxy and transparent proxy.") @@ -601,10 +579,17 @@ def process_proxy_options(parser, options): else: authenticator = http_auth.NullProxyAuth(None) + certs = [] + for i in options.certs: + parts = i.split("=", 1) + if len(parts) == 1: + parts = ["*", parts[0]] + parts[1] = os.path.expanduser(parts[1]) + if not os.path.exists(parts[1]): + parser.error("Certificate file does not exist: %s"%parts[1]) + certs.append(parts) + return ProxyConfig( - certfile=options.certfile, - keyfile=options.keyfile, - cacert=cacert, clientcerts=options.clientcerts, body_size_limit=body_size_limit, no_upstream_cert=options.no_upstream_cert, @@ -613,4 +598,5 @@ def process_proxy_options(parser, options): transparent_proxy=trans, authenticator=authenticator, ciphers=options.ciphers, + certs = certs, ) -- cgit v1.2.3 From d0e6fa270533689fdaca0daab2d9dfa209bdfc26 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Fri, 7 Mar 2014 15:20:15 +1300 Subject: Use the right conf dir... --- libmproxy/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 1eebba07..6dd37752 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -55,7 +55,7 @@ class ProxyConfig: self.transparent_proxy = transparent_proxy self.authenticator = authenticator self.confdir = os.path.expanduser(confdir) - self.certstore = certutils.CertStore.from_store(confdir, CONF_BASENAME) + self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME) -- cgit v1.2.3