From ce0a500885619444b7fd386419c5c556d5196e18 Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Sat, 23 Jan 2016 21:29:14 -0500 Subject: Add ignore based on TLS ClientHello SNI - also add some documentation about ignoring based on SNI --- libmproxy/proxy/root_context.py | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index f62b0c8e..ab10c47d 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -4,14 +4,40 @@ import sys import six -from libmproxy.exceptions import ProtocolException +from libmproxy.exceptions import ProtocolException, TlsProtocolException from netlib.exceptions import TcpException from ..protocol import ( RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin, - UpstreamConnectLayer + UpstreamConnectLayer, TlsClientHello ) from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy +def tls_sni_check_ignore(fun): + """ + A decorator to wrap the process of getting the next layer. + If it's a TlsLayer and the client uses SNI, see if the user asked us to + ignore the host. + Returns: + A function that returns the next layer. + """ + def inner(self, top_layer): + """ + Arguments: + top_layer: the current innermost layer. + Returns: + The next layer + """ + layer = fun(self, top_layer) + if not isinstance(layer, TlsLayer) or not layer.client_tls: + return layer + try: + parsed_client_hello = TlsClientHello.from_client_conn(self.client_conn) + if parsed_client_hello and self.config.check_ignore((parsed_client_hello.client_sni, 443)): + return RawTCPLayer(top_layer, logging=False) + except TlsProtocolException as e: + six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) + return layer + return inner class RootContext(object): """ @@ -33,6 +59,7 @@ class RootContext(object): self.client_conn = client_conn self.channel = channel self.config = config + self._client_tls = False def next_layer(self, top_layer): """ @@ -47,6 +74,7 @@ class RootContext(object): layer = self._next_layer(top_layer) return self.channel.ask("next_layer", layer) + @tls_sni_check_ignore def _next_layer(self, top_layer): # 1. Check for --ignore. if self.config.check_ignore(top_layer.server_conn.address): @@ -56,15 +84,15 @@ class RootContext(object): d = top_layer.client_conn.rfile.peek(3) except TcpException as e: six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) - client_tls = is_tls_record_magic(d) + self._client_tls = is_tls_record_magic(d) # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. if isinstance(top_layer, ReverseProxy): - return TlsLayer(top_layer, client_tls, top_layer.server_tls) + return TlsLayer(top_layer, self._client_tls, top_layer.server_tls) if isinstance(top_layer, ServerConnectionMixin) or isinstance(top_layer, UpstreamConnectLayer): - return TlsLayer(top_layer, client_tls, client_tls) + return TlsLayer(top_layer, self._client_tls, self._client_tls) # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. if isinstance(top_layer, TlsLayer): @@ -74,7 +102,7 @@ class RootContext(object): return Http1Layer(top_layer, "upstream") # 4. Check for other TLS cases (e.g. after CONNECT). - if client_tls: + if self._client_tls: return TlsLayer(top_layer, True, True) # 4. Check for --tcp -- cgit v1.2.3 From 56ef7d238b9c51aae096391e26d19fdcbc43ac5b Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Mon, 25 Jan 2016 17:49:31 -0500 Subject: Don't store client_tls state in root context --- libmproxy/proxy/root_context.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index ab10c47d..80e2047e 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -59,7 +59,6 @@ class RootContext(object): self.client_conn = client_conn self.channel = channel self.config = config - self._client_tls = False def next_layer(self, top_layer): """ @@ -84,15 +83,15 @@ class RootContext(object): d = top_layer.client_conn.rfile.peek(3) except TcpException as e: six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) - self._client_tls = is_tls_record_magic(d) + client_tls = is_tls_record_magic(d) # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. if isinstance(top_layer, ReverseProxy): - return TlsLayer(top_layer, self._client_tls, top_layer.server_tls) + return TlsLayer(top_layer, client_tls, top_layer.server_tls) if isinstance(top_layer, ServerConnectionMixin) or isinstance(top_layer, UpstreamConnectLayer): - return TlsLayer(top_layer, self._client_tls, self._client_tls) + return TlsLayer(top_layer, client_tls, client_tls) # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. if isinstance(top_layer, TlsLayer): @@ -102,7 +101,7 @@ class RootContext(object): return Http1Layer(top_layer, "upstream") # 4. Check for other TLS cases (e.g. after CONNECT). - if self._client_tls: + if client_tls: return TlsLayer(top_layer, True, True) # 4. Check for --tcp -- cgit v1.2.3 From 21ddd5e50393617d0e4e55b1a8117fc51c67a2b0 Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Tue, 26 Jan 2016 11:12:46 -0500 Subject: Move SNI ignore check into _next_layer --- libmproxy/proxy/root_context.py | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index 80e2047e..8bf84951 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -12,33 +12,6 @@ from ..protocol import ( ) from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy -def tls_sni_check_ignore(fun): - """ - A decorator to wrap the process of getting the next layer. - If it's a TlsLayer and the client uses SNI, see if the user asked us to - ignore the host. - Returns: - A function that returns the next layer. - """ - def inner(self, top_layer): - """ - Arguments: - top_layer: the current innermost layer. - Returns: - The next layer - """ - layer = fun(self, top_layer) - if not isinstance(layer, TlsLayer) or not layer.client_tls: - return layer - try: - parsed_client_hello = TlsClientHello.from_client_conn(self.client_conn) - if parsed_client_hello and self.config.check_ignore((parsed_client_hello.client_sni, 443)): - return RawTCPLayer(top_layer, logging=False) - except TlsProtocolException as e: - six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) - return layer - return inner - class RootContext(object): """ The outermost context provided to the root layer. @@ -73,7 +46,6 @@ class RootContext(object): layer = self._next_layer(top_layer) return self.channel.ask("next_layer", layer) - @tls_sni_check_ignore def _next_layer(self, top_layer): # 1. Check for --ignore. if self.config.check_ignore(top_layer.server_conn.address): @@ -85,6 +57,16 @@ class RootContext(object): six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) client_tls = is_tls_record_magic(d) + # 1A. check for --ignore with SNI host + if client_tls: + try: + client_hello = TlsClientHello.from_client_conn(self.client_conn) + if (client_hello and + self.config.check_ignore((client_hello.client_sni, 443))): + return RawTCPLayer(top_layer, logging=False) + except TlsProtocolException as e: + six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) + # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. -- cgit v1.2.3 From a2ebcfe8795490dc862996218f9e9821b2c3ed83 Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Tue, 26 Jan 2016 11:44:32 -0500 Subject: Simplify check_ignore in _next_layer --- libmproxy/proxy/root_context.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index 8bf84951..23d4aaf5 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -47,25 +47,24 @@ class RootContext(object): return self.channel.ask("next_layer", layer) def _next_layer(self, top_layer): - # 1. Check for --ignore. - if self.config.check_ignore(top_layer.server_conn.address): - return RawTCPLayer(top_layer, logging=False) - try: d = top_layer.client_conn.rfile.peek(3) except TcpException as e: six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) client_tls = is_tls_record_magic(d) - # 1A. check for --ignore with SNI host - if client_tls: - try: - client_hello = TlsClientHello.from_client_conn(self.client_conn) - if (client_hello and - self.config.check_ignore((client_hello.client_sni, 443))): - return RawTCPLayer(top_layer, logging=False) - except TlsProtocolException as e: - six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) + # 1. check for --ignore + if self.config.check_ignore: + address = top_layer.server_conn.address + if client_tls: + try: + client_hello = TlsClientHello.from_client_conn(self.client_conn) + except TlsProtocolException as e: + self.log("Cannot parse Client Hello: %s" % repr(e), "error") + else: + address = (client_hello.client_sni, 443) # TODO: may need to wrap that in tcp.Address? + if self.config.check_ignore(address): + return RawTCPLayer(top_layer, logging=False) # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, -- cgit v1.2.3 From 7d69c3480b6dcc75987958e06b4ee828c07249a9 Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Tue, 26 Jan 2016 11:59:32 -0500 Subject: remove TODO --- libmproxy/proxy/root_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index 23d4aaf5..8a3372e0 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -62,7 +62,7 @@ class RootContext(object): except TlsProtocolException as e: self.log("Cannot parse Client Hello: %s" % repr(e), "error") else: - address = (client_hello.client_sni, 443) # TODO: may need to wrap that in tcp.Address? + address = (client_hello.client_sni, 443) if self.config.check_ignore(address): return RawTCPLayer(top_layer, logging=False) -- cgit v1.2.3 From 4be8d148b13ae15d6b0f287935d3dc53a40cdf28 Mon Sep 17 00:00:00 2001 From: David Weinstein Date: Tue, 26 Jan 2016 13:09:22 -0500 Subject: Add SNI ignore docs and have code match it --- libmproxy/proxy/root_context.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'libmproxy/proxy') diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index 8a3372e0..d70fc299 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -55,15 +55,15 @@ class RootContext(object): # 1. check for --ignore if self.config.check_ignore: - address = top_layer.server_conn.address - if client_tls: - try: - client_hello = TlsClientHello.from_client_conn(self.client_conn) - except TlsProtocolException as e: - self.log("Cannot parse Client Hello: %s" % repr(e), "error") - else: - address = (client_hello.client_sni, 443) - if self.config.check_ignore(address): + ignore = self.config.check_ignore(top_layer.server_conn.address) + if not ignore and client_tls: + try: + client_hello = TlsClientHello.from_client_conn(self.client_conn) + except TlsProtocolException as e: + self.log("Cannot parse Client Hello: %s" % repr(e), "error") + else: + ignore = self.config.check_ignore((client_hello.client_sni, 443)) + if ignore: return RawTCPLayer(top_layer, logging=False) # 2. Always insert a TLS layer, even if there's neither client nor server tls. -- cgit v1.2.3