aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/cmdline.py6
-rw-r--r--mitmproxy/protocol/tls.py6
-rw-r--r--mitmproxy/proxy/config.py23
-rw-r--r--netlib/tcp.py10
-rw-r--r--pathod/pathoc.py5
-rw-r--r--test/mitmproxy/test_server.py40
-rw-r--r--test/mitmproxy/tservers.py2
7 files changed, 86 insertions, 6 deletions
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
index b1b860f8..d7de350f 100644
--- a/mitmproxy/cmdline.py
+++ b/mitmproxy/cmdline.py
@@ -435,6 +435,12 @@ def proxy_ssl_options(parser):
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",
help="Verify upstream server SSL/TLS certificates and fail if invalid "
diff --git a/mitmproxy/protocol/tls.py b/mitmproxy/protocol/tls.py
index f014142b..fc4be830 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_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 @@ 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 149d4710..bd02c628 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_upstream_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_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 574f3845..04aa868b 100644
--- a/netlib/tcp.py
+++ b/netlib/tcp.py
@@ -586,6 +586,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
@@ -670,6 +671,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(
@@ -737,6 +742,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
@@ -772,6 +778,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)
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index c0a33b62..64a81c94 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -42,7 +42,8 @@ class SSLInfo(object):
"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 @@ class SSLInfo(object):
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 d7b23bbb..26e53e8a 100644
--- a/test/mitmproxy/test_server.py
+++ b/test/mitmproxy/test_server.py
@@ -999,3 +999,43 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
# (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 b7b5de9e..4fa519cc 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 @@ class ProxyTestBase(object):
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,
)