From c2ae8285f4bc092a449d92f4bbdb150d586c2c51 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 20 Feb 2011 12:53:42 +1300 Subject: Revamp SSL configuration. - Move option parsing utiliities to proxy.py - Don't have a global config object. Pass it as an argument to ProxyServer. - Simplify certificate generation logic. --- libmproxy/proxy.py | 94 ++++++++++++++++++++++++++++++++++++++++-------------- libmproxy/utils.py | 58 ++++++--------------------------- 2 files changed, 79 insertions(+), 73 deletions(-) (limited to 'libmproxy') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index e3c692b9..f70fad25 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -6,11 +6,10 @@ Development started from Neil Schemenauer's munchy.py """ import sys, os, time, string, socket, urlparse, re, select, copy, base64 -import SocketServer, ssl +import optparse, SocketServer, ssl import utils, controller NAME = "mitmproxy" -config = None class ProxyError(Exception): @@ -495,7 +494,8 @@ class ServerConnection: class ProxyHandler(SocketServer.StreamRequestHandler): - def __init__(self, request, client_address, server, q): + def __init__(self, config, request, client_address, server, q): + self.config = config self.mqueue = q SocketServer.StreamRequestHandler.__init__(self, request, client_address, server) @@ -545,22 +545,14 @@ class ProxyHandler(SocketServer.StreamRequestHandler): if server: server.terminate() - def find_cert(self, host, port=443): - #return config.certpath + "/" + host + ":" + port + ".pem" - if config.certpath is not None: - cert = config.certpath + "/" + host + ".pem" - if not os.path.exists(cert) and config.cacert is not None: - utils.dummy_cert(config.certpath, config.cacert, host) - if os.path.exists(cert): - return cert - print >> sys.stderr, "WARNING: Certificate missing for %s:%d! (%s)\n" % (host, port, cert) - return config.certfile - - def find_key(self, host, port=443): - if config.cacert is not None: - return config.cacert + def find_cert(self, host): + if self.config.certfile: + return self.config.certfile else: - return config.certfile + ret = utils.dummy_cert(self.config.certpath, self.config.cacert, host) + if not ret: + raise ProxyError(400, "mitmproxy: Unable to generate dummy cert.") + return ret def read_request(self, client_conn): line = self.rfile.readline() @@ -583,14 +575,14 @@ class ProxyHandler(SocketServer.StreamRequestHandler): ) self.wfile.flush() kwargs = dict( - certfile = self.find_cert(host,port), - keyfile = self.find_key(host,port), + certfile = self.find_cert(host), + keyfile = self.config.certfile or self.config.cacert, server_side = True, ssl_version = ssl.PROTOCOL_SSLv23, do_handshake_on_connect = False ) if sys.version_info[1] > 6: - kwargs["ciphers"] = config.ciphers + kwargs["ciphers"] = self.config.ciphers self.connection = ssl.wrap_socket(self.connection, **kwargs) self.rfile = FileLike(self.connection) self.wfile = FileLike(self.connection) @@ -671,8 +663,8 @@ ServerBase.daemon_threads = True # Terminate workers when main thread ter class ProxyServer(ServerBase): request_queue_size = 20 allow_reuse_address = True - def __init__(self, port, address=''): - self.port, self.address = port, address + def __init__(self, config, port, address=''): + self.config, self.port, self.address = config, port, address ServerBase.__init__(self, (address, port), ProxyHandler) self.masterq = None @@ -680,8 +672,62 @@ class ProxyServer(ServerBase): self.masterq = q def finish_request(self, request, client_address): - self.RequestHandlerClass(request, client_address, self, self.masterq) + self.RequestHandlerClass(self.config, request, client_address, self, self.masterq) def shutdown(self): ServerBase.shutdown(self) + +# Command-line utils +def certificate_option_group(parser): + group = optparse.OptionGroup(parser, "SSL") + group.add_option( + "--cert", action="store", + type = "str", dest="cert", default=None, + help = "User-created SSL certificate file." + ) + group.add_option( + "--cacert", action="store", + type = "str", dest="cacert", default="~/.mitmproxy/ca.pem", + help = "SSL CA certificate file. Generated if it doesn't exist." + ) + group.add_option( + "--certpath", action="store", + type = "str", dest="certpath", default="~/.mitmproxy/", + help = "SSL certificate store path." + ) + group.add_option( + "--ciphers", action="store", + type = "str", dest="ciphers", default=None, + help = "SSL ciphers." + ) + parser.add_option_group(group) + + +def process_certificate_option_group(parser, options): + conf = {} + if options.cert: + options.cert = os.path.expanduser(options.cert) + if not os.path.exists(options.cert): + parser.error("Manually created certificate does not exist: %s"%options.cert) + if options.cacert: + options.cacert = os.path.expanduser(options.cacert) + if not os.path.exists(options.cacert): + dummy_ca(options.cacert) + if options.certpath: + options.certpath = os.path.expanduser(options.certpath) + elif options.cacert: + options.certpath = os.path.dirname(options.cacert) + + if getattr(options, "cache", None) is not None: + options.cache = os.path.expanduser(options.cache) + + return Config( + certfile = options.cert, + certpath = options.certpath, + cacert = options.cacert, + ciphers = options.ciphers + ) + + + diff --git a/libmproxy/utils.py b/libmproxy/utils.py index 51a8e871..ab861c55 100644 --- a/libmproxy/utils.py +++ b/libmproxy/utils.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re, os, subprocess, datetime, textwrap, errno, sys -import optparse def format_timestamp(s): d = datetime.datetime.fromtimestamp(s) @@ -355,11 +354,14 @@ def dummy_cert(certdir, ca, commonname): ca: Path to the certificate authority file, or None. commonname: Common name for the generated certificate. - Returns True if operation succeeded, False if not. + Returns cert path if operation succeeded, None if not. """ + certpath = os.path.join(certdir, commonname + ".pem") + if os.path.exists(certpath): + return certpath + confpath = os.path.join(certdir, commonname + ".cnf") reqpath = os.path.join(certdir, commonname + ".req") - certpath = os.path.join(certdir, commonname + ".pem") template = open(data.path("resources/cert.cnf")).read() f = open(confpath, "w").write(template%(dict(commonname=commonname))) @@ -381,7 +383,7 @@ def dummy_cert(certdir, ca, commonname): stdin=subprocess.PIPE ) if ret: - return False + return None cmd = [ "openssl", "x509", @@ -401,7 +403,7 @@ def dummy_cert(certdir, ca, commonname): stdin=subprocess.PIPE ) if ret: - return False + return None else: # Create a new selfsigned certificate + key cmd = [ @@ -423,8 +425,8 @@ def dummy_cert(certdir, ca, commonname): stdin=subprocess.PIPE ) if ret: - return False - return True + return None + return certpath def mkdir_p(path): @@ -437,45 +439,3 @@ def mkdir_p(path): raise -def certificate_option_group(parser): - group = optparse.OptionGroup(parser, "SSL") - group.add_option( - "--cert", action="store", - type = "str", dest="cert", default=None, - help = "SSL certificate file." - ) - group.add_option( - "--cacert", action="store", - type = "str", dest="cacert", default="~/.mitmproxy/ca.pem", - help = "SSL CA certificate file." - ) - group.add_option( - "--certpath", action="store", - type = "str", dest="certpath", default="~/.mitmproxy/", - help = "SSL certificate store path." - ) - group.add_option( - "--ciphers", action="store", - type = "str", dest="ciphers", default=None, - help = "SSL ciphers." - ) - parser.add_option_group(group) - - -def process_certificate_option_group(parser, options): - if options.cert is not None: - options.cert = os.path.expanduser(options.cert) - if not os.path.exists(options.cert): - parser.error("Manually created certificate does not exist: %s"%options.cert) - if options.cacert is not None: - options.cacert = os.path.expanduser(options.cacert) - if not os.path.exists(options.cacert): - print >> sys.stderr, "Creating dummy CA certificate at %s"%options.cacert - dummy_ca(options.cacert) - if options.certpath is not None: - options.certpath = os.path.expanduser(options.certpath) - elif options.cacert is not None: - options.certpath = os.path.dirname(options.cacert) - if getattr(options, "cache", None) is not None: - options.cache = os.path.expanduser(options.cache) - -- cgit v1.2.3