move tls hook data to mitmproxy.tls

This commit is contained in:
Maximilian Hils 2021-09-04 16:37:39 +02:00
parent bdf4e31c58
commit 7fd887a553
7 changed files with 191 additions and 171 deletions

View File

@ -19,8 +19,7 @@ import random
from abc import ABC, abstractmethod
from enum import Enum
from mitmproxy import connection, ctx
from mitmproxy.proxy.layers import tls
from mitmproxy import connection, ctx, tls
from mitmproxy.utils import human
@ -95,7 +94,7 @@ class MaybeTls:
data.ignore_connection = True
self.strategy.record_skipped(server_address)
def tls_handshake(self, data: tls.TlsHookData):
def tls_handshake(self, data: tls.TlsData):
if isinstance(data.conn, connection.Server):
return # we are only interested in failing client connections here.
server_address = data.context.server.peername

View File

@ -4,11 +4,12 @@ from pathlib import Path
from typing import List, Optional, TypedDict, Any
from OpenSSL import SSL
from mitmproxy import certs, ctx, exceptions, connection
from mitmproxy import certs, ctx, exceptions, connection, tls
from mitmproxy.net import tls as net_tls
from mitmproxy.options import CONF_BASENAME
from mitmproxy.proxy import context
from mitmproxy.proxy.layers import tls, modes
from mitmproxy.proxy.layers import modes
from mitmproxy.proxy.layers import tls as proxy_tls
# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default.
# https://ssl-config.mozilla.org/#config=old
@ -46,7 +47,7 @@ def alpn_select_callback(conn: SSL.Connection, options: List[bytes]) -> Any:
# We do have a server connection, but the remote server refused to negotiate a protocol:
# We need to mirror this on the client connection.
return SSL.NO_OVERLAPPING_PROTOCOLS
http_alpns = tls.HTTP_ALPNS if http2 else tls.HTTP1_ALPNS
http_alpns = proxy_tls.HTTP_ALPNS if http2 else proxy_tls.HTTP1_ALPNS
for alpn in options: # client sends in order of preference, so we are nice and respect that.
if alpn in http_alpns:
return alpn
@ -112,7 +113,7 @@ class TlsConfig:
ctx.options.upstream_cert
)
def tls_start_client(self, tls_start: tls.TlsHookData) -> None:
def tls_start_client(self, tls_start: tls.TlsData) -> None:
"""Establish TLS between client and proxy."""
client: connection.Client = tls_start.context.client
server: connection.Server = tls_start.context.server
@ -159,7 +160,7 @@ class TlsConfig:
))
tls_start.ssl_conn.set_accept_state()
def tls_start_server(self, tls_start: tls.TlsHookData) -> None:
def tls_start_server(self, tls_start: tls.TlsData) -> None:
"""Establish TLS between proxy and server."""
client: connection.Client = tls_start.context.client
server: connection.Server = tls_start.context.server

View File

@ -4,7 +4,7 @@ from dataclasses import dataclass
from typing import Iterator, Literal, Optional, Tuple
from OpenSSL import SSL
from mitmproxy.tls import ClientHello
from mitmproxy.tls import ClientHello, ClientHelloData, TlsData
from mitmproxy import certs, connection
from mitmproxy.proxy import commands, events, layer, tunnel
from mitmproxy.proxy import context
@ -98,22 +98,6 @@ HTTP_ALPNS = (b"h2",) + HTTP1_ALPNS
# We need these classes as hooks can only have one argument at the moment.
@dataclass
class ClientHelloData:
context: context.Context
"""The context object for this connection."""
client_hello: ClientHello
"""The entire parsed TLS ClientHello."""
ignore_connection: bool = False
"""
If set to `True`, do not intercept this connection and forward encrypted contents unmodified.
"""
establish_server_tls_first: bool = False
"""
If set to `True`, pause this handshake and establish TLS with an upstream server first.
This makes it possible to process the server certificate when generating an interception certificate.
"""
@dataclass
class TlsClienthelloHook(StartHook):
@ -126,13 +110,6 @@ class TlsClienthelloHook(StartHook):
data: ClientHelloData
@dataclass
class TlsHookData:
conn: connection.Connection
context: context.Context
ssl_conn: Optional[SSL.Connection] = None
@dataclass
class TlsStartClientHook(StartHook):
"""
@ -141,7 +118,7 @@ class TlsStartClientHook(StartHook):
An addon is expected to initialize data.ssl_conn.
(by default, this is done by `mitmproxy.addons.tlsconfig`)
"""
data: TlsHookData
data: TlsData
@dataclass
@ -152,7 +129,7 @@ class TlsStartServerHook(StartHook):
An addon is expected to initialize data.ssl_conn.
(by default, this is done by `mitmproxy.addons.tlsconfig`)
"""
data: TlsHookData
data: TlsData
@dataclass
@ -162,7 +139,7 @@ class TlsHandshakeHook(StartHook):
If `data.conn.error` is `None`, negotiation was successful.
"""
data: TlsHookData
data: TlsData
class _TLSLayer(tunnel.TunnelLayer):
@ -184,7 +161,7 @@ class _TLSLayer(tunnel.TunnelLayer):
def start_tls(self) -> layer.CommandGenerator[None]:
assert not self.tls
tls_start = TlsHookData(self.conn, self.context)
tls_start = TlsData(self.conn, self.context)
if tls_start.conn == tls_start.context.client:
yield TlsStartClientHook(tls_start)
else:
@ -256,13 +233,13 @@ class _TLSLayer(tunnel.TunnelLayer):
self.conn.tls_version = self.tls.get_protocol_version_name()
if self.debug:
yield commands.Log(f"{self.debug}[tls] tls established: {self.conn}", "debug")
yield TlsHandshakeHook(TlsHookData(self.conn, self.context, self.tls))
yield TlsHandshakeHook(TlsData(self.conn, self.context, self.tls))
yield from self.receive_data(b"")
return True, None
def on_handshake_error(self, err: str) -> layer.CommandGenerator[None]:
self.conn.error = err
yield TlsHandshakeHook(TlsHookData(self.conn, self.context, self.tls))
yield TlsHandshakeHook(TlsData(self.conn, self.context, self.tls))
yield from super().on_handshake_error(err)
def receive_data(self, data: bytes) -> layer.CommandGenerator[None]:

View File

@ -16,12 +16,11 @@ from contextlib import contextmanager
from dataclasses import dataclass
from OpenSSL import SSL
from mitmproxy import http, options as moptions
from mitmproxy import http, options as moptions, tls
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import commands, events, layer, layers, server_hooks
from mitmproxy.connection import Address, Client, Connection, ConnectionState
from mitmproxy.proxy.layers import tls
from mitmproxy.utils import asyncio_utils
from mitmproxy.utils import human
from mitmproxy.utils.data import pkg_data
@ -414,7 +413,7 @@ if __name__ == "__main__": # pragma: no cover
if "redirect" in flow.request.path:
flow.request.host = "httpbin.org"
def tls_start_client(tls_start: tls.TlsHookData):
def tls_start_client(tls_start: tls.TlsData):
# INSECURE
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
ssl_context.use_privatekey_file(
@ -426,7 +425,7 @@ if __name__ == "__main__": # pragma: no cover
tls_start.ssl_conn = SSL.Connection(ssl_context)
tls_start.ssl_conn.set_accept_state()
def tls_start_server(tls_start: tls.TlsHookData):
def tls_start_server(tls_start: tls.TlsData):
# INSECURE
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
tls_start.ssl_conn = SSL.Connection(ssl_context)

View File

@ -1,13 +1,20 @@
import io
from dataclasses import dataclass
from typing import List, Optional, Tuple
from kaitaistruct import KaitaiStream
from OpenSSL import SSL
from mitmproxy import connection
from mitmproxy.contrib.kaitaistruct import tls_client_hello
from mitmproxy.net import check
from mitmproxy.proxy import context
class ClientHello:
"""
A TLS ClientHello is the first message sent by the client when initiating TLS.
"""
def __init__(self, raw_client_hello):
self._client_hello = tls_client_hello.TlsClientHello(
@ -23,10 +30,10 @@ class ClientHello:
if self._client_hello.extensions:
for extension in self._client_hello.extensions.extensions:
is_valid_sni_extension = (
extension.type == 0x00 and
len(extension.body.server_names) == 1 and
extension.body.server_names[0].name_type == 0 and
check.is_valid_host(extension.body.server_names[0].host_name)
extension.type == 0x00 and
len(extension.body.server_names) == 1 and
extension.body.server_names[0].name_type == 0 and
check.is_valid_host(extension.body.server_names[0].host_name)
)
if is_valid_sni_extension:
return extension.body.server_names[0].host_name.decode("ascii")
@ -51,3 +58,39 @@ class ClientHello:
def __repr__(self):
return f"ClientHello(sni: {self.sni}, alpn_protocols: {self.alpn_protocols})"
@dataclass
class ClientHelloData:
"""
Event data for `tls_clienthello` event hooks.
"""
context: context.Context
"""The context object for this connection."""
client_hello: ClientHello
"""The entire parsed TLS ClientHello."""
ignore_connection: bool = False
"""
If set to `True`, do not intercept this connection and forward encrypted contents unmodified.
"""
establish_server_tls_first: bool = False
"""
If set to `True`, pause this handshake and establish TLS with an upstream server first.
This makes it possible to process the server certificate when generating an interception certificate.
"""
@dataclass
class TlsData:
"""
Event data for `tls_start_client`, `tls_start_server`, and `tls_handshake` event hooks.
"""
conn: connection.Connection
"""The affected connection."""
context: context.Context
"""The context object for this connection."""
ssl_conn: Optional[SSL.Connection] = None
"""
The associated pyOpenSSL `SSL.Connection` object.
This will be set by an addon in the `tls_start_*` event hooks.
"""

View File

@ -6,10 +6,10 @@ from typing import Union
import pytest
from OpenSSL import SSL
from mitmproxy import certs, connection
from mitmproxy import certs, connection, tls
from mitmproxy.addons import tlsconfig
from mitmproxy.proxy import context
from mitmproxy.proxy.layers import modes, tls
from mitmproxy.proxy.layers import modes, tls as proxy_tls
from mitmproxy.test import taddons
from test.mitmproxy.proxy.layers import test_tls
@ -130,7 +130,7 @@ class TestTlsConfig:
)
ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options)
tls_start = tls.TlsHookData(ctx.client, context=ctx)
tls_start = tls.TlsData(ctx.client, context=ctx)
ta.tls_start_client(tls_start)
tssl_server = tls_start.ssl_conn
tssl_client = test_tls.SSLTest()
@ -145,7 +145,7 @@ class TestTlsConfig:
ctx.client.cipher_list = ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES128-SHA"]
ctx.server.address = ("example.mitmproxy.org", 443)
tls_start = tls.TlsHookData(ctx.server, context=ctx)
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)
@ -160,7 +160,7 @@ class TestTlsConfig:
tctx.configure(ta, ssl_verify_upstream_trusted_ca=tdata.path(
"mitmproxy/net/data/verificationcerts/trusted-root.crt"))
tls_start = tls.TlsHookData(ctx.server, context=ctx)
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)
@ -179,7 +179,7 @@ class TestTlsConfig:
http2=False,
ciphers_server="ALL"
)
tls_start = tls.TlsHookData(ctx.server, context=ctx)
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)
@ -190,7 +190,7 @@ class TestTlsConfig:
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.TlsHookData(ctx.server, context=ctx)
tls_start = tls.TlsData(ctx.server, context=ctx)
def assert_alpn(http2, client_offers, expected):
tctx.configure(ta, http2=http2)
@ -199,8 +199,8 @@ class TestTlsConfig:
ta.tls_start_server(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, proxy_tls.HTTP_ALPNS + (b"foo",), proxy_tls.HTTP_ALPNS + (b"foo",))
assert_alpn(False, proxy_tls.HTTP_ALPNS + (b"foo",), proxy_tls.HTTP1_ALPNS + (b"foo",))
assert_alpn(True, [], [])
assert_alpn(False, [], [])
ctx.client.timestamp_tls_setup = time.time()
@ -222,7 +222,7 @@ class TestTlsConfig:
modes.HttpProxy(ctx),
123
]
tls_start = tls.TlsHookData(ctx.client, context=ctx)
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"
@ -244,7 +244,7 @@ class TestTlsConfig:
ssl_verify_upstream_trusted_ca=tdata.path("mitmproxy/net/data/verificationcerts/trusted-root.crt"),
)
tls_start = tls.TlsHookData(ctx.server, context=ctx)
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)

View File

@ -9,6 +9,7 @@ from mitmproxy import connection
from mitmproxy.connection import ConnectionState, Server
from mitmproxy.proxy import commands, context, events, layer
from mitmproxy.proxy.layers import tls
from mitmproxy.tls import ClientHelloData, TlsData
from mitmproxy.utils import data
from test.mitmproxy.proxy import tutils
@ -68,8 +69,8 @@ def test_get_client_hello():
assert tls.get_client_hello(single_record) == client_hello_no_extensions
split_over_two_records = (
bytes.fromhex("1603010020") + client_hello_no_extensions[:32] +
bytes.fromhex("1603010045") + client_hello_no_extensions[32:]
bytes.fromhex("1603010020") + client_hello_no_extensions[:32] +
bytes.fromhex("1603010045") + client_hello_no_extensions[32:]
)
assert tls.get_client_hello(split_over_two_records) == client_hello_no_extensions
@ -134,9 +135,9 @@ def _test_echo(playbook: tutils.Playbook, tssl: SSLTest, conn: connection.Connec
tssl.obj.write(b"Hello World")
data = tutils.Placeholder(bytes)
assert (
playbook
>> events.DataReceived(conn, tssl.bio_read())
<< commands.SendData(conn, data)
playbook
>> events.DataReceived(conn, tssl.bio_read())
<< commands.SendData(conn, data)
)
tssl.bio_write(data())
assert tssl.obj.read() == b"hello world"
@ -156,13 +157,13 @@ class TlsEchoLayer(tutils.EchoLayer):
def finish_handshake(playbook: tutils.Playbook, conn: connection.Connection, tssl: SSLTest):
data = tutils.Placeholder(bytes)
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
>> events.DataReceived(conn, tssl.bio_read())
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.SendData(conn, data)
playbook
>> events.DataReceived(conn, tssl.bio_read())
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.SendData(conn, data)
)
assert tls_hook_data().conn.error is None
tssl.bio_write(data())
@ -173,7 +174,7 @@ def reply_tls_start_client(alpn: typing.Optional[bytes] = None, *args, **kwargs)
Helper function to simplify the syntax for tls_start_client hooks.
"""
def make_client_conn(tls_start: tls.TlsHookData) -> None:
def make_client_conn(tls_start: TlsData) -> None:
# ssl_context = SSL.Context(Method.TLS_METHOD)
# ssl_context.set_min_proto_version(SSL.TLS1_3_VERSION)
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
@ -198,7 +199,7 @@ def reply_tls_start_server(alpn: typing.Optional[bytes] = None, *args, **kwargs)
Helper function to simplify the syntax for tls_start_server hooks.
"""
def make_server_conn(tls_start: tls.TlsHookData) -> None:
def make_server_conn(tls_start: TlsData) -> None:
# ssl_context = SSL.Context(Method.TLS_METHOD)
# ssl_context.set_min_proto_version(SSL.TLS1_3_VERSION)
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
@ -243,9 +244,9 @@ class TestServerTLS:
layer.child_layer = TlsEchoLayer(tctx)
assert (
tutils.Playbook(layer)
>> events.DataReceived(tctx.client, b"Hello World")
<< commands.SendData(tctx.client, b"hello world")
tutils.Playbook(layer)
>> events.DataReceived(tctx.client, b"Hello World")
<< commands.SendData(tctx.client, b"hello world")
)
def test_simple(self, tctx):
@ -259,10 +260,10 @@ class TestServerTLS:
# send ClientHello, receive ClientHello
data = tutils.Placeholder(bytes)
assert (
playbook
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
playbook
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
)
tssl.bio_write(data())
with pytest.raises(ssl.SSLWantReadError):
@ -274,31 +275,31 @@ class TestServerTLS:
# finish handshake (locally)
tssl.do_handshake()
assert (
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< None
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< None
)
assert tctx.server.tls_established
# Echo
assert (
playbook
>> events.DataReceived(tctx.client, b"foo")
<< layer.NextLayerHook(tutils.Placeholder())
>> tutils.reply_next_layer(TlsEchoLayer)
<< commands.SendData(tctx.client, b"foo")
playbook
>> events.DataReceived(tctx.client, b"foo")
<< layer.NextLayerHook(tutils.Placeholder())
>> tutils.reply_next_layer(TlsEchoLayer)
<< commands.SendData(tctx.client, b"foo")
)
_test_echo(playbook, tssl, tctx.server)
with pytest.raises(ssl.SSLWantReadError):
tssl.obj.unwrap()
assert (
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< commands.CloseConnection(tctx.server)
>> events.ConnectionClosed(tctx.server)
<< None
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< commands.CloseConnection(tctx.server)
>> events.ConnectionClosed(tctx.server)
<< None
)
def test_untrusted_cert(self, tctx):
@ -312,15 +313,15 @@ class TestServerTLS:
# send ClientHello
data = tutils.Placeholder(bytes)
assert (
playbook
>> events.DataReceived(tctx.client, b"open-connection")
<< layer.NextLayerHook(tutils.Placeholder())
>> tutils.reply_next_layer(TlsEchoLayer)
<< commands.OpenConnection(tctx.server)
>> tutils.reply(None)
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
playbook
>> events.DataReceived(tctx.client, b"open-connection")
<< layer.NextLayerHook(tutils.Placeholder())
>> tutils.reply_next_layer(TlsEchoLayer)
<< commands.OpenConnection(tctx.server)
>> tutils.reply(None)
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
)
# receive ServerHello, finish client handshake
@ -328,16 +329,16 @@ class TestServerTLS:
with pytest.raises(ssl.SSLWantReadError):
tssl.do_handshake()
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< commands.Log("Server TLS handshake failed. Certificate verify failed: Hostname mismatch", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.server)
<< commands.SendData(tctx.client,
b"open-connection failed: Certificate verify failed: Hostname mismatch")
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
<< commands.Log("Server TLS handshake failed. Certificate verify failed: Hostname mismatch", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.server)
<< commands.SendData(tctx.client,
b"open-connection failed: Certificate verify failed: Hostname mismatch")
)
assert tls_hook_data().conn.error == "Certificate verify failed: Hostname mismatch"
assert not tctx.server.tls_established
@ -349,17 +350,17 @@ class TestServerTLS:
# send ClientHello, receive random garbage back
data = tutils.Placeholder(bytes)
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
>> events.DataReceived(tctx.server, b"HTTP/1.1 404 Not Found\r\n")
<< commands.Log("Server TLS handshake failed. The remote server does not speak TLS.", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.server)
playbook
<< tls.TlsStartServerHook(tutils.Placeholder())
>> reply_tls_start_server()
<< commands.SendData(tctx.server, data)
>> events.DataReceived(tctx.server, b"HTTP/1.1 404 Not Found\r\n")
<< commands.Log("Server TLS handshake failed. The remote server does not speak TLS.", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.server)
)
assert tls_hook_data().conn.error == "The remote server does not speak TLS."
@ -388,7 +389,7 @@ class TestServerTLS:
tssl.do_handshake()
# send back error
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
>> events.DataReceived(tctx.server, tssl.bio_read())
@ -402,8 +403,8 @@ class TestServerTLS:
def make_client_tls_layer(
tctx: context.Context,
**kwargs
tctx: context.Context,
**kwargs
) -> typing.Tuple[tutils.Playbook, tls.ClientTLSLayer, SSLTest]:
# This is a bit contrived as the client layer expects a server layer as parent.
# We also set child layers manually to avoid NextLayer noise.
@ -435,13 +436,13 @@ class TestClientTLS:
# Send ClientHello, receive ServerHello
data = tutils.Placeholder(bytes)
assert (
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply()
<< tls.TlsStartClientHook(tutils.Placeholder())
>> reply_tls_start_client()
<< commands.SendData(tctx.client, data)
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply()
<< tls.TlsStartClientHook(tutils.Placeholder())
>> reply_tls_start_client()
<< commands.SendData(tctx.client, data)
)
tssl_client.bio_write(data())
tssl_client.do_handshake()
@ -455,9 +456,9 @@ class TestClientTLS:
_test_echo(playbook, tssl_client, tctx.client)
other_server = Server(None)
assert (
playbook
>> events.DataReceived(other_server, b"Plaintext")
<< commands.SendData(other_server, b"plaintext")
playbook
>> events.DataReceived(other_server, b"Plaintext")
<< commands.SendData(other_server, b"plaintext")
)
@pytest.mark.parametrize("server_state", ["open", "closed"])
@ -474,14 +475,14 @@ class TestClientTLS:
# We should now get instructed to open a server connection.
data = tutils.Placeholder(bytes)
def require_server_conn(client_hello: tls.ClientHelloData) -> None:
def require_server_conn(client_hello: ClientHelloData) -> None:
client_hello.establish_server_tls_first = True
(
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply(side_effect=require_server_conn)
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply(side_effect=require_server_conn)
)
if server_state == "closed":
(
@ -503,12 +504,12 @@ class TestClientTLS:
data = tutils.Placeholder(bytes)
assert (
playbook
>> events.DataReceived(tctx.server, tssl_server.bio_read())
<< tls.TlsHandshakeHook(tutils.Placeholder())
>> tutils.reply()
<< commands.SendData(tctx.server, data)
<< tls.TlsStartClientHook(tutils.Placeholder())
playbook
>> events.DataReceived(tctx.server, tssl_server.bio_read())
<< tls.TlsHandshakeHook(tutils.Placeholder())
>> tutils.reply()
<< commands.SendData(tctx.server, data)
<< tls.TlsStartClientHook(tutils.Placeholder())
)
tssl_server.bio_write(data())
assert tctx.server.tls_established
@ -516,9 +517,9 @@ class TestClientTLS:
data = tutils.Placeholder(bytes)
assert (
playbook
>> reply_tls_start_client(alpn=b"quux")
<< commands.SendData(tctx.client, data)
playbook
>> reply_tls_start_client(alpn=b"quux")
<< commands.SendData(tctx.client, data)
)
tssl_client.bio_write(data())
tssl_client.do_handshake()
@ -544,7 +545,7 @@ class TestClientTLS:
playbook, client_layer, tssl_client = make_client_tls_layer(tctx, alpn=["quux"])
def make_passthrough(client_hello: tls.ClientHelloData) -> None:
def make_passthrough(client_hello: ClientHelloData) -> None:
client_hello.ignore_connection = True
client_hello = tssl_client.bio_read()
@ -570,17 +571,17 @@ class TestClientTLS:
def test_cannot_parse_clienthello(self, tctx: context.Context):
"""Test the scenario where we cannot parse the ClientHello"""
playbook, client_layer, tssl_client = make_client_tls_layer(tctx)
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
invalid = b"\x16\x03\x01\x00\x00"
assert (
playbook
>> events.DataReceived(tctx.client, invalid)
<< commands.Log(f"Client TLS handshake failed. Cannot parse ClientHello: {invalid.hex()}", level="warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.client)
playbook
>> events.DataReceived(tctx.client, invalid)
<< commands.Log(f"Client TLS handshake failed. Cannot parse ClientHello: {invalid.hex()}", level="warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.client)
)
assert tls_hook_data().conn.error
assert not tctx.client.tls_established
@ -601,28 +602,28 @@ class TestClientTLS:
data = tutils.Placeholder(bytes)
assert (
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply()
<< tls.TlsStartClientHook(tutils.Placeholder())
>> reply_tls_start_client()
<< commands.SendData(tctx.client, data)
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< tls.TlsClienthelloHook(tutils.Placeholder())
>> tutils.reply()
<< tls.TlsStartClientHook(tutils.Placeholder())
>> reply_tls_start_client()
<< commands.SendData(tctx.client, data)
)
tssl_client.bio_write(data())
with pytest.raises(ssl.SSLCertVerificationError):
tssl_client.do_handshake()
# Finish Handshake
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< commands.Log("Client TLS handshake failed. The client does not trust the proxy's certificate "
"for wrong.host.mitmproxy.org (sslv3 alert bad certificate)", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.client)
>> events.ConnectionClosed(tctx.client)
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())
<< commands.Log("Client TLS handshake failed. The client does not trust the proxy's certificate "
"for wrong.host.mitmproxy.org (sslv3 alert bad certificate)", "warn")
<< tls.TlsHandshakeHook(tls_hook_data)
>> tutils.reply()
<< commands.CloseConnection(tctx.client)
>> events.ConnectionClosed(tctx.client)
)
assert not tctx.client.tls_established
assert tls_hook_data().conn.error
@ -634,7 +635,7 @@ class TestClientTLS:
the proxy certificate."""
playbook, client_layer, tssl_client = make_client_tls_layer(tctx, sni=b"wrong.host.mitmproxy.org")
playbook.logs = True
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
playbook >> events.DataReceived(tctx.client, tssl_client.bio_read())
playbook << tls.TlsClienthelloHook(tutils.Placeholder())
@ -687,7 +688,7 @@ class TestClientTLS:
playbook, client_layer, tssl_client = make_client_tls_layer(tctx, max_ver=ssl.TLSVersion.TLSv1_2)
playbook.logs = True
tls_hook_data = tutils.Placeholder(tls.TlsHookData)
tls_hook_data = tutils.Placeholder(TlsData)
assert (
playbook
>> events.DataReceived(tctx.client, tssl_client.bio_read())