diff --git a/libmproxy/exceptions.py b/libmproxy/exceptions.py new file mode 100644 index 000000000..4d98c024d --- /dev/null +++ b/libmproxy/exceptions.py @@ -0,0 +1,22 @@ +from __future__ import (absolute_import, print_function, division) + + +class ProxyException(Exception): + """ + Base class for all exceptions thrown by libmproxy. + """ + def __init__(self, message, cause=None): + """ + :param message: Error Message + :param cause: Exception object that caused this exception to be thrown. + """ + super(ProxyException, self).__init__(message) + self.cause = cause + + +class ProtocolException(ProxyException): + pass + + +class ServerException(ProxyException): + pass \ No newline at end of file diff --git a/libmproxy/protocol2/__init__.py b/libmproxy/protocol2/__init__.py index 3f714f62d..0d232b138 100644 --- a/libmproxy/protocol2/__init__.py +++ b/libmproxy/protocol2/__init__.py @@ -2,6 +2,7 @@ from __future__ import (absolute_import, print_function, division) from .layer import RootContext from .socks import Socks5IncomingLayer from .reverse_proxy import ReverseProxy +from .upstream_proxy import UpstreamProxy from .rawtcp import TcpLayer from .auto import AutoLayer -__all__ = ["Socks5IncomingLayer", "TcpLayer", "AutoLayer", "RootContext", "ReverseProxy"] +__all__ = ["Socks5IncomingLayer", "TcpLayer", "AutoLayer", "RootContext", "ReverseProxy", "UpstreamProxy"] diff --git a/libmproxy/protocol2/auto.py b/libmproxy/protocol2/auto.py index fc1117582..4a9307202 100644 --- a/libmproxy/protocol2/auto.py +++ b/libmproxy/protocol2/auto.py @@ -10,11 +10,11 @@ class AutoLayer(Layer): return # TLS ClientHello magic, see http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello if d[0] == "\x16": - layer = SslLayer(self, True, True) + layer = TlsLayer(self, True, True) else: layer = TcpLayer(self) for m in layer(): yield m from .rawtcp import TcpLayer -from .ssl import SslLayer +from .tls import TlsLayer diff --git a/libmproxy/protocol2/layer.py b/libmproxy/protocol2/layer.py index c18be83cf..8aede22ed 100644 --- a/libmproxy/protocol2/layer.py +++ b/libmproxy/protocol2/layer.py @@ -35,9 +35,10 @@ from __future__ import (absolute_import, print_function, division) import Queue import threading from netlib import tcp -from ..proxy import ProxyError2, Log +from ..proxy import Log from ..proxy.connection import ServerConnection from .messages import Connect, Reconnect, ChangeServer +from ..exceptions import ProtocolException class RootContext(object): @@ -51,6 +52,9 @@ class RootContext(object): self.channel = channel # provides .ask() method to communicate with FlowMaster self.config = config # Proxy Configuration + def next_layer(self): + print(type(self)) + class _LayerCodeCompletion(object): """ @@ -149,7 +153,7 @@ class ServerConnectionMixin(object): try: self.server_conn.connect() except tcp.NetLibError as e: - raise ProxyError2("Server connection to '%s' failed: %s" % (self.server_address, e), e) + raise ProtocolException("Server connection to '%s' failed: %s" % (self.server_address, e), e) def yield_from_callback(fun): @@ -197,7 +201,7 @@ def yield_from_callback(fun): break elif isinstance(msg, Exception): # TODO: Include func name? - raise ProxyError2("Error in %s: %s" % (fun.__name__, repr(msg)), msg) + raise ProtocolException("Error in %s: %s" % (fun.__name__, repr(msg)), msg) else: yield msg yield_queue.put(None) diff --git a/libmproxy/protocol2/messages.py b/libmproxy/protocol2/messages.py index baf4312dc..3f53fbd45 100644 --- a/libmproxy/protocol2/messages.py +++ b/libmproxy/protocol2/messages.py @@ -32,9 +32,9 @@ class ChangeServer(_Message): Change the upstream server. """ - def __init__(self, address, server_ssl, sni, depth=1): + def __init__(self, address, server_tls, sni, depth=1): self.address = address - self.server_ssl = server_ssl + self.server_tls = server_tls self.sni = sni # upstream proxy scenario: you may want to change either the final target or the upstream proxy. diff --git a/libmproxy/protocol2/rawtcp.py b/libmproxy/protocol2/rawtcp.py index 39e48e242..608a53e3a 100644 --- a/libmproxy/protocol2/rawtcp.py +++ b/libmproxy/protocol2/rawtcp.py @@ -1,4 +1,6 @@ from __future__ import (absolute_import, print_function, division) +import OpenSSL +from ..exceptions import ProtocolException from ..protocol.tcp import TCPHandler from .layer import Layer from .messages import Connect @@ -8,7 +10,11 @@ class TcpLayer(Layer): def __call__(self): yield Connect() tcp_handler = TCPHandler(self) - tcp_handler.handle_messages() + try: + tcp_handler.handle_messages() + except OpenSSL.SSL.Error as e: + raise ProtocolException("SSL error: %s" % repr(e), e) + def establish_server_connection(self): pass diff --git a/libmproxy/protocol2/reverse_proxy.py b/libmproxy/protocol2/reverse_proxy.py index dfffd2f25..cb6d1d786 100644 --- a/libmproxy/protocol2/reverse_proxy.py +++ b/libmproxy/protocol2/reverse_proxy.py @@ -1,19 +1,19 @@ from __future__ import (absolute_import, print_function, division) from .layer import Layer, ServerConnectionMixin -from .ssl import SslLayer +from .tls import TlsLayer class ReverseProxy(Layer, ServerConnectionMixin): - def __init__(self, ctx, server_address, client_ssl, server_ssl): + def __init__(self, ctx, server_address, client_tls, server_tls): super(ReverseProxy, self).__init__(ctx) self.server_address = server_address - self.client_ssl = client_ssl - self.server_ssl = server_ssl + self._client_tls = client_tls + self._server_tls = server_tls def __call__(self): - layer = SslLayer(self, self.client_ssl, self.server_ssl) + layer = TlsLayer(self, self._client_tls, self._server_tls) for message in layer(): if not self._handle_server_message(message): yield message diff --git a/libmproxy/protocol2/socks.py b/libmproxy/protocol2/socks.py index 14564521e..1222ef5c8 100644 --- a/libmproxy/protocol2/socks.py +++ b/libmproxy/protocol2/socks.py @@ -1,10 +1,10 @@ from __future__ import (absolute_import, print_function, division) -from ..proxy import ProxyError, Socks5ProxyMode, ProxyError2 +from ..exceptions import ProtocolException +from ..proxy import ProxyError, Socks5ProxyMode from .layer import Layer, ServerConnectionMixin from .auto import AutoLayer - class Socks5IncomingLayer(Layer, ServerConnectionMixin): def __call__(self): try: @@ -12,7 +12,7 @@ class Socks5IncomingLayer(Layer, ServerConnectionMixin): address = s5mode.get_upstream_server(self.client_conn)[2:] except ProxyError as e: # TODO: Unmonkeypatch - raise ProxyError2(str(e), e) + raise ProtocolException(str(e), e) self.server_address = address diff --git a/libmproxy/protocol2/ssl.py b/libmproxy/protocol2/tls.py similarity index 76% rename from libmproxy/protocol2/ssl.py rename to libmproxy/protocol2/tls.py index a744a979c..2362b2b25 100644 --- a/libmproxy/protocol2/ssl.py +++ b/libmproxy/protocol2/tls.py @@ -2,17 +2,17 @@ from __future__ import (absolute_import, print_function, division) import traceback from netlib import tcp -from ..proxy import ProxyError2 +from ..exceptions import ProtocolException from .layer import Layer, yield_from_callback from .messages import Connect, Reconnect, ChangeServer from .auto import AutoLayer -class SslLayer(Layer): - def __init__(self, ctx, client_ssl, server_ssl): - super(SslLayer, self).__init__(ctx) - self._client_ssl = client_ssl - self._server_ssl = server_ssl +class TlsLayer(Layer): + def __init__(self, ctx, client_tls, server_tls): + super(TlsLayer, self).__init__(ctx) + self._client_tls = client_tls + self._server_tls = server_tls self._connected = False self.client_sni = None self._sni_from_server_change = None @@ -41,33 +41,34 @@ class SslLayer(Layer): https://www.openssl.org/docs/ssl/SSL_CTX_set_cert_cb.html - The original mitmproxy issue is https://github.com/mitmproxy/mitmproxy/issues/427 """ - client_ssl_requires_server_cert = ( - self._client_ssl and self._server_ssl and not self.config.no_upstream_cert + client_tls_requires_server_cert = ( + self._client_tls and self._server_tls and not self.config.no_upstream_cert ) - lazy_server_ssl = ( - self._server_ssl and not client_ssl_requires_server_cert + lazy_server_tls = ( + self._server_tls and not client_tls_requires_server_cert ) - if client_ssl_requires_server_cert: - for m in self._establish_ssl_with_client_and_server(): + if client_tls_requires_server_cert: + for m in self._establish_tls_with_client_and_server(): yield m - elif self.client_ssl: - for m in self._establish_ssl_with_client(): + elif self._client_tls: + for m in self._establish_tls_with_client(): yield m + self.next_layer() layer = AutoLayer(self) for message in layer(): if message != Connect or not self._connected: yield message if message == Connect: - if lazy_server_ssl: - self._establish_ssl_with_server() + if lazy_server_tls: + self._establish_tls_with_server() if message == ChangeServer and message.depth == 1: - self.server_ssl = message.server_ssl + self._server_tls = message.server_tls self._sni_from_server_change = message.sni if message == Reconnect or message == ChangeServer: - if self.server_ssl: - self._establish_ssl_with_server() + if self._server_tls: + self._establish_tls_with_server() @property def sni_for_upstream_connection(self): @@ -76,7 +77,7 @@ class SslLayer(Layer): else: return self._sni_from_server_change or self.client_sni - def _establish_ssl_with_client_and_server(self): + def _establish_tls_with_client_and_server(self): """ This function deals with the problem that the server may require a SNI value from the client. """ @@ -86,14 +87,14 @@ class SslLayer(Layer): self._connected = True server_err = None try: - self._establish_ssl_with_server() - except ProxyError2 as e: + self._establish_tls_with_server() + except ProtocolException as e: server_err = e - for message in self._establish_ssl_with_client(): + for message in self._establish_tls_with_client(): if message == Reconnect: yield message - self._establish_ssl_with_server() + self._establish_tls_with_server() else: raise RuntimeError("Unexpected Message: %s" % message) @@ -102,7 +103,7 @@ class SslLayer(Layer): def handle_sni(self, connection): """ - This callback gets called during the SSL handshake with the client. + This callback gets called during the TLS handshake with the client. The client has just sent the Sever Name Indication (SNI). """ try: @@ -115,7 +116,7 @@ class SslLayer(Layer): if old_upstream_sni != self.sni_for_upstream_connection: # Perform reconnect - if self.server_ssl: + if self._server_tls: self.yield_from_callback(Reconnect()) if self.client_sni: @@ -136,8 +137,8 @@ class SslLayer(Layer): self.log("Error in handle_sni:\r\n" + traceback.format_exc(), "error") @yield_from_callback - def _establish_ssl_with_client(self): - self.log("Establish SSL with client", "debug") + def _establish_tls_with_client(self): + self.log("Establish TLS with client", "debug") cert, key, chain_file = self.find_cert() try: self.client_conn.convert_to_ssl( @@ -150,10 +151,10 @@ class SslLayer(Layer): chain_file=chain_file ) except tcp.NetLibError as e: - raise ProxyError2(repr(e), e) + raise ProtocolException(repr(e), e) - def _establish_ssl_with_server(self): - self.log("Establish SSL with server", "debug") + def _establish_tls_with_server(self): + self.log("Establish TLS with server", "debug") try: self.server_conn.establish_ssl( self.config.clientcerts, @@ -165,30 +166,29 @@ class SslLayer(Layer): ca_pemfile=self.config.openssl_trusted_ca_server, cipher_list=self.config.ciphers_server, ) - ssl_cert_err = self.server_conn.ssl_verification_error - if ssl_cert_err is not None: + tls_cert_err = self.server_conn.ssl_verification_error + if tls_cert_err is not None: self.log( - "SSL verification failed for upstream server at depth %s with error: %s" % - (ssl_cert_err['depth'], ssl_cert_err['errno']), + "TLS verification failed for upstream server at depth %s with error: %s" % + (tls_cert_err['depth'], tls_cert_err['errno']), "error") self.log("Ignoring server verification error, continuing with connection", "error") except tcp.NetLibInvalidCertificateError as e: - ssl_cert_err = self.server_conn.ssl_verification_error + tls_cert_err = self.server_conn.ssl_verification_error self.log( - "SSL verification failed for upstream server at depth %s with error: %s" % - (ssl_cert_err['depth'], ssl_cert_err['errno']), + "TLS verification failed for upstream server at depth %s with error: %s" % + (tls_cert_err['depth'], tls_cert_err['errno']), "error") self.log("Aborting connection attempt", "error") - raise ProxyError2(repr(e), e) + raise ProtocolException(repr(e), e) except tcp.NetLibError as e: - raise ProxyError2(repr(e), e) + raise ProtocolException(repr(e), e) def find_cert(self): host = self.server_conn.address.host - # TODO: Better use an OrderedSet here sans = set() # Incorporate upstream certificate - if self.server_conn.ssl_established and (not self.config.no_upstream_cert): + if self.server_conn.tls_established and (not self.config.no_upstream_cert): upstream_cert = self.server_conn.cert sans.update(upstream_cert.altnames) if upstream_cert.cn: diff --git a/libmproxy/protocol2/transparent_proxy.py b/libmproxy/protocol2/transparent_proxy.py new file mode 100644 index 000000000..078954c2b --- /dev/null +++ b/libmproxy/protocol2/transparent_proxy.py @@ -0,0 +1,24 @@ +from __future__ import (absolute_import, print_function, division) + +from ..exceptions import ProtocolException +from .. import platform +from .layer import Layer, ServerConnectionMixin +from .auto import AutoLayer + + +class TransparentProxy(Layer, ServerConnectionMixin): + + def __init__(self, ctx): + super(TransparentProxy, self).__init__(ctx) + self.resolver = platform.resolver() + + def __call__(self): + try: + self.server_address = self.resolver.original_addr(self.client_conn.connection) + except Exception as e: + raise ProtocolException("Transparent mode failure: %s" % repr(e), e) + + layer = AutoLayer(self) + for message in layer(): + if not self._handle_server_message(message): + yield message diff --git a/libmproxy/protocol2/upstream_proxy.py b/libmproxy/protocol2/upstream_proxy.py new file mode 100644 index 000000000..bd9203094 --- /dev/null +++ b/libmproxy/protocol2/upstream_proxy.py @@ -0,0 +1,18 @@ +from __future__ import (absolute_import, print_function, division) + +from .layer import Layer, ServerConnectionMixin +#from .http import HttpLayer + + +class UpstreamProxy(Layer, ServerConnectionMixin): + + def __init__(self, ctx, server_address): + super(UpstreamProxy, self).__init__(ctx) + self.server_address = server_address + + def __call__(self): + #layer = HttpLayer(self) + layer = None + for message in layer(): + if not self._handle_server_message(message): + yield message diff --git a/libmproxy/proxy/connection.py b/libmproxy/proxy/connection.py index 9e03157a3..49210e474 100644 --- a/libmproxy/proxy/connection.py +++ b/libmproxy/proxy/connection.py @@ -32,6 +32,10 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject): port=self.address.port ) + @property + def tls_established(self): + return self.ssl_established + _stateobject_attributes = dict( ssl_established=bool, timestamp_start=float, @@ -112,6 +116,10 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): port=self.address.port ) + @property + def tls_established(self): + return self.ssl_established + _stateobject_attributes = dict( state=list, timestamp_start=float, diff --git a/libmproxy/proxy/primitives.py b/libmproxy/proxy/primitives.py index fd4eb882a..a9f31181a 100644 --- a/libmproxy/proxy/primitives.py +++ b/libmproxy/proxy/primitives.py @@ -2,12 +2,6 @@ from __future__ import absolute_import from netlib import socks, tcp -class ProxyError2(Exception): - def __init__(self, message, cause=None): - super(ProxyError2, self).__init__(message) - self.cause = cause - - class ProxyError(Exception): def __init__(self, code, message, headers=None): super(ProxyError, self).__init__(message) diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 32d596ad8..c107cbedc 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -7,7 +7,7 @@ from netlib import tcp from ..protocol.handle import protocol_handler from .. import protocol2 -from .primitives import ProxyServerError, Log, ProxyError, ProxyError2 +from .primitives import ProxyServerError, Log, ProxyError from .connection import ClientConnection, ServerConnection @@ -79,12 +79,12 @@ class ConnectionHandler2: self.config, self.channel ) - root_layer = protocol2.ReverseProxy(root_context, ("localhost", 5000), True, True) + root_layer = protocol2.Socks5IncomingLayer(root_context) try: for message in root_layer(): print("Root layer receveived: %s" % message) - except ProxyError2 as e: + except protocol2.ProtocolException as e: self.log(e, "info") except Exception: self.log(traceback.format_exc(), "error")