def test_tls_start_client(self, tdata): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ta.configure(["confdir"]) tctx.configure( ta, certs=[ tdata.path( "mitmproxy/net/data/verificationcerts/trusted-leaf.pem" ) ], ciphers_client="ECDHE-ECDSA-AES128-GCM-SHA256", ) ctx = context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) tls_start = tls.TlsData(ctx.client, context=ctx) ta.tls_start_client(tls_start) tssl_server = tls_start.ssl_conn # assert that a preexisting ssl_conn is not overwritten ta.tls_start_client(tls_start) assert tssl_server is tls_start.ssl_conn tssl_client = test_tls.SSLTest() assert self.do_handshake(tssl_client, tssl_server) assert tssl_client.obj.getpeercert()["subjectAltName"] == (( "DNS", "example.mitmproxy.org"), )
def tctx() -> context.Context: opts = options.Options() Proxyserver().load(opts) TermLog().load(opts) return context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts)
def test_alpn_selection(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ctx = context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tls_start = tls.TlsStartData(ctx.server, context=ctx) def assert_alpn(http2, client_offers, expected): tctx.configure(ta, http2=http2) ctx.client.alpn_offers = client_offers ctx.server.alpn_offers = None ta.tls_start(tls_start) assert ctx.server.alpn_offers == expected assert_alpn(True, tls.HTTP_ALPNS + (b"foo", ), tls.HTTP_ALPNS + (b"foo", )) assert_alpn(False, tls.HTTP_ALPNS + (b"foo", ), tls.HTTP1_ALPNS + (b"foo", )) assert_alpn(True, [], tls.HTTP_ALPNS) assert_alpn(False, [], tls.HTTP1_ALPNS) ctx.client.timestamp_tls_setup = time.time() # make sure that we don't upgrade h1 to h2, # see comment in tlsconfig.py assert_alpn(True, [], [])
def test_get_cert(self, tdata): """Test that we generate a certificate matching the connection's context.""" ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ta.configure(["confdir"]) ctx = context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) # Edge case first: We don't have _any_ idea about the server, so we just return "mitmproxy" as subject. entry = ta.get_cert(ctx) assert entry.cert.cn == "mitmproxy" # Here we have an existing server connection... ctx.server.address = ("server-address.example", 443) with open( tdata.path( "mitmproxy/net/data/verificationcerts/trusted-leaf.crt" ), "rb") as f: ctx.server.certificate_list = [certs.Cert.from_pem(f.read())] entry = ta.get_cert(ctx) assert entry.cert.cn == "example.mitmproxy.org" assert entry.cert.altnames == [ "example.mitmproxy.org", "server-address.example" ] # And now we also incorporate SNI. ctx.client.sni = "sni.example" entry = ta.get_cert(ctx) assert entry.cert.altnames == [ "example.mitmproxy.org", "sni.example" ]
def test_tls_clienthello(self): # only really testing for coverage here, there's no point in mirroring the individual conditions ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ch = tls.ClientHelloData(ctx, None) # type: ignore ta.tls_clienthello(ch) assert not ch.establish_server_tls_first
def test_fuzz_h1_request(data): tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) layer = http.HttpLayer(tctx, HTTPMode.regular) for _ in layer.handle_event(Start()): pass for chunk in data: for _ in layer.handle_event(DataReceived(tctx.client, chunk)): pass
async def test_block_global(block_global, block_private, should_be_killed, address): ar = block.Block() with taddons.context(ar) as tctx: tctx.configure(ar, block_global=block_global, block_private=block_private) client = connection.Client(address, ("127.0.0.1", 8080), 1607699500) ar.client_connected(client) assert bool(client.error) == should_be_killed
def h2_layer(opts): tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) tctx.client.alpn = b"h2" layer = http.HttpLayer(tctx, HTTPMode.regular) for _ in layer.handle_event(Start()): pass for _ in layer.handle_event(DataReceived(tctx.client, b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n')): pass return tctx, layer
def receive_handshake_data( self, data: bytes) -> layer.CommandGenerator[Tuple[bool, Optional[str]]]: if self.client_hello_parsed: return (yield from super().receive_handshake_data(data)) self.recv_buffer.extend(data) try: client_hello = parse_client_hello(self.recv_buffer) except ValueError: return False, f"Cannot parse ClientHello: {self.recv_buffer.hex()}" if client_hello: self.client_hello_parsed = True else: return False, None self.conn.sni = client_hello.sni self.conn.alpn_offers = client_hello.alpn_protocols tls_clienthello = ClientHelloData(self.context, client_hello) yield TlsClienthelloHook(tls_clienthello) if tls_clienthello.ignore_connection: # we've figured out that we don't want to intercept this connection, so we assign fake connection objects # to all TLS layers. This makes the real connection contents just go through. self.conn = self.tunnel_connection = connection.Client( ("ignore-conn", 0), ("ignore-conn", 0), time.time()) parent_layer = self.context.layers[self.context.layers.index(self) - 1] if isinstance(parent_layer, ServerTLSLayer): parent_layer.conn = parent_layer.tunnel_connection = connection.Server( None) self.child_layer = tcp.TCPLayer(self.context, ignore=True) yield from self.event_to_child( events.DataReceived(self.context.client, bytes(self.recv_buffer))) self.recv_buffer.clear() return True, None if tls_clienthello.establish_server_tls_first and not self.context.server.tls_established: err = yield from self.start_server_tls() if err: yield commands.Log( f"Unable to establish TLS connection with server ({err}). " f"Trying to establish TLS with client anyway.") yield from self.start_tls() if not self.conn.connected: return False, "connection closed early" ret = yield from super().receive_handshake_data(bytes( self.recv_buffer)) self.recv_buffer.clear() return ret
def test_tls_start_server_verify_ok(self, tdata): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tctx.configure(ta, ssl_verify_upstream_trusted_ca=tdata.path( "mitmproxy/net/data/verificationcerts/trusted-root.crt")) tls_start = tls.TlsStartData(ctx.server, context=ctx) ta.tls_start_server(tls_start) tssl_client = tls_start.ssl_conn tssl_server = test_tls.SSLTest(server_side=True) assert self.do_handshake(tssl_client, tssl_server)
def create(self, method: str, url: str) -> None: try: req = http.Request.make(method.upper(), url) except ValueError as e: raise exceptions.CommandError("Invalid URL: %s" % e) c = connection.Client(("", 0), ("", 0), req.timestamp_start - 0.0001) s = connection.Server((req.host, req.port)) f = http.HTTPFlow(c, s) f.request = req f.request.headers["Host"] = req.host self.add([f])
def test_tls_start_server_verify_failed(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.client.alpn_offers = [b"h2"] ctx.client.cipher_list = ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES128-SHA"] ctx.server.address = ("example.mitmproxy.org", 443) tls_start = tls.TlsData(ctx.server, context=ctx) ta.tls_start_server(tls_start) tssl_client = tls_start.ssl_conn tssl_server = test_tls.SSLTest(server_side=True) with pytest.raises(SSL.Error, match="certificate verify failed"): assert self.do_handshake(tssl_client, tssl_server)
def _h2_response(chunks): tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) server = Placeholder(connection.Server) assert ( playbook >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") << OpenConnection(server) >> reply(None, side_effect=make_h2) << SendData(server, Placeholder()) ) for chunk in chunks: for _ in playbook.layer.handle_event(events.DataReceived(server(), chunk)): pass
def test_no_h2_proxy(self, tdata): """Do not negotiate h2 on the client<->proxy connection in secure web proxy mode, https://github.com/mitmproxy/mitmproxy/issues/4689""" ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: tctx.configure(ta, certs=[tdata.path("mitmproxy/net/data/verificationcerts/trusted-leaf.pem")]) ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) # mock up something that looks like a secure web proxy. ctx.layers = [ modes.HttpProxy(ctx), 123 ] tls_start = tls.TlsData(ctx.client, context=ctx) ta.tls_start_client(tls_start) assert tls_start.ssl_conn.get_app_data()["client_alpn"] == b"http/1.1"
def test_create_proxy_server_ssl_conn_insecure(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: ctx = context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tctx.configure(ta, ssl_verify_upstream_trusted_ca=None, ssl_insecure=True, http2=False, ciphers_server="ALL") tls_start = tls.TlsStartData(ctx.server, context=ctx) ta.tls_start(tls_start) tssl_client = tls_start.ssl_conn tssl_server = test_tls.SSLTest(server_side=True) assert self.do_handshake(tssl_client, tssl_server)
def tctx(): context.Context( connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options)
def _test_cancel(stream_req, stream_resp, draw): """ Test that we don't raise an exception if someone disconnects. """ tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) def maybe_stream(flow: HTTPFlow): if stream_req: flow.request.stream = True if stream_resp and flow.response: flow.response.stream = True hook_req_headers = http.HttpRequestHeadersHook(flow) hook_req = http.HttpRequestHook(flow) hook_resp_headers = http.HttpResponseHeadersHook(flow) hook_resp = http.HttpResponseHook(flow) hook_error = http.HttpErrorHook(flow) openconn = OpenConnection(server) send_upstream = SendData(server, Placeholder(bytes)) data_req = DataReceived(tctx.client, cff.build_headers_frame(example_request_headers).serialize()) data_reqbody = DataReceived(tctx.client, cff.build_data_frame(b"foo", flags=["END_STREAM"]).serialize()) data_resp = DataReceived(server, cff.build_headers_frame(example_response_headers).serialize()) data_respbody = DataReceived(server, cff.build_data_frame(b"bar", flags=["END_STREAM"]).serialize()) client_disc = ConnectionClosed(tctx.client) client_rst = DataReceived(tctx.client, cff.build_rst_stream_frame(1).serialize()) server_disc = ConnectionClosed(server) server_rst = DataReceived(server, cff.build_rst_stream_frame(1).serialize()) evts: Dict[str, Tuple[Any, Any, Any]] = {} # precondition, but-not-after-this evts["data_req"] = data_req, None, client_disc evts["data_reqbody"] = data_reqbody, data_req, client_disc evts["reply_hook_req_headers"] = reply(to=hook_req_headers, side_effect=maybe_stream), hook_req_headers, None evts["reply_hook_req"] = reply(to=hook_req), hook_req, None evts["reply_openconn"] = reply(None, to=openconn, side_effect=make_h2), openconn, None evts["data_resp"] = data_resp, send_upstream, server_disc evts["data_respbody"] = data_respbody, data_resp, server_disc evts["reply_hook_resp_headers"] = reply(to=hook_resp_headers, side_effect=maybe_stream), hook_resp_headers, None evts["reply_hook_resp"] = reply(to=hook_resp), hook_resp, None evts["reply_hook_error"] = reply(to=hook_error), hook_error, None evts["err_client_disc"] = client_disc, None, None evts["err_client_rst"] = client_rst, None, client_disc evts["err_server_disc"] = server_disc, send_upstream, None evts["err_server_rst"] = server_rst, send_upstream, server_disc def eq_maybe(a, b): # _eq helpfully raises a TypeError when placeholder types don't match # that is useful in (test) development, but may happen legitimately when fuzzing here. try: return _eq(a, b) except TypeError: return False while evts: candidates = [] for name, (evt, precon, negprecon) in evts.items(): precondition_ok = ( precon is None or any(eq_maybe(x, precon) for x in playbook.actual) ) neg_precondition_ok = ( negprecon is None or not any(eq_maybe(x, negprecon) for x in playbook.actual) ) if precondition_ok and neg_precondition_ok: # crude hack to increase fuzzing efficiency: make it more likely that we progress. for i in range(1 if name.startswith("err_") else 3): candidates.append((name, evt)) if not candidates: break name, evt = draw(candidates) del evts[name] try: assert playbook >> evt except AssertionError: if any( isinstance(x, _TracebackInPlaybook) for x in playbook.actual ): raise else: # add commands that the server issued. playbook.expected.extend(playbook.actual[len(playbook.expected):])