summaryrefslogtreecommitdiff
path: root/src/sslkeylog.c
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2018-10-23 18:39:24 +0200
committerPeter Wu <peter@lekensteyn.nl>2018-10-23 19:10:11 +0200
commit9fa13901cd459ea71543753a68fbf75c4f35c51e (patch)
treee6653f25c71dd679d53255c81404ce14c8ede5f2 /src/sslkeylog.c
parent5a413f9cd4626d14de0326a2ab2156994a069d30 (diff)
downloadwireshark-notes-9fa13901cd459ea71543753a68fbf75c4f35c51e.tar.gz
sslkeylog.c: support TLS 1.3 and OpenSSL 1.1.1
OpenSSL 1.1.1 adds TLS 1.3 support which uses a new secrets format. Previously it resulted in garbage keylog files, this has been fixed now. OpenSSL 1.1.1 also introduces a new API for secrets extraction. Consumers can use it like this (curl uses this code for example): static void keylog_callback(const SSL *ssl, const char *line) { /* write line and terminating '\n' */ } { SSL_CTX *ctx; ... SSL_CTX_set_keylog_callback(ctx, keylog_callback); SSL *ssl = SSL_new(ctx); } In case you cannot change the source code for an application, you can use sslkeylog.c again. This will basically perform the above step, set the key log callback before calling SSL_new. Since the new OpenSSL 1.1.1 API requires no further interception of SSL_read and other functions, a new NO_OPENSSL_110_SUPPORT macro was to avoid intercepting these. Additionally, a NO_OPENSSL_102_SUPPORT macro avoids the need for OpenSSL development headers. Caveat: when building with OpenSSL <= 1.0.2, libsslkeylog.so will not be compatible with runtime OpenSSL 1.1.0. OpenSSL 1.1.1 still works though. Use of SSL_new and interception via SSL_CTX_set_keylog_callback was initially proposed by Derick Rethans, thanks for the suggestion!
Diffstat (limited to 'src/sslkeylog.c')
-rw-r--r--src/sslkeylog.c103
1 files changed, 99 insertions, 4 deletions
diff --git a/src/sslkeylog.c b/src/sslkeylog.c
index fa46291..1617cd6 100644
--- a/src/sslkeylog.c
+++ b/src/sslkeylog.c
@@ -1,6 +1,7 @@
/*
* Dumps master keys for OpenSSL clients to file. The format is documented at
* https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+ * Supports TLS 1.3 when used with OpenSSL 1.1.1.
*
* Copyright (C) 2014 Peter Wu <peter@lekensteyn.nl>
* Licensed under the terms of GPLv3 (or any later version) at your choice.
@@ -9,9 +10,28 @@
* cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl
* SSLKEYLOGFILE=premaster.txt LD_PRELOAD=./libsslkeylog.so openssl ...
*/
+
+/*
+ * A single libsslkeylog.so supports multiple OpenSSL runtime versions. If you
+ * would like to build this library without OpenSSL development headers and do
+ * not require support for older OpenSSL versions, then disable it by defining
+ * the NO_OPENSSL_102_SUPPORT or NO_OPENSSL_110_SUPPORT macros.
+ */
+/* Define to drop OpenSSL <= 1.0.2 support and require OpenSSL >= 1.1.0. */
+//#define NO_OPENSSL_102_SUPPORT
+/* Define to drop OpenSSL <= 1.1.0 support and require OpenSSL >= 1.1.1. */
+//#define NO_OPENSSL_110_SUPPORT
+
+/* No OpenSSL 1.1.0 support implies no OpenSSL 1.0.2 support. */
+#ifdef NO_OPENSSL_110_SUPPORT
+# define NO_OPENSSL_102_SUPPORT
+#endif
+
#define _GNU_SOURCE /* for RTLD_NEXT */
#include <dlfcn.h>
-#include <openssl/ssl.h>
+#ifndef NO_OPENSSL_102_SUPPORT
+# include <openssl/ssl.h>
+#endif /* ! NO_OPENSSL_102_SUPPORT */
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
@@ -20,17 +40,33 @@
#ifndef OPENSSL_SONAME
/* fallback library if OpenSSL is not already loaded. Other values to try:
- * libssl.so.0.9.8 libssl.so.1.0.0 */
+ * libssl.so.0.9.8 libssl.so.1.0.0 libssl.so.1.1 */
# define OPENSSL_SONAME "libssl.so"
#endif
-#define PREFIX "CLIENT_RANDOM "
-#define PREFIX_LEN (sizeof(PREFIX) - 1)
#define FIRSTLINE "# SSL key logfile generated by sslkeylog.c\n"
#define FIRSTLINE_LEN (sizeof(FIRSTLINE) - 1)
+/* When building for OpenSSL 1.1.0 or newer, no headers are required. */
+#ifdef NO_OPENSSL_102_SUPPORT
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+/* Extra definitions for OpenSSL 1.1.0 support when headers are unavailable. */
+# ifndef NO_OPENSSL_110_SUPPORT
+typedef struct ssl_session_st SSL_SESSION;
+# define SSL3_RANDOM_SIZE 32
+# define SSL_MAX_MASTER_KEY_LENGTH 48
+# define OPENSSL_VERSION_NUMBER 0x10100000L
+# endif /* ! NO_OPENSSL_110_SUPPORT */
+#endif /* ! NO_OPENSSL_102_SUPPORT */
+
static int keylog_file_fd = -1;
+/* Legacy routines for dumping TLS <= 1.2 secrets on older OpenSSL versions. */
+#ifndef NO_OPENSSL_110_SUPPORT
+#define PREFIX "CLIENT_RANDOM "
+#define PREFIX_LEN (sizeof(PREFIX) - 1)
+
static inline void put_hex(char *buffer, int pos, char c)
{
unsigned char c1 = ((unsigned char) c) >> 4;
@@ -64,6 +100,7 @@ static void dump_to_fd(int fd, unsigned char *client_random,
* write access so do not write hex values one by one. */
write(fd, line, pos);
}
+#endif /* ! NO_OPENSSL_110_SUPPORT */
static void init_keylog_file(void)
{
@@ -102,6 +139,7 @@ static inline void *lookup_symbol(const char *sym)
return func;
}
+#ifndef NO_OPENSSL_110_SUPPORT
typedef struct ssl_tap_state {
int master_key_length;
unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
@@ -156,9 +194,29 @@ static void copy_client_random(const SSL *ssl, unsigned char *client_random)
#endif
}
+/* non-NULL if the new OpenSSL 1.1.1 keylog API is supported. */
+static void *new_keylog_api(void)
+{
+ static void *set_keylog_cb = (void *)-1;
+ if (set_keylog_cb == (void *)-1) {
+ /*
+ * Assume OpenSSL is already loaded and try to locate the OpenSSL 1.1.1
+ * keylog API. Do not fallback to lookup_symbol as that could result in
+ * loading a different, incompatible version of libssl.so.
+ */
+ set_keylog_cb = dlsym(RTLD_NEXT, "SSL_CTX_set_keylog_callback");
+ }
+ return set_keylog_cb;
+}
+
/* Copies SSL state for later comparison in tap_ssl_key. */
static void ssl_tap_state_init(ssl_tap_state_t *state, const SSL *ssl)
{
+ if (new_keylog_api()) {
+ /* Favor using the callbacks API to extract secrets. */
+ return;
+ }
+
const SSL_SESSION *session = ssl_get_session(ssl);
memset(state, 0, sizeof(ssl_tap_state_t));
@@ -173,6 +231,11 @@ static void ssl_tap_state_init(ssl_tap_state_t *state, const SSL *ssl)
static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state)
{
+ if (new_keylog_api()) {
+ /* Favor using the callbacks API to extract secrets. */
+ return;
+ }
+
const SSL_SESSION *session = ssl_get_session(ssl);
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
@@ -261,3 +324,35 @@ int SSL_write(SSL *ssl, const void *buf, int num)
tap_ssl_key(ssl, &state);
return ret;
}
+#endif /* ! NO_OPENSSL_110_SUPPORT */
+
+/* Key extraction via the new OpenSSL 1.1.1 API. */
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ init_keylog_file();
+ if (keylog_file_fd >= 0) {
+ write(keylog_file_fd, line, strlen(line));
+ write(keylog_file_fd, "\n", 1);
+ }
+}
+
+SSL *SSL_new(SSL_CTX *ctx)
+{
+ static SSL *(*func)();
+ static void (*set_keylog_cb)();
+ if (!func) {
+ func = lookup_symbol(__func__);
+#ifdef NO_OPENSSL_110_SUPPORT
+ /* The new API MUST be available since OpenSSL 1.1.1. */
+ set_keylog_cb = lookup_symbol("SSL_CTX_set_keylog_callback");
+#else /* ! NO_OPENSSL_110_SUPPORT */
+ /* May be NULL if used with an older OpenSSL runtime library. */
+ set_keylog_cb = new_keylog_api();
+#endif /* ! NO_OPENSSL_110_SUPPORT */
+ }
+ if (set_keylog_cb) {
+ /* Override any previous key log callback. */
+ set_keylog_cb(ctx, keylog_callback);
+ }
+ return func(ctx);
+}