def get_client_hello(rfile):
    """
    Peek into the socket and read all records that contain the initial client hello message.

    client_conn:
        The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.

    Returns:
        The raw handshake packet bytes, without TLS record header(s).
    """
    client_hello = b""
    client_hello_size = 1
    offset = 0
    while len(client_hello) < client_hello_size:
        record_header = rfile.peek(offset + 5)[offset:]
        if not is_tls_record_magic(record_header) or len(record_header) < 5:
            raise exceptions.TlsProtocolException(
                'Expected TLS record, got "%s" instead.' % record_header)
        record_size = struct.unpack_from("!H", record_header, 3)[0] + 5
        record_body = rfile.peek(offset + record_size)[offset + 5:]
        if len(record_body) != record_size - 5:
            raise exceptions.TlsProtocolException(
                "Unexpected EOF in TLS handshake: %s" % record_body)
        client_hello += record_body
        offset += record_size
        client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4
    return client_hello
Пример #2
0
    def _establish_tls_with_server(self):
        self.log("Establish TLS with server", "debug")
        try:
            alpn = None
            if self._client_tls:
                if self._client_hello.alpn_protocols:
                    # We only support http/1.1 and h2.
                    # If the server only supports spdy (next to http/1.1), it may select that
                    # and mitmproxy would enter TCP passthrough mode, which we want to avoid.
                    alpn = [x for x in self._client_hello.alpn_protocols if not (x.startswith(b"h2-") or x.startswith(b"spdy"))]
                if alpn and b"h2" in alpn and not self.config.options.http2:
                    alpn.remove(b"h2")

            if self.client_conn.ssl_established:
                # If the client has already negotiated an ALP, then force the
                # server to use the same. This can only happen if the host gets
                # changed after the initial connection was established. E.g.:
                #   * the client offers http/1.1 and h2,
                #   * the initial host is only capable of http/1.1,
                #   * then the first server connection negotiates http/1.1,
                #   * but after the server_conn change, the new host offers h2
                #   * which results in garbage because the layers don' match.
                alpn = [self.client_conn.connection.get_alpn_proto_negotiated()]

            ciphers_server = self.config.options.ciphers_server
            if not ciphers_server and self._client_tls:
                ciphers_server = []
                for id in self._client_hello.cipher_suites:
                    if id in CIPHER_ID_NAME_MAP.keys():
                        ciphers_server.append(CIPHER_ID_NAME_MAP[id])
                ciphers_server = ':'.join(ciphers_server)

            self.server_conn.establish_ssl(
                self.config.clientcerts,
                self.server_sni,
                method=self.config.openssl_method_server,
                options=self.config.openssl_options_server,
                verify_options=self.config.openssl_verification_mode_server,
                ca_path=self.config.options.ssl_verify_upstream_trusted_cadir,
                ca_pemfile=self.config.options.ssl_verify_upstream_trusted_ca,
                cipher_list=ciphers_server,
                alpn_protos=alpn,
            )
            tls_cert_err = self.server_conn.ssl_verification_error
            if tls_cert_err is not None:
                self.log(str(tls_cert_err), "warn")
                self.log("Ignoring server verification error, continuing with connection", "warn")
        except exceptions.InvalidCertificateException as e:
            raise exceptions.InvalidServerCertificate(str(e))
        except exceptions.TlsException as e:
            raise exceptions.TlsProtocolException(
                "Cannot establish TLS with {address} (sni: {sni}): {e}".format(
                    address=repr(self.server_conn.address),
                    sni=self.server_sni,
                    e=repr(e)
                )
            )

        proto = self.alpn_for_client_connection.decode() if self.alpn_for_client_connection else '-'
        self.log("ALPN selected by server: {}".format(proto), "debug")
    def from_file(cls, client_conn) -> "ClientHello":
        """
        Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
        client_conn:
            The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
        Returns:
            :py:class:`client hello <mitmproxy.net.tls.ClientHello>`.
        """
        try:
            raw_client_hello = get_client_hello(client_conn)[4:]  # exclude handshake header.
        except exceptions.ProtocolException as e:
            raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))

        try:
            return cls(raw_client_hello)
        except EOFError as e:
            raise exceptions.TlsProtocolException(
                f"Cannot parse Client Hello: {e!r}, Raw Client Hello: {binascii.hexlify(raw_client_hello)!r}"
            )
Пример #4
0
    def from_client_conn(cls, client_conn):
        """
        Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
        client_conn:
            The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
        Returns:
            :py:class:`client hello <mitmproxy.proxy.protocol.tls.TlsClientHello>`.
        """
        try:
            raw_client_hello = get_client_hello(client_conn)[4:]  # exclude handshake header.
        except exceptions.ProtocolException as e:
            raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))

        try:
            return cls(raw_client_hello)
        except EOFError as e:
            raise exceptions.TlsProtocolException(
                'Cannot parse Client Hello: %s, Raw Client Hello: %s' %
                (repr(e), raw_client_hello.encode("hex"))
            )
Пример #5
0
    def _establish_tls_with_server(self):
        self.log("Establish TLS with server", "debug")
        try:
            alpn = None
            if self._client_tls:
                if self._client_hello.alpn_protocols:
                    # We only support http/1.1 and h2.
                    # If the server only supports spdy (next to http/1.1), it may select that
                    # and mitmproxy would enter TCP passthrough mode, which we want to avoid.
                    alpn = [
                        x for x in self._client_hello.alpn_protocols
                        if not (x.startswith(b"h2-") or x.startswith(b"spdy"))
                    ]
                if alpn and b"h2" in alpn and not self.config.options.http2:
                    alpn.remove(b"h2")

            ciphers_server = self.config.options.ciphers_server
            if not ciphers_server and self._client_tls:
                ciphers_server = []
                for id in self._client_hello.cipher_suites:
                    if id in CIPHER_ID_NAME_MAP.keys():
                        ciphers_server.append(CIPHER_ID_NAME_MAP[id])
                ciphers_server = ':'.join(ciphers_server)

            self.server_conn.establish_ssl(
                self.config.clientcerts,
                self.server_sni,
                method=self.config.openssl_method_server,
                options=self.config.openssl_options_server,
                verify_options=self.config.openssl_verification_mode_server,
                ca_path=self.config.options.ssl_verify_upstream_trusted_cadir,
                ca_pemfile=self.config.options.ssl_verify_upstream_trusted_ca,
                cipher_list=ciphers_server,
                alpn_protos=alpn,
            )
            tls_cert_err = self.server_conn.ssl_verification_error
            if tls_cert_err is not None:
                self.log(str(tls_cert_err), "warn")
                self.log(
                    "Ignoring server verification error, continuing with connection",
                    "warn")
        except exceptions.InvalidCertificateException as e:
            raise exceptions.InvalidServerCertificate(str(e))
        except exceptions.TlsException as e:
            raise exceptions.TlsProtocolException(
                "Cannot establish TLS with {address} (sni: {sni}): {e}".format(
                    address=repr(self.server_conn.address),
                    sni=self.server_sni,
                    e=repr(e)))

        proto = self.alpn_for_client_connection.decode(
        ) if self.alpn_for_client_connection else '-'
        self.log("ALPN selected by server: {}".format(proto), "debug")
Пример #6
0
    def _establish_tls_with_server(self):
        self.log("Establish TLS with server", "debug")
        try:
            alpn = None
            if self._client_tls:
                if self._client_hello.alpn_protocols:
                    # We only support http/1.1 and h2.
                    # If the server only supports spdy (next to http/1.1), it may select that
                    # and mitmproxy would enter TCP passthrough mode, which we want to avoid.
                    alpn = [
                        x for x in self._client_hello.alpn_protocols
                        if not (x.startswith(b"h2-") or x.startswith(b"spdy"))
                    ]
                if alpn and b"h2" in alpn and not self.config.options.http2:
                    alpn.remove(b"h2")

            if self.client_conn.tls_established and self.client_conn.get_alpn_proto_negotiated(
            ):
                # If the client has already negotiated an ALP, then force the
                # server to use the same. This can only happen if the host gets
                # changed after the initial connection was established. E.g.:
                #   * the client offers http/1.1 and h2,
                #   * the initial host is only capable of http/1.1,
                #   * then the first server connection negotiates http/1.1,
                #   * but after the server_conn change, the new host offers h2
                #   * which results in garbage because the layers don' match.
                alpn = [self.client_conn.get_alpn_proto_negotiated()]

            # We pass through the list of ciphers send by the client, because some HTTP/2 servers
            # will select a non-HTTP/2 compatible cipher from our default list and then hang up
            # because it's incompatible with h2. :-)
            ciphers_server = self.config.options.ciphers_server
            if not ciphers_server and self._client_tls:
                ciphers_server = []
                for id in self._client_hello.cipher_suites:
                    if id in CIPHER_ID_NAME_MAP.keys():
                        ciphers_server.append(CIPHER_ID_NAME_MAP[id])
                ciphers_server = ':'.join(ciphers_server)

            args = net_tls.client_arguments_from_options(self.config.options)
            args["cipher_list"] = ciphers_server
            self.server_conn.establish_tls(sni=self.server_sni,
                                           alpn_protos=alpn,
                                           **args)
            tls_cert_err = self.server_conn.ssl_verification_error
            if tls_cert_err is not None:
                self.log(str(tls_cert_err), "warn")
                self.log(
                    "Ignoring server verification error, continuing with connection",
                    "warn")
        except exceptions.InvalidCertificateException as e:
            raise exceptions.InvalidServerCertificate(str(e))
        except exceptions.TlsException as e:
            raise exceptions.TlsProtocolException(
                "Cannot establish TLS with {host}:{port} (sni: {sni}): {e}".
                format(host=self.server_conn.address[0],
                       port=self.server_conn.address[1],
                       sni=self.server_sni,
                       e=repr(e)))

        proto = self.alpn_for_client_connection.decode(
        ) if self.alpn_for_client_connection else '-'
        self.log("ALPN selected by server: {}".format(proto), "debug")