diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py index b1b860f83..a04a36ba7 100644 --- a/mitmproxy/cmdline.py +++ b/mitmproxy/cmdline.py @@ -434,6 +434,12 @@ def proxy_ssl_options(parser): action="store_true", dest="no_upstream_cert", help="Don't connect to upstream server to look up certificate details." ) + group.add_argument( + "--add-server-certs-to-client-chain", default=False, + action="store_true", dest="add_server_certs_to_client_chain", + help="Add all the certificates of the server to the certificate chain " + "that will be served to the client, as extras." + ) group.add_argument( "--verify-upstream-cert", default=False, action="store_true", dest="ssl_verify_upstream_cert", diff --git a/mitmproxy/protocol/tls.py b/mitmproxy/protocol/tls.py index 6913396d5..22ee8ff98 100644 --- a/mitmproxy/protocol/tls.py +++ b/mitmproxy/protocol/tls.py @@ -432,6 +432,11 @@ class TlsLayer(Layer): self.log("Establish TLS with client", "debug") cert, key, chain_file = self._find_cert() + if self.config.add_server_certs_to_client_chain: + extra_certs = self.server_conn.server_certs + else: + extra_certs = None + try: self.client_conn.convert_to_ssl( cert, key, @@ -441,6 +446,7 @@ class TlsLayer(Layer): dhparams=self.config.certstore.dhparams, chain_file=chain_file, alpn_select_callback=self.__alpn_select_callback, + extra_chain_certs = extra_certs, ) # Some TLS clients will not fail the handshake, # but will immediately throw an "unexpected eof" error on the first read. diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py index 149d47105..9932ec8c0 100644 --- a/mitmproxy/proxy/config.py +++ b/mitmproxy/proxy/config.py @@ -67,6 +67,7 @@ class ProxyConfig: ssl_verify_upstream_cert=False, ssl_verify_upstream_trusted_cadir=None, ssl_verify_upstream_trusted_ca=None, + add_server_certs_to_client_chain=False, ): self.host = host self.port = port @@ -107,6 +108,7 @@ class ProxyConfig: self.openssl_verification_mode_server = SSL.VERIFY_NONE self.openssl_trusted_cadir_server = ssl_verify_upstream_trusted_cadir self.openssl_trusted_ca_server = ssl_verify_upstream_trusted_ca + self.add_server_certs_to_client_chain = add_server_certs_to_client_chain def process_proxy_options(parser, options): @@ -206,5 +208,6 @@ def process_proxy_options(parser, options): ssl_version_server=options.ssl_version_server, ssl_verify_upstream_cert=options.ssl_verify_upstream_cert, ssl_verify_upstream_trusted_cadir=options.ssl_verify_upstream_trusted_cadir, - ssl_verify_upstream_trusted_ca=options.ssl_verify_upstream_trusted_ca + ssl_verify_upstream_trusted_ca=options.ssl_verify_upstream_trusted_ca, + add_server_certs_to_client_chain=options.add_server_certs_to_client_chain, ) diff --git a/netlib/tcp.py b/netlib/tcp.py index 6423888a1..68a712702 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -584,6 +584,7 @@ class TCPClient(_Connection): self.address = address self.source_address = source_address self.cert = None + self.server_certs = [] self.ssl_verification_error = None self.sni = None @@ -668,6 +669,10 @@ class TCPClient(_Connection): self.cert = certutils.SSLCert(self.connection.get_peer_certificate()) + # Keep all server certificates in a list + for i in self.connection.get_peer_cert_chain(): + self.server_certs.append(certutils.SSLCert(i)) + # Validate TLS Hostname try: crt = dict( @@ -734,6 +739,7 @@ class BaseHandler(_Connection): request_client_cert=None, chain_file=None, dhparams=None, + extra_chain_certs=None, **sslctx_kwargs): """ cert: A certutils.SSLCert object or the path to a certificate @@ -769,6 +775,10 @@ class BaseHandler(_Connection): else: context.use_certificate_chain_file(cert) + if extra_chain_certs: + for i in extra_chain_certs: + context.add_extra_chain_cert(i.x509) + if handle_sni: # SNI callback happens during do_handshake() context.set_tlsext_servername_callback(handle_sni)