Esempio n. 1
0
    def state_auth(self):
        if len(self.buf) < 3:
            return

        # Parsing username and password, which is somewhat atrocious
        user_len = self.buf[1]
        if len(self.buf) < 3 + user_len:
            return
        pass_len = self.buf[2 + user_len]
        if len(self.buf) < 3 + user_len + pass_len:
            return
        user = self.buf[2:(2 + user_len)].decode("utf-8", "backslashreplace")
        password = self.buf[(3 + user_len):(3 + user_len + pass_len)].decode("utf-8", "backslashreplace")

        data = Socks5AuthData(self.context.client, user, password)
        yield Socks5AuthHook(data)
        if not data.valid:
            # The VER field contains the current **version of the subnegotiation**, which is X'01'.
            yield commands.SendData(self.context.client, b"\x01\x01")
            yield from self.socks_err("authentication failed")
            return

        yield commands.SendData(self.context.client, b"\x01\x00")
        self.buf = self.buf[3 + user_len + pass_len:]
        self.state = self.state_connect
        yield from self.state()
Esempio n. 2
0
    def test_server_required(self, tctx):
        """
        Test the scenario where a server connection is required (for example, because of an unknown ALPN)
        to establish TLS with the client.
        """
        tssl_server = SSLTest(server_side=True, alpn=["quux"])
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx, alpn=["quux"])

        # We should now get instructed to open a server connection.
        data = tutils.Placeholder(bytes)

        def require_server_conn(client_hello: tls.ClientHelloData) -> None:
            client_hello.establish_server_tls_first = True

        assert (
                playbook
                >> events.DataReceived(tctx.client, tssl_client.bio_read())
                << tls.TlsClienthelloHook(tutils.Placeholder())
                >> tutils.reply(side_effect=require_server_conn)
                << commands.OpenConnection(tctx.server)
                >> tutils.reply(None)
                << tls.TlsStartHook(tutils.Placeholder())
                >> reply_tls_start(alpn=b"quux")
                << commands.SendData(tctx.server, data)
        )

        # Establish TLS with the server...
        tssl_server.bio_write(data())
        with pytest.raises(ssl.SSLWantReadError):
            tssl_server.do_handshake()

        data = tutils.Placeholder(bytes)
        assert (
                playbook
                >> events.DataReceived(tctx.server, tssl_server.bio_read())
                << commands.SendData(tctx.server, data)
                << tls.TlsStartHook(tutils.Placeholder())
        )
        tssl_server.bio_write(data())
        assert tctx.server.tls_established
        # Server TLS is established, we can now reply to the client handshake...

        data = tutils.Placeholder(bytes)
        assert (
                playbook
                >> reply_tls_start(alpn=b"quux")
                << commands.SendData(tctx.client, data)
        )
        tssl_client.bio_write(data())
        tssl_client.do_handshake()
        interact(playbook, tctx.client, tssl_client)

        # Both handshakes completed!
        assert tctx.client.tls_established
        assert tctx.server.tls_established
        assert tctx.server.sni == tctx.client.sni
        assert tctx.client.alpn == b"quux"
        assert tctx.server.alpn == b"quux"
        _test_echo(playbook, tssl_server, tctx.server)
        _test_echo(playbook, tssl_client, tctx.client)
Esempio n. 3
0
    def relay_messages(
            self,
            event: events.ConnectionEvent) -> layer.CommandGenerator[None]:
        from_client = event.connection == self.context.client
        send_to: Connection
        if from_client:
            send_to = self.context.server
        else:
            send_to = self.context.client

        if isinstance(event, events.DataReceived):
            if self.flow:
                tcp_message = tcp.TCPMessage(from_client, event.data)
                self.flow.messages.append(tcp_message)
                yield TcpMessageHook(self.flow)
                yield commands.SendData(send_to, tcp_message.content)
            else:
                yield commands.SendData(send_to, event.data)

        elif isinstance(event, events.ConnectionClosed):
            all_done = not (
                (self.context.client.state & ConnectionState.CAN_READ) or
                (self.context.server.state & ConnectionState.CAN_READ))
            if all_done:
                if self.context.server.state is not ConnectionState.CLOSED:
                    yield commands.CloseConnection(self.context.server)
                if self.context.client.state is not ConnectionState.CLOSED:
                    yield commands.CloseConnection(self.context.client)
                self._handle_event = self.done
                if self.flow:
                    yield TcpEndHook(self.flow)
            else:
                yield commands.CloseConnection(send_to, half_close=True)
Esempio n. 4
0
    def test_client_only(self, tctx: context.Context):
        """Test TLS with client only"""
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx)
        client_layer.debug = "  "
        assert not tctx.client.tls_established

        # 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.TlsStartHook(
                    tutils.Placeholder()) >> reply_tls_start() <<
                commands.SendData(tctx.client, data))
        tssl_client.bio_write(data())
        tssl_client.do_handshake()
        # Finish Handshake
        interact(playbook, tctx.client, tssl_client)

        assert tssl_client.obj.getpeercert(True)
        assert tctx.client.tls_established

        # Echo
        _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"))
Esempio n. 5
0
    def test_untrusted_cert(self, tctx):
        """If the certificate is not trusted, we should fail."""
        playbook = tutils.Playbook(tls.ServerTLSLayer(tctx))
        tctx.server.address = ("wrong.host.mitmproxy.org", 443)
        tctx.server.sni = "wrong.host.mitmproxy.org"

        tssl = SSLTest(server_side=True)

        # 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.TlsStartHook(tutils.Placeholder()) >> reply_tls_start()
                << commands.SendData(tctx.server, data))

        # receive ServerHello, finish client handshake
        tssl.bio_write(data())
        with pytest.raises(ssl.SSLWantReadError):
            tssl.do_handshake()

        assert (playbook >> events.DataReceived(tctx.server, tssl.bio_read(
        )) << commands.Log(
            "Server TLS handshake failed. Certificate verify failed: Hostname mismatch",
            "warn"
        ) << commands.CloseConnection(tctx.server) << commands.SendData(
            tctx.client,
            b"open-connection failed: Certificate verify failed: Hostname mismatch"
        ))
        assert not tctx.server.tls_established
Esempio n. 6
0
    def test_passthrough_from_clienthello(self, tctx, server_state):
        """
        Test the scenario where the connection is moved to passthrough mode in the tls_clienthello hook.
        """
        if server_state == "open":
            tctx.server.timestamp_start = time.time()
            tctx.server.state = ConnectionState.OPEN

        playbook, client_layer, tssl_client = make_client_tls_layer(
            tctx, alpn=["quux"])

        def make_passthrough(client_hello: ClientHelloData) -> None:
            client_hello.ignore_connection = True

        client_hello = tssl_client.bio_read()
        (playbook >> events.DataReceived(tctx.client, client_hello) <<
         tls.TlsClienthelloHook(tutils.Placeholder()) >>
         tutils.reply(side_effect=make_passthrough))
        if server_state == "closed":
            (playbook << commands.OpenConnection(tctx.server) >>
             tutils.reply(None))
        assert (playbook << commands.SendData(
            tctx.server, client_hello)  # passed through unmodified
                >> events.DataReceived(
                    tctx.server,
                    b"ServerHello")  # and the same for the serverhello.
                << commands.SendData(tctx.client, b"ServerHello"))
Esempio n. 7
0
    def test_simple(self, tctx):
        nl = layer.NextLayer(tctx, ask_on_start=True)
        nl.debug = "  "
        playbook = tutils.Playbook(nl, hooks=True)

        assert (
                playbook
                << layer.NextLayerHook(nl)
                >> tutils.reply()
                >> events.DataReceived(tctx.client, b"foo")
                << layer.NextLayerHook(nl)
                >> tutils.reply()
                >> events.DataReceived(tctx.client, b"bar")
                << layer.NextLayerHook(nl)
        )
        assert nl.data_client() == b"foobar"
        assert nl.data_server() == b""

        nl.layer = tutils.EchoLayer(tctx)
        assert (
                playbook
                >> tutils.reply()
                << commands.SendData(tctx.client, b"foo")
                << commands.SendData(tctx.client, b"bar")
        )
Esempio n. 8
0
    def state_connect(self):
        # Parse Connect Request
        if len(self.buf) < 5:
            return

        if self.buf[:3] != b"\x05\x01\x00":
            yield from self.socks_err(f"Unsupported SOCKS5 request: {self.buf!r}", SOCKS5_REP_COMMAND_NOT_SUPPORTED)
            return

        # Determine message length
        atyp = self.buf[3]
        message_len: int
        if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
            message_len = 4 + 4 + 2
        elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
            message_len = 4 + 16 + 2
        elif atyp == SOCKS5_ATYP_DOMAINNAME:
            message_len = 4 + 1 + self.buf[4] + 2
        else:
            yield from self.socks_err(f"Unknown address type: {atyp}", SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED)
            return

        # Do we have enough bytes yet?
        if len(self.buf) < message_len:
            return

        # Parse host and port
        msg, self.buf = self.buf[:message_len], self.buf[message_len:]

        host: str
        if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
            host = socket.inet_ntop(socket.AF_INET, msg[4:-2])
        elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
            host = socket.inet_ntop(socket.AF_INET6, msg[4:-2])
        else:
            host_bytes = msg[5:-2]
            host = host_bytes.decode("ascii", "replace")

        port, = struct.unpack("!H", msg[-2:])

        # We now have all we need, let's get going.
        self.context.server.address = (host, port)
        self.child_layer = layer.NextLayer(self.context)

        # this already triggers the child layer's Start event,
        # but that's not a problem in practice...
        err = yield from self.finish_start()
        if err:
            yield commands.SendData(self.context.client, b"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00")
            yield commands.CloseConnection(self.context.client)
        else:
            yield commands.SendData(self.context.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
            if self.buf:
                yield from self.child_layer.handle_event(events.DataReceived(self.context.client, self.buf))
                del self.buf
Esempio n. 9
0
    def test_simple(self, tctx):
        playbook = tutils.Playbook(tls.ServerTLSLayer(tctx))
        tctx.server.state = ConnectionState.OPEN
        tctx.server.address = ("example.mitmproxy.org", 443)
        tctx.server.sni = b"example.mitmproxy.org"

        tssl = SSLTest(server_side=True)

        # send ClientHello
        data = tutils.Placeholder(bytes)
        assert (
                playbook
                << tls.TlsStartHook(tutils.Placeholder())
                >> reply_tls_start()
                << commands.SendData(tctx.server, data)
        )

        # receive ServerHello, finish client handshake
        tssl.bio_write(data())
        with pytest.raises(ssl.SSLWantReadError):
            tssl.do_handshake()
        interact(playbook, tctx.server, tssl)

        # finish server handshake
        tssl.do_handshake()
        assert (
                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")
        )
        _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
        )
Esempio n. 10
0
    def state_greet(self):
        if len(self.buf) < 2:
            return

        if self.buf[0] != SOCKS5_VERSION:
            if self.buf[:3].isupper():
                guess = "Probably not a SOCKS request but a regular HTTP request. "
            else:
                guess = ""
            yield from self.socks_err(guess + "Invalid SOCKS version. Expected 0x05, got 0x%x" % self.buf[0])
            return

        n_methods = self.buf[1]
        if len(self.buf) < 2 + n_methods:
            return

        if "proxyauth" in self.context.options and self.context.options.proxyauth:
            method = SOCKS5_METHOD_USER_PASSWORD_AUTHENTICATION
            self.state = self.state_auth
        else:
            method = SOCKS5_METHOD_NO_AUTHENTICATION_REQUIRED
            self.state = self.state_connect

        if method not in self.buf[2:2 + n_methods]:
            method_str = "user/password" if method == SOCKS5_METHOD_USER_PASSWORD_AUTHENTICATION else "no"
            yield from self.socks_err(
                f"Client does not support SOCKS5 with {method_str} authentication.",
                SOCKS5_METHOD_NO_ACCEPTABLE_METHODS
            )
            return
        yield commands.SendData(self.context.client, bytes([SOCKS5_VERSION, method]))
        self.buf = self.buf[2 + n_methods:]
        yield from self.state()
Esempio n. 11
0
 def patched_receive_handshake_data(
     self, data
 ) -> layer.CommandGenerator[typing.Tuple[bool, typing.Optional[str]]]:
     self.buf += data
     response_head = self.buf.maybe_extract_lines()
     if response_head:
         response_head = [bytes(x) for x in response_head]
         try:
             response = http1.read_response_head(response_head)
         except ValueError:
             return True, None
         challenge_message = extract_proxy_authenticate_msg(
             response_head)
         if 200 <= response.status_code < 300:
             if self.buf:
                 yield from self.receive_data(data)
                 del self.buf
             return True, None
         else:
             if not challenge_message:
                 return True, None
             proxy_authorization = self.ntlm_context.get_ntlm_challenge_response_message(
                 challenge_message)
             self.flow = build_connect_flow(
                 self.context,
                 ("Proxy-Authorization", proxy_authorization))
             raw = http1.assemble_request(self.flow.request)
             yield commands.SendData(self.tunnel_connection, raw)
             return False, None
     else:
         return False, None
Esempio n. 12
0
    def test_late_hook_reply(self, tctx: Context):
        """
        Properly handle case where we receive an additional event while we are waiting for
        a reply from the proxy core.
        """
        nl = layer.NextLayer(tctx)
        playbook = tutils.Playbook(nl)

        assert (playbook >> events.DataReceived(tctx.client, b"foo") <<
                layer.NextLayerHook(nl) >> events.DataReceived(
                    tctx.client, b"bar"))
        assert nl.data_client() == b"foo"  # "bar" is paused.
        nl.layer = tutils.EchoLayer(tctx)

        assert (playbook >> tutils.reply(to=-2) << commands.SendData(
            tctx.client, b"foo") << commands.SendData(tctx.client, b"bar"))
Esempio n. 13
0
 def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]:
     if isinstance(event, events.DataReceived) and event.data == b"open-connection":
         err = yield commands.OpenConnection(self.context.server)
         if err:
             yield commands.SendData(event.connection, f"open-connection failed: {err}".encode())
     else:
         yield from super()._handle_event(event)
Esempio n. 14
0
 def start_handshake(self) -> layer.CommandGenerator[None]:
     if self.tunnel_connection.tls:
         # "Secure Web Proxy": We may have negotiated an ALPN when connecting to the upstream proxy.
         # The semantics are not really clear here, but we make sure that if we negotiated h2,
         # we act as an h2 client.
         self.conn.alpn = self.tunnel_connection.alpn
     if not self.send_connect:
         return (yield from super().start_handshake())
     assert self.conn.address
     req = http.Request(
         host=self.conn.address[0],
         port=self.conn.address[1],
         method=b"CONNECT",
         scheme=b"",
         authority=f"{self.conn.address[0]}:{self.conn.address[1]}".encode(
         ),
         path=b"",
         http_version=b"HTTP/1.1",
         headers=http.Headers(),
         content=b"",
         trailers=None,
         timestamp_start=time.time(),
         timestamp_end=time.time(),
     )
     raw = http1.assemble_request(req)
     yield commands.SendData(self.tunnel_connection, raw)
Esempio n. 15
0
    def test_immediate_disconnect(self, tctx: context.Context, close_at):
        """Test the scenario where the client is disconnecting during the handshake.
        This may happen because they are not interested in the connection anymore, or because they do not like
        the proxy certificate."""
        playbook, client_layer, tssl_client = make_client_tls_layer(
            tctx, sni=b"wrong.host.mitmproxy.org")
        playbook.logs = True

        playbook >> events.DataReceived(tctx.client, tssl_client.bio_read())
        playbook << tls.TlsClienthelloHook(tutils.Placeholder())

        if close_at == "tls_clienthello":
            assert (playbook >> events.ConnectionClosed(
                tctx.client) >> tutils.reply(to=-2) << tls.TlsStartClientHook(
                    tutils.Placeholder()) >> reply_tls_start_client() <<
                    commands.CloseConnection(tctx.client))
            return

        playbook >> tutils.reply()
        playbook << tls.TlsStartClientHook(tutils.Placeholder())

        if close_at == "tls_start_client":
            assert (playbook >> events.ConnectionClosed(tctx.client) >>
                    reply_tls_start_client(to=-2) << commands.CloseConnection(
                        tctx.client))
            return

        assert (playbook >> reply_tls_start_client() << commands.SendData(
            tctx.client, tutils.Placeholder()
        ) >> events.ConnectionClosed(tctx.client) << commands.Log(
            "Client TLS handshake failed. The client disconnected during the handshake. "
            "If this happens consistently for wrong.host.mitmproxy.org, this may indicate that the "
            "client does not trust the proxy's certificate.", "info") <<
                commands.CloseConnection(tctx.client))
Esempio n. 16
0
    def test_unsupported_protocol(self, tctx: context.Context):
        """Test the scenario where the server only supports an outdated TLS version by default."""
        playbook = tutils.Playbook(tls.ServerTLSLayer(tctx))
        tctx.server.address = ("example.mitmproxy.org", 443)
        tctx.server.state = ConnectionState.OPEN
        tctx.server.sni = "example.mitmproxy.org"

        # noinspection PyTypeChecker
        tssl = SSLTest(server_side=True, max_ver=ssl.TLSVersion.TLSv1_2)

        # send ClientHello
        data = tutils.Placeholder(bytes)
        assert (
            playbook << tls.TlsStartServerHook(tutils.Placeholder()) >>
            reply_tls_start_server() << commands.SendData(tctx.server, data))

        # receive ServerHello
        tssl.bio_write(data())
        with pytest.raises(ssl.SSLError):
            tssl.do_handshake()

        # send back error
        tls_hook_data = tutils.Placeholder(TlsData)
        assert (playbook >> events.DataReceived(tctx.server, tssl.bio_read(
        )) << commands.Log(
            "Server TLS handshake failed. The remote server and mitmproxy cannot agree on a TLS version"
            " to use. You may need to adjust mitmproxy's tls_version_server_min option.",
            "warn") << tls.TlsFailedServerHook(tls_hook_data) >>
                tutils.reply() << commands.CloseConnection(tctx.server))
        assert tls_hook_data().conn.error
Esempio n. 17
0
    def test_mitmproxy_ca_is_untrusted(self, tctx: context.Context):
        """Test the scenario where the client doesn't trust the mitmproxy CA."""
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx, sni=b"wrong.host.mitmproxy.org")
        playbook.logs = True

        data = tutils.Placeholder(bytes)
        assert (
                playbook
                >> events.DataReceived(tctx.client, tssl_client.bio_read())
                << tls.TlsClienthelloHook(tutils.Placeholder())
                >> tutils.reply()
                << tls.TlsStartHook(tutils.Placeholder())
                >> reply_tls_start()
                << commands.SendData(tctx.client, data)
        )
        tssl_client.bio_write(data())
        with pytest.raises(ssl.SSLCertVerificationError):
            tssl_client.do_handshake()
        # Finish Handshake
        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")
                << commands.CloseConnection(tctx.client)
                >> events.ConnectionClosed(tctx.client)
        )
        assert not tctx.client.tls_established
Esempio n. 18
0
    def send(self, event: HttpEvent) -> layer.CommandGenerator[None]:
        assert event.stream_id == self.stream_id
        if isinstance(event, ResponseHeaders):
            self.response = response = event.response

            if response.is_http2:
                response = response.copy()
                # Convert to an HTTP/1 response.
                response.http_version = "HTTP/1.1"
                # not everyone supports empty reason phrases, so we better make up one.
                response.reason = status_codes.RESPONSES.get(
                    response.status_code, "")
                # Shall we set a Content-Length header here if there is none?
                # For now, let's try to modify as little as possible.

            raw = http1.assemble_response_head(response)
            yield commands.SendData(self.conn, raw)
        elif isinstance(event, ResponseData):
            assert self.response
            if "chunked" in self.response.headers.get("transfer-encoding",
                                                      "").lower():
                raw = b"%x\r\n%s\r\n" % (len(event.data), event.data)
            else:
                raw = event.data
            if raw:
                yield commands.SendData(self.conn, raw)
        elif isinstance(event, ResponseEndOfMessage):
            assert self.response
            if "chunked" in self.response.headers.get("transfer-encoding",
                                                      "").lower():
                yield commands.SendData(self.conn, b"0\r\n\r\n")
            yield from self.mark_done(response=True)
        elif isinstance(event, ResponseProtocolError):
            if not self.response and event.code != status_codes.NO_RESPONSE:
                resp = http.Response.make(
                    event.code, format_error(event.code, event.message),
                    http.Headers(
                        Server=version.MITMPROXY,
                        Connection="close",
                        Content_Type="text/html",
                    ))
                raw = http1.assemble_response(resp)
                yield commands.SendData(self.conn, raw)
            if self.conn.state & ConnectionState.CAN_WRITE:
                yield commands.CloseConnection(self.conn)
        else:
            raise AssertionError(f"Unexpected event: {event}")
Esempio n. 19
0
    def relay_messages(self,
                       event: events.Event) -> layer.CommandGenerator[None]:

        if isinstance(event, TcpMessageInjected):
            # we just spoof that we received data here and then process that regularly.
            event = events.DataReceived(
                self.context.client
                if event.message.from_client else self.context.server,
                event.message.content,
            )

        assert isinstance(event, events.ConnectionEvent)

        from_client = event.connection == self.context.client
        send_to: Connection
        if from_client:
            send_to = self.context.server
        else:
            send_to = self.context.client

        if isinstance(event, events.DataReceived):
            if self.flow:
                tcp_message = tcp.TCPMessage(from_client, event.data)
                self.flow.messages.append(tcp_message)
                yield TcpMessageHook(self.flow)
                yield commands.SendData(send_to, tcp_message.content)
            else:
                yield commands.SendData(send_to, event.data)

        elif isinstance(event, events.ConnectionClosed):
            all_done = not (
                (self.context.client.state & ConnectionState.CAN_READ) or
                (self.context.server.state & ConnectionState.CAN_READ))
            if all_done:
                if self.context.server.state is not ConnectionState.CLOSED:
                    yield commands.CloseConnection(self.context.server)
                if self.context.client.state is not ConnectionState.CLOSED:
                    yield commands.CloseConnection(self.context.client)
                self._handle_event = self.done
                if self.flow:
                    yield TcpEndHook(self.flow)
                    self.flow.live = False
            else:
                yield commands.CloseConnection(send_to, half_close=True)
        else:
            raise AssertionError(f"Unexpected event: {event}")
Esempio n. 20
0
def interact(playbook: tutils.Playbook, conn: context.Connection, tssl: SSLTest):
    data = tutils.Placeholder(bytes)
    assert (
            playbook
            >> events.DataReceived(conn, tssl.bio_read())
            << commands.SendData(conn, data)
    )
    tssl.bio_write(data())
Esempio n. 21
0
    def test_not_connected(self, tctx: context.Context):
        """Test that we don't do anything if no server connection exists."""
        layer = tls.ServerTLSLayer(tctx)
        layer.child_layer = TlsEchoLayer(tctx)

        assert (tutils.Playbook(layer) >> events.DataReceived(
            tctx.client, b"Hello World") << commands.SendData(
                tctx.client, b"hello world"))
Esempio n. 22
0
def _test_echo(playbook: tutils.Playbook, tssl: SSLTest,
               conn: connection.Connection) -> None:
    tssl.obj.write(b"Hello World")
    data = tutils.Placeholder(bytes)
    assert (playbook >> events.DataReceived(conn, tssl.bio_read()) <<
            commands.SendData(conn, data))
    tssl.bio_write(data())
    assert tssl.obj.read() == b"hello world"
Esempio n. 23
0
 def tls_interact(self) -> layer.CommandGenerator[None]:
     while True:
         try:
             data = self.tls.bio_read(65535)
         except SSL.WantReadError:
             return  # Okay, nothing more waiting to be sent.
         else:
             yield commands.SendData(self.conn, data)
Esempio n. 24
0
 def patched_start_handshake(self) -> layer.CommandGenerator[None]:
     assert self.conn.address
     self.ntlm_context = CustomNTLMContext(ctx)
     proxy_authorization = self.ntlm_context.get_ntlm_start_negotiate_message(
     )
     self.flow = build_connect_flow(
         self.context, ("Proxy-Authorization", proxy_authorization))
     yield HttpConnectUpstreamHook(self.flow)
     raw = http1.assemble_request(self.flow.request)
     yield commands.SendData(self.tunnel_connection, raw)
Esempio n. 25
0
    def send(self, event: HttpEvent) -> layer.CommandGenerator[None]:
        if isinstance(event, RequestProtocolError):
            yield commands.CloseConnection(self.conn)
            return

        if not self.stream_id:
            assert isinstance(event, RequestHeaders)
            self.stream_id = event.stream_id
            self.request = event.request
        assert self.stream_id == event.stream_id

        if isinstance(event, RequestHeaders):
            request = event.request
            if request.is_http2:
                # Convert to an HTTP/1 request.
                request = request.copy(
                )  # (we could probably be a bit more efficient here.)
                request.http_version = "HTTP/1.1"
                if "Host" not in request.headers and request.authority:
                    request.headers.insert(0, "Host", request.authority)
                request.authority = ""
            raw = http1.assemble_request_head(request)
            yield commands.SendData(self.conn, raw)
        elif isinstance(event, RequestData):
            assert self.request
            if "chunked" in self.request.headers.get("transfer-encoding",
                                                     "").lower():
                raw = b"%x\r\n%s\r\n" % (len(event.data), event.data)
            else:
                raw = event.data
            if raw:
                yield commands.SendData(self.conn, raw)
        elif isinstance(event, RequestEndOfMessage):
            assert self.request
            if "chunked" in self.request.headers.get("transfer-encoding",
                                                     "").lower():
                yield commands.SendData(self.conn, b"0\r\n\r\n")
            elif http1.expected_http_body_size(self.request,
                                               self.response) == -1:
                yield commands.CloseConnection(self.conn, half_close=True)
            yield from self.mark_done(request=True)
        else:
            raise AssertionError(f"Unexpected event: {event}")
Esempio n. 26
0
    def test_func_references(self, tctx: Context):
        nl = layer.NextLayer(tctx)
        playbook = tutils.Playbook(nl)

        assert (playbook >> events.DataReceived(tctx.client, b"foo") <<
                layer.NextLayerHook(nl))
        nl.layer = tutils.EchoLayer(tctx)
        handle = nl.handle_event
        assert (playbook >> tutils.reply() << commands.SendData(
            tctx.client, b"foo"))
        sd, = handle(events.DataReceived(tctx.client, b"bar"))
        assert isinstance(sd, commands.SendData)
Esempio n. 27
0
 def start_handshake(self) -> layer.CommandGenerator[None]:
     if self.tunnel_connection.tls:
         # "Secure Web Proxy": We may have negotiated an ALPN when connecting to the upstream proxy.
         # The semantics are not really clear here, but we make sure that if we negotiated h2,
         # we act as an h2 client.
         self.conn.alpn = self.tunnel_connection.alpn
     if not self.send_connect:
         return (yield from super().start_handshake())
     assert self.conn.address
     req = http.make_connect_request(self.conn.address)
     raw = http1.assemble_request(req)
     yield commands.SendData(self.tunnel_connection, raw)
Esempio n. 28
0
def finish_handshake(playbook: tutils.Playbook, conn: connection.Connection,
                     tssl: SSLTest):
    data = tutils.Placeholder(bytes)
    tls_hook_data = tutils.Placeholder(TlsData)
    if isinstance(conn, connection.Client):
        established_hook = tls.TlsEstablishedClientHook(tls_hook_data)
    else:
        established_hook = tls.TlsEstablishedServerHook(tls_hook_data)
    assert (
        playbook >> events.DataReceived(conn, tssl.bio_read()) <<
        established_hook >> tutils.reply() << commands.SendData(conn, data))
    assert tls_hook_data().conn.error is None
    tssl.bio_write(data())
Esempio n. 29
0
 def socks_err(
     self,
     message: str,
     reply_code: Optional[int] = None,
 ) -> layer.CommandGenerator[None]:
     if reply_code is not None:
         yield commands.SendData(
             self.context.client,
             bytes([SOCKS5_VERSION, reply_code]) +
             b"\x00\x01\x00\x00\x00\x00\x00\x00")
     yield commands.CloseConnection(self.context.client)
     yield commands.Log(message)
     self._handle_event = self.done
Esempio n. 30
0
    def test_remote_speaks_no_tls(self, tctx):
        playbook = tutils.Playbook(tls.ServerTLSLayer(tctx))
        tctx.server.state = ConnectionState.OPEN
        tctx.server.sni = "example.mitmproxy.org"

        # send ClientHello, receive random garbage back
        data = tutils.Placeholder(bytes)
        assert (playbook << tls.TlsStartHook(tutils.Placeholder(
        )) >> reply_tls_start(
        ) << 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") << commands.CloseConnection(tctx.server))