use OpenSSL's keylog callback for SSLKEYLOGFILE, refs #3994

This commit is contained in:
Maximilian Hils 2020-11-19 14:39:04 +01:00
parent b01d574d8b
commit 4b8fcc8650
2 changed files with 13 additions and 32 deletions

View File

@ -86,36 +86,17 @@ class MasterSecretLogger:
# required for functools.wraps, which pyOpenSSL uses. # required for functools.wraps, which pyOpenSSL uses.
__name__ = "MasterSecretLogger" __name__ = "MasterSecretLogger"
def __call__(self, connection, where, ret): def __call__(self, connection, keymaterial):
done_now = (
where == SSL.SSL_CB_HANDSHAKE_DONE and ret == 1
)
# this is a horrendous workaround for https://github.com/mitmproxy/mitmproxy/pull/3692#issuecomment-608454530:
# OpenSSL 1.1.1f decided to not make connection.master_key() fail in the SSL_CB_HANDSHAKE_DONE callback.
# To support various OpenSSL versions and still log master secrets, we now mark connections where this has
# happened and then try again on the next event. This is ugly and shouldn't be done, but eventually we
# replace this with context.set_keylog_callback anyways.
done_previously_but_not_logged_yet = (
hasattr(connection, "_still_needs_masterkey")
)
if done_now or done_previously_but_not_logged_yet:
with self.lock: with self.lock:
if not self.f: if not self.f:
d = os.path.dirname(self.filename) d = os.path.dirname(self.filename)
if not os.path.isdir(d): if not os.path.isdir(d):
os.makedirs(d) os.makedirs(d)
self.f = open(self.filename, "ab") self.f = open(self.filename, "ab")
self.f.write(b"\r\n") self.f.write(b"\n")
try: self.f.write(keymaterial)
client_random = binascii.hexlify(connection.client_random()) self.f.write(b"\n")
masterkey = binascii.hexlify(connection.master_key())
except (AssertionError, SSL.Error): # careful: exception type changes between pyOpenSSL versions
connection._still_needs_masterkey = True
else:
self.f.write(b"CLIENT_RANDOM %s %s\r\n" % (client_random, masterkey))
self.f.flush() self.f.flush()
if hasattr(connection, "_still_needs_masterkey"):
delattr(connection, "_still_needs_masterkey")
def close(self): def close(self):
with self.lock: with self.lock:
@ -203,7 +184,7 @@ def _create_ssl_context(
# SSLKEYLOGFILE # SSLKEYLOGFILE
if log_master_secret: if log_master_secret:
context.set_info_callback(log_master_secret) context.set_keylog_callback(log_master_secret)
if alpn_protos is not None: if alpn_protos is not None:
# advertise application layer protocols # advertise application layer protocols

View File

@ -43,7 +43,7 @@ class TestMasterSecretLogger(tservers.ServerTestBase):
tls.log_master_secret.close() tls.log_master_secret.close()
with open(logfile, "rb") as f: with open(logfile, "rb") as f:
assert f.read().count(b"CLIENT_RANDOM") >= 2 assert f.read().count(b"SERVER_HANDSHAKE_TRAFFIC_SECRET") >= 2
tls.log_master_secret = _logfun tls.log_master_secret = _logfun