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
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}" )
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")) )
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")
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")