From 018c229ae40d93f0f0987a37a33256db57cdc62c Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Mon, 31 Dec 2012 09:15:56 +1300 Subject: Start solidifying proxy authentication - Add a unit test file - Remove some extraneous methods - Change the auth API to make the authenticate method take a header object. --- libmproxy/proxy.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index b1ce310c..2c62a880 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -356,8 +356,12 @@ class ProxyHandler(tcp.BaseHandler): headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") - if authenticate and self.config.authenticator and not self.config.authenticator.authenticate(headers.get('Proxy-Authorization', [])): - raise ProxyError(407, "Proxy Authentication Required", self.config.authenticator.auth_challenge_headers()) + if authenticate and self.config.authenticator and not self.config.authenticator.authenticate(headers): + raise ProxyError( + 407, + "Proxy Authentication Required", + self.config.authenticator.auth_challenge_headers() + ) return headers def send_response(self, response): -- cgit v1.2.3 From 5347cb9c269acdbc2fc36f92e3545fcbb9de45a1 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Mon, 31 Dec 2012 10:56:44 +1300 Subject: More work on proxy auth - Strip auth header if auth succeeds, so it's not passed upstream - Actually use realm specification to BasicProxyAuth, and make it mandatory - Cleanups and unit tests --- libmproxy/proxy.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 2c62a880..0cba4cbc 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -356,12 +356,15 @@ class ProxyHandler(tcp.BaseHandler): headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") - if authenticate and self.config.authenticator and not self.config.authenticator.authenticate(headers): - raise ProxyError( - 407, - "Proxy Authentication Required", - self.config.authenticator.auth_challenge_headers() - ) + if authenticate and self.config.authenticator: + if self.config.authenticator.authenticate(headers): + self.config.authenticator.clean(headers) + else: + raise ProxyError( + 407, + "Proxy Authentication Required", + self.config.authenticator.auth_challenge_headers() + ) return headers def send_response(self, response): @@ -552,7 +555,7 @@ def process_proxy_options(parser, options): password_manager = authentication.HtpasswdPasswordManager(options.auth_htpasswd) # in the meanwhile, basic auth is the only true authentication scheme we support # so just use it - authenticator = authentication.BasicProxyAuth(password_manager) + authenticator = authentication.BasicProxyAuth(password_manager, "mitmproxy") else: authenticator = authentication.NullProxyAuth(None) -- cgit v1.2.3 From e42136a6ef6f29a16cb2eb5f566be317ed7f3579 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Tue, 1 Jan 2013 11:24:11 +1300 Subject: Better error handling for transparent mode remote address resolution. --- libmproxy/proxy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 0cba4cbc..9abb9833 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -267,7 +267,10 @@ class ProxyHandler(tcp.BaseHandler): def read_request(self, client_conn): if self.config.transparent_proxy: - host, port = self.config.transparent_proxy["resolver"].original_addr(self.connection) + orig = self.config.transparent_proxy["resolver"].original_addr(self.connection) + if not orig: + raise ProxyError(502, "Transparent mode failure: could not resolve original destination.") + host, port = orig if not self.ssl_established and (port in self.config.transparent_proxy["sslports"]): scheme = "https" certfile = self.find_cert(host, port, None) -- cgit v1.2.3 From 09f664cdeafae1d9923fe5ce2c4ab3acc7757a61 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Wed, 2 Jan 2013 17:35:44 +1300 Subject: Refactor proxy auth a bit - Remove authentication scheme option. We only support basic at the moment - we'll add the option back when we diversify. - Add some meta variables to make printout nicer --- libmproxy/proxy.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 9abb9833..22e7ff63 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -544,25 +544,20 @@ def process_proxy_options(parser, options): if not os.path.exists(options.certdir) or not os.path.isdir(options.certdir): parser.error("Dummy cert directory does not exist or is not a directory: %s"%options.certdir) - if options.authscheme and (options.authscheme!='none'): - if not (options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd): - parser.error("Proxy authentication scheme is specified, but no allowed user list is given.") - if options.auth_singleuser and len(options.auth_singleuser.split(':'))!=2: - parser.error("Authorized user is not given in correct format username:password") - if options.auth_nonanonymous: - password_manager = authentication.PermissivePasswordManager() - elif options.auth_singleuser: + if (options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd): + if options.auth_singleuser: + if len(options.auth_singleuser.split(':')) != 2: + parser.error("Please specify user in the format username:password") username, password = options.auth_singleuser.split(':') password_manager = authentication.SingleUserPasswordManager(username, password) + elif options.auth_nonanonymous: + password_manager = authentication.PermissivePasswordManager() elif options.auth_htpasswd: password_manager = authentication.HtpasswdPasswordManager(options.auth_htpasswd) - # in the meanwhile, basic auth is the only true authentication scheme we support - # so just use it authenticator = authentication.BasicProxyAuth(password_manager, "mitmproxy") else: authenticator = authentication.NullProxyAuth(None) - return ProxyConfig( certfile = options.cert, cacert = cacert, -- cgit v1.2.3 From 46ab6ed4912039e19c17cc4346bf562f2acdc8a9 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Fri, 4 Jan 2013 14:19:32 +1300 Subject: Minor cleanups of proxy request handling. --- libmproxy/proxy.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 22e7ff63..da47dc20 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -51,6 +51,7 @@ class ProxyConfig: self.transparent_proxy = transparent_proxy self.authenticator = authenticator + class RequestReplayThread(threading.Thread): def __init__(self, config, flow, masterq): self.config, self.flow, self.masterq = config, flow, masterq @@ -311,7 +312,7 @@ class ProxyHandler(tcp.BaseHandler): line = self.get_line(self.rfile) if line == "": return None - if line.startswith("CONNECT"): + if http.parse_init_connect(line): r = http.parse_init_connect(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) @@ -332,14 +333,15 @@ class ProxyHandler(tcp.BaseHandler): raise ProxyError(400, str(v)) self.proxy_connect_state = (host, port, httpversion) line = self.rfile.readline(line) + if self.proxy_connect_state: - host, port, httpversion = self.proxy_connect_state r = http.parse_init_http(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = self.read_headers(authenticate=False) + host, port, _ = self.proxy_connect_state content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) @@ -348,7 +350,7 @@ class ProxyHandler(tcp.BaseHandler): r = http.parse_init_proxy(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) - method, scheme, host, port, path, httpversion = http.parse_init_proxy(line) + method, scheme, host, port, path, httpversion = r headers = self.read_headers(authenticate=True) content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit -- cgit v1.2.3 From 891c441a6dc26b725999ca67bb16f649b92176b4 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 6 Jan 2013 01:16:08 +1300 Subject: Use new netlib certificate store implementation. --- libmproxy/proxy.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index da47dc20..98bcb23d 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -43,7 +43,6 @@ class ProxyConfig: self.certfile = certfile self.cacert = cacert self.clientcerts = clientcerts - self.certdir = certdir self.cert_wait_time = cert_wait_time self.no_upstream_cert = no_upstream_cert self.body_size_limit = body_size_limit @@ -51,6 +50,7 @@ class ProxyConfig: self.transparent_proxy = transparent_proxy self.authenticator = authenticator + self.certstore = certutils.CertStore(certdir) class RequestReplayThread(threading.Thread): def __init__(self, config, flow, masterq): @@ -246,7 +246,9 @@ class ProxyHandler(tcp.BaseHandler): raise ProxyError(502, "Unable to get remote cert: %s"%str(v)) sans = cert.altnames host = cert.cn.decode("utf8").encode("idna") - ret = certutils.dummy_cert(self.config.certdir, self.config.cacert, host, sans) + ret = self.config.certstore.get_cert(host, sans, self.config.cacert) + # FIXME: Is this still necessary? Can we now set a proper + # commencement date, since we're using PyOpenSSL? time.sleep(self.config.cert_wait_time) if not ret: raise ProxyError(502, "mitmproxy: Unable to generate dummy cert.") @@ -414,13 +416,6 @@ class ProxyServer(tcp.TCPServer): except socket.error, v: raise ProxyServerError('Error starting proxy server: ' + v.strerror) self.masterq = None - if config.certdir: - self.certdir = config.certdir - self.remove_certdir = False - else: - self.certdir = tempfile.mkdtemp(prefix="mitmproxy") - config.certdir = self.certdir - self.remove_certdir = True self.apps = AppRegistry() def start_slave(self, klass, masterq): @@ -439,11 +434,7 @@ class ProxyServer(tcp.TCPServer): pass def handle_shutdown(self): - try: - if self.remove_certdir: - shutil.rmtree(self.certdir) - except OSError: - pass + self.config.certstore.cleanup() class AppRegistry: -- cgit v1.2.3 From 060e3198bca48f327984b2d1d73a5d4592866736 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 6 Jan 2013 01:18:47 +1300 Subject: Remove cert_wait_time flag. We now cater for this by generating certs with a commencement date an hour in the past in netlib. --- libmproxy/proxy.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'libmproxy/proxy.py') diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 98bcb23d..370eca9a 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -38,12 +38,11 @@ class Log(controller.Msg): class ProxyConfig: - def __init__(self, certfile = None, cacert = None, clientcerts = None, cert_wait_time=0, no_upstream_cert=False, body_size_limit = None, reverse_proxy=None, transparent_proxy=None, certdir = None, authenticator=None): + def __init__(self, certfile = None, cacert = None, clientcerts = None, no_upstream_cert=False, body_size_limit = None, reverse_proxy=None, transparent_proxy=None, certdir = None, authenticator=None): assert not (reverse_proxy and transparent_proxy) self.certfile = certfile self.cacert = cacert self.clientcerts = clientcerts - self.cert_wait_time = cert_wait_time self.no_upstream_cert = no_upstream_cert self.body_size_limit = body_size_limit self.reverse_proxy = reverse_proxy @@ -247,9 +246,6 @@ class ProxyHandler(tcp.BaseHandler): sans = cert.altnames host = cert.cn.decode("utf8").encode("idna") ret = self.config.certstore.get_cert(host, sans, self.config.cacert) - # FIXME: Is this still necessary? Can we now set a proper - # commencement date, since we're using PyOpenSSL? - time.sleep(self.config.cert_wait_time) if not ret: raise ProxyError(502, "mitmproxy: Unable to generate dummy cert.") return ret @@ -555,7 +551,6 @@ def process_proxy_options(parser, options): certfile = options.cert, cacert = cacert, clientcerts = options.clientcerts, - cert_wait_time = options.cert_wait_time, body_size_limit = body_size_limit, no_upstream_cert = options.no_upstream_cert, reverse_proxy = rp, -- cgit v1.2.3