From e62e979e60331dfb0a6fb5b49240b1ee55260713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Wed, 3 May 2017 22:21:18 +0200 Subject: [PATCH] Allow dumping SSL session keys on TRACE_SESSION_KEY This dumps SSL session keys to a log file specified by the SSLKEYLOGFILE environment variable. This permits decrypting SSL trafic in wireshark with a tcpdump capture for example. cf. https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format https://security.stackexchange.com/questions/35639/decrypting-tls-in-wireshark-when-using-dhe-rsa-ciphersuites/42350#42350 https://wiki.wireshark.org/SSL#Using_the_.28Pre.29-Master-Secret https://jimshaver.net/2015/02/11/decrypting-tls-browser-traffic-with-wireshark-the-easy-way/ Looks like we miss the required function from SSL... SSL_SESSION_print_keylog is only in 1.1.0. Also added dumping of client_random as it's required by wireshark. --- src/kits/network/libnetapi/SecureSocket.cpp | 114 ++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/kits/network/libnetapi/SecureSocket.cpp b/src/kits/network/libnetapi/SecureSocket.cpp index 7b3cfeb0e2..261fe592a3 100644 --- a/src/kits/network/libnetapi/SecureSocket.cpp +++ b/src/kits/network/libnetapi/SecureSocket.cpp @@ -11,6 +11,7 @@ #ifdef OPENSSL_ENABLED # include +# include // for TRACE_SESSION_KEY only # include #endif @@ -32,9 +33,94 @@ # define TRACE(x...) ; #endif +//#define TRACE_SESSION_KEY + #ifdef OPENSSL_ENABLED +#ifdef TRACE_SESSION_KEY +#if OPENSSL_VERSION_NUMBER < 0x10100000L +/* + * print session id and master key in NSS keylog format (RSA + * Session-ID: Master-Key:) + */ +int SSL_SESSION_print_keylog(BIO *bp, const SSL_SESSION *x) +{ + size_t i; + + if (x == NULL) + goto err; + if (x->session_id_length == 0 || x->master_key_length == 0) + goto err; + + /* + * the RSA prefix is required by the format's definition although there's + * nothing RSA-specific in the output, therefore, we don't have to check if + * the cipher suite is based on RSA + */ + if (BIO_puts(bp, "RSA ") <= 0) + goto err; + + if (BIO_puts(bp, "Session-ID:") <= 0) + goto err; + for (i = 0; i < x->session_id_length; i++) { + if (BIO_printf(bp, "%02X", x->session_id[i]) <= 0) + goto err; + } + if (BIO_puts(bp, " Master-Key:") <= 0) + goto err; + for (i = 0; i < (size_t)x->master_key_length; i++) { + if (BIO_printf(bp, "%02X", x->master_key[i]) <= 0) + goto err; + } + if (BIO_puts(bp, "\n") <= 0) + goto err; + + return (1); + err: + return (0); +} +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ +/* + * print client random id and master key in NSS keylog format + * as session ID is not enough. + */ +int SSL_SESSION_print_client_random(BIO *bp, const SSL *ssl) +{ + const SSL_SESSION *x = SSL_get_session(ssl); + size_t i; + + if (x == NULL) + goto err; + if (x->session_id_length == 0 || x->master_key_length == 0) + goto err; + + /* + * the RSA prefix is required by the format's definition although there's + * nothing RSA-specific in the output, therefore, we don't have to check if + * the cipher suite is based on RSA + */ + if (BIO_puts(bp, "CLIENT_RANDOM ") <= 0) + goto err; + + for (i = 0; i < sizeof(ssl->s3->client_random); i++) { + if (BIO_printf(bp, "%02X", ssl->s3->client_random[i]) <= 0) + goto err; + } + if (BIO_puts(bp, " ") <= 0) + goto err; + for (i = 0; i < (size_t)x->master_key_length; i++) { + if (BIO_printf(bp, "%02X", x->master_key[i]) <= 0) + goto err; + } + if (BIO_puts(bp, "\n") <= 0) + goto err; + + return (1); + err: + return (0); +} +#endif /* TRACE_SESSION_KEY */ class BSecureSocket::Private { public: @@ -59,6 +145,11 @@ private: static SSL_CTX* sContext; // FIXME When do we SSL_CTX_free it? static pthread_once_t sInitOnce; +#ifdef TRACE_SESSION_KEY +public: + static BIO* sKeyLogBIO; +#endif + }; @@ -66,6 +157,9 @@ private: /* static */ int BSecureSocket::Private::sDataIndex; /* static */ pthread_once_t BSecureSocket::Private::sInitOnce = PTHREAD_ONCE_INIT; +#ifdef TRACE_SESSION_KEY +/* static */ BIO* BSecureSocket::Private::sKeyLogBIO = NULL; +#endif BSecureSocket::Private::Private() @@ -286,6 +380,17 @@ BSecureSocket::Private::_CreateContext() // Get an unique index number for storing application data in SSL // structs. We will store a pointer to the BSecureSocket class there. sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + +#ifdef TRACE_SESSION_KEY + FILE *keylog = NULL; + const char *logpath = getenv("SSLKEYLOGFILE"); + if (logpath) + keylog = fopen(logpath, "w+"); + if (keylog) { + fprintf(keylog, "# Key Log File generated by Haiku Network Kit\n"); + sKeyLogBIO = BIO_new_fp(keylog, BIO_NOCLOSE); + } +#endif } @@ -477,6 +582,7 @@ BSecureSocket::_SetupCommon(const char* host) SSL_set_tlsext_host_name(fPrivate->fSSL, host); } + return B_OK; } @@ -495,6 +601,14 @@ BSecureSocket::_SetupConnect(const char* host) return fPrivate->ErrorCode(returnValue); } +#ifdef TRACE_SESSION_KEY + fprintf(stderr, "SSL SESSION INFO:\n"); + //SSL_SESSION_print_fp(stderr, SSL_get_session(fPrivate->fSSL)); + SSL_SESSION_print_keylog(fPrivate->sKeyLogBIO, SSL_get_session(fPrivate->fSSL)); + SSL_SESSION_print_client_random(fPrivate->sKeyLogBIO, fPrivate->fSSL); + fprintf(stderr, "\n"); +#endif + return B_OK; }