New option: Add server certs to client chain
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
index b1b860f83b..d7de350fff 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-upstream-certs-to-client-chain", default=False,
+        action="store_true", dest="add_upstream_certs_to_client_chain",
+        help="Add all certificates of the upstream server to the certificate chain "
+             "that will be served to the proxy 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 6913396d52..7a4d53fe00 100644
--- a/mitmproxy/protocol/tls.py
+++ b/mitmproxy/protocol/tls.py
@@ -432,6 +432,11 @@ def _establish_tls_with_client(self):
         self.log("Establish TLS with client", "debug")
         cert, key, chain_file = self._find_cert()
 
+        if self.config.add_upstream_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 @@ def _establish_tls_with_client(self):
                 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 149d471053..bd02c628c5 100644
--- a/mitmproxy/proxy/config.py
+++ b/mitmproxy/proxy/config.py
@@ -67,6 +67,7 @@ def __init__(
             ssl_verify_upstream_cert=False,
             ssl_verify_upstream_trusted_cadir=None,
             ssl_verify_upstream_trusted_ca=None,
+            add_upstream_certs_to_client_chain=False,
     ):
         self.host = host
         self.port = port
@@ -107,6 +108,7 @@ def __init__(
             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_upstream_certs_to_client_chain = add_upstream_certs_to_client_chain
 
 
 def process_proxy_options(parser, options):
@@ -136,14 +138,26 @@ def process_proxy_options(parser, options):
             "Transparent, SOCKS5, reverse and upstream proxy mode "
             "are mutually exclusive. Read the docs on proxy modes to understand why."
         )
-
+    if options.add_upstream_certs_to_client_chain and options.no_upstream_cert:
+        return parser.error(
+            "The no-upstream-cert and add-upstream-certs-to-client-chain "
+            "options are mutually exclusive. If no-upstream-cert is enabled "
+            "then the upstream certificate is not retrieved before generating "
+            "the client certificate chain."
+        )
+    if options.add_upstream_certs_to_client_chain and options.ssl_verify_upstream_cert:
+        return parser.error(
+            "The verify-upstream-cert and add-upstream-certs-to-client-chain "
+            "options are mutually exclusive. If upstream certificates are verified "
+            "then extra upstream certificates are not available for inclusion "
+            "to the client chain."
+        )
     if options.clientcerts:
         options.clientcerts = os.path.expanduser(options.clientcerts)
         if not os.path.exists(options.clientcerts):
             return parser.error(
-                "Client certificate path does not exist: %s" % options.clientcerts
+                    "Client certificate path does not exist: %s" % options.clientcerts
             )
-
     if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd:
 
         if options.transparent_proxy:
@@ -206,5 +220,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_upstream_certs_to_client_chain=options.add_upstream_certs_to_client_chain,
     )
diff --git a/netlib/tcp.py b/netlib/tcp.py
index 6423888a14..68a7127023 100644
--- a/netlib/tcp.py
+++ b/netlib/tcp.py
@@ -584,6 +584,7 @@ def __init__(self, address, source_address=None):
         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 @@ def convert_to_ssl(self, sni=None, alpn_protos=None, **sslctx_kwargs):
 
         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 @@ def create_ssl_context(self,
                            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 @@ def create_ssl_context(self,
         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)
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index c0a33b6283..64a81c9454 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -42,7 +42,8 @@ def __str__(self):
             "Cipher: %s, %s bit, %s" % self.cipher,
             "SSL certificate chain:"
         ]
-        for i in self.certchain:
+        for n,i in enumerate(self.certchain):
+            parts.append("  Certificate [%s]" % n)
             parts.append("\tSubject: ")
             for cn in i.get_subject().get_components():
                 parts.append("\t\t%s=%s" % cn)
@@ -69,7 +70,7 @@ def __str__(self):
             s = certutils.SSLCert(i)
             if s.altnames:
                 parts.append("\tSANs: %s" % " ".join(s.altnames))
-            return "\n".join(parts)
+        return "\n".join(parts)
 
 
 
diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py
index d7b23bbb82..26e53e8aea 100644
--- a/test/mitmproxy/test_server.py
+++ b/test/mitmproxy/test_server.py
@@ -999,3 +999,43 @@ def handler(f):
         # (both terminated)
         # nothing happened here
         assert self.chain[1].tmaster.state.flow_count() == 2
+
+
+class AddUpstreamCertsToClientChainMixin:
+
+    ssl = True
+    servercert = tutils.test_data.path("data/trusted-server.crt")
+    ssloptions = pathod.SSLOptions(
+            cn="trusted-cert",
+            certs=[
+                ("trusted-cert", servercert)
+            ]
+    )
+
+    def test_add_upstream_certs_to_client_chain(self):
+        with open(self.servercert, "rb") as f:
+            d = f.read()
+        upstreamCert = SSLCert.from_pem(d)
+        p = self.pathoc()
+        upstream_cert_found_in_client_chain = False
+        for receivedCert in p.server_certs:
+            if receivedCert.digest('sha256') == upstreamCert.digest('sha256'):
+                upstream_cert_found_in_client_chain = True
+                break
+        assert(upstream_cert_found_in_client_chain == self.add_upstream_certs_to_client_chain)
+
+
+class TestHTTPSAddUpstreamCertsToClientChainTrue(AddUpstreamCertsToClientChainMixin, tservers.HTTPProxyTest):
+
+    """
+    If --add-server-certs-to-client-chain is True, then the client should receive the upstream server's certificates
+    """
+    add_upstream_certs_to_client_chain = True
+
+
+class TestHTTPSAddUpstreamCertsToClientChainFalse(AddUpstreamCertsToClientChainMixin, tservers.HTTPProxyTest):
+
+    """
+    If --add-server-certs-to-client-chain is False, then the client should not receive the upstream server's certificates
+    """
+    add_upstream_certs_to_client_chain = False
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index b7b5de9e89..4fa519cc60 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -86,6 +86,7 @@ class ProxyTestBase(object):
     no_upstream_cert = False
     authenticator = None
     masterclass = TestMaster
+    add_upstream_certs_to_client_chain = False
 
     @classmethod
     def setup_class(cls):
@@ -129,6 +130,7 @@ def get_proxy_config(cls):
             no_upstream_cert = cls.no_upstream_cert,
             cadir = cls.cadir,
             authenticator = cls.authenticator,
+            add_upstream_certs_to_client_chain = cls.add_upstream_certs_to_client_chain,
         )
 
 

If enabled, append all server certificates to the certificate chain
served to the client, as extras. Can be used to test/bypass certain
certificate pinning impementations.
