Skip to content

Commit

Permalink
openssl: Integrate Peter Wu's SSLKEYLOGFILE implementation
Browse files Browse the repository at this point in the history
This is an adaptation of 2 of Peter Wu's SSLKEYLOGFILE implementations.

The first one, written for old OpenSSL versions:
https://git.lekensteyn.nl/peter/wireshark-notes/tree/src/sslkeylog.c

The second one, written for BoringSSL and new OpenSSL versions:
#1346

Note the first one is GPL licensed but the author gave permission to
waive that license for libcurl.

As of right now this feature is disabled by default, and does not have
a configure option to enable it. To enable this feature define
ENABLE_SSLKEYLOGFILE when building libcurl and set environment
variable SSLKEYLOGFILE to a pathname that will receive the keys.

And in Wireshark change your preferences to point to that key file:
Edit > Preferences > Protocols > SSL > Master-Secret

Co-authored-by: Peter Wu

Ref: #1030
Ref: #1346

Closes #1866
  • Loading branch information
jay committed Sep 6, 2017
1 parent ee5725f commit 6cdba64
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/curl_setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
#if defined(WIN32) || defined(MSDOS)
#define FOPEN_READTEXT "rt"
#define FOPEN_WRITETEXT "wt"
#define FOPEN_APPENDTEXT "at"
#elif defined(__CYGWIN__)
/* Cygwin has specific behavior we need to address when WIN32 is not defined.
https://cygwin.com/cygwin-ug-net/using-textbinary.html
Expand All @@ -726,9 +727,11 @@ endings either CRLF or LF so 't' is appropriate.
*/
#define FOPEN_READTEXT "rt"
#define FOPEN_WRITETEXT "w"
#define FOPEN_APPENDTEXT "a"
#else
#define FOPEN_READTEXT "r"
#define FOPEN_WRITETEXT "w"
#define FOPEN_APPENDTEXT "a"
#endif

/* WinSock destroys recv() buffer when send() failed.
Expand Down
168 changes: 168 additions & 0 deletions lib/vtls/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ static unsigned long OpenSSL_version_num(void)
#define OPENSSL_load_builtin_modules(x)
#endif

/*
* Whether SSL_CTX_set_keylog_callback is available.
* OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
* BoringSSL: supported since d28f59c27bac (committed 2015-11-19), the
* BORINGSSL_201512 macro from 2016-01-21 should be close enough.
* LibreSSL: unsupported in at least 2.5.1 (explicitly check for it since it
* lies and pretends to be OpenSSL 2.0.0).
*/
#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
!defined(LIBRESSL_VERSION_NUMBER)) || \
defined(BORINGSSL_201512)
#define HAVE_KEYLOG_CALLBACK
#endif

#if defined(LIBRESSL_VERSION_NUMBER)
#define OSSL_PACKAGE "LibreSSL"
#elif defined(OPENSSL_IS_BORINGSSL)
Expand All @@ -165,11 +179,23 @@ static unsigned long OpenSSL_version_num(void)
"ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"
#endif

#ifdef ENABLE_SSLKEYLOGFILE
typedef struct ssl_tap_state {
int master_key_length;
unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
unsigned char client_random[SSL3_RANDOM_SIZE];
} ssl_tap_state_t;
#endif /* ENABLE_SSLKEYLOGFILE */

struct ssl_backend_data {
/* these ones requires specific SSL-types */
SSL_CTX* ctx;
SSL* handle;
X509* server_cert;
#ifdef ENABLE_SSLKEYLOGFILE
/* tap_state holds the last seen master key if we're logging them */
ssl_tap_state_t tap_state;
#endif
};

#define BACKEND connssl->backend
Expand All @@ -182,6 +208,112 @@ struct ssl_backend_data {
*/
#define RAND_LOAD_LENGTH 1024

#ifdef ENABLE_SSLKEYLOGFILE
/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
static FILE *keylog_file_fp;

#ifdef HAVE_KEYLOG_CALLBACK
static void ossl_keylog_callback(const SSL *ssl, const char *line)
{
(void)ssl;

/* Using fputs here instead of fprintf since libcurl's fprintf replacement
may not be thread-safe. */
if(keylog_file_fp && line && *line) {
char stackbuf[256];
char *buf;
size_t linelen = strlen(line);

if(linelen <= sizeof(stackbuf) - 2)
buf = stackbuf;
else {
buf = malloc(linelen + 2);
if(!buf)
return;
}
strncpy(buf, line, linelen);
buf[linelen] = '\n';
buf[linelen + 1] = '\0';

fputs(buf, keylog_file_fp);
if(buf != stackbuf)
free(buf);
}
}
#else
#define KEYLOG_PREFIX "CLIENT_RANDOM "
#define KEYLOG_PREFIX_LEN (sizeof(KEYLOG_PREFIX) - 1)
/*
* tap_ssl_key is called by libcurl to make the CLIENT_RANDOMs if the OpenSSL
* being used doesn't have native support for doing that.
*/
static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state)
{
const char *hex = "0123456789ABCDEF";
int pos, i;
char line[KEYLOG_PREFIX_LEN + 2 * SSL3_RANDOM_SIZE + 1 +
2 * SSL_MAX_MASTER_KEY_LENGTH + 1 + 1];
const SSL_SESSION *session = SSL_get_session(ssl);
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
int master_key_length = 0;

if(!session || !keylog_file_fp)
return;

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that
* we have a valid SSL context if we have a non-NULL session. */
SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
master_key_length =
SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH);
#else
if(ssl->s3 && session->master_key_length > 0) {
master_key_length = session->master_key_length;
memcpy(master_key, session->master_key, session->master_key_length);
memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE);
}
#endif

if(master_key_length <= 0)
return;

/* Skip writing keys if there is no key or it did not change. */
if(state->master_key_length == master_key_length &&
!memcmp(state->master_key, master_key, master_key_length) &&
!memcmp(state->client_random, client_random, SSL3_RANDOM_SIZE)) {
return;
}

state->master_key_length = master_key_length;
memcpy(state->master_key, master_key, master_key_length);
memcpy(state->client_random, client_random, SSL3_RANDOM_SIZE);

memcpy(line, KEYLOG_PREFIX, KEYLOG_PREFIX_LEN);
pos = KEYLOG_PREFIX_LEN;

/* Client Random for SSLv3/TLS */
for(i = 0; i < SSL3_RANDOM_SIZE; i++) {
line[pos++] = hex[client_random[i] >> 4];
line[pos++] = hex[client_random[i] & 0xF];
}
line[pos++] = ' ';

/* Master Secret (size is at most SSL_MAX_MASTER_KEY_LENGTH) */
for(i = 0; i < master_key_length; i++) {
line[pos++] = hex[master_key[i] >> 4];
line[pos++] = hex[master_key[i] & 0xF];
}
line[pos++] = '\n';
line[pos] = '\0';

/* Using fputs here instead of fprintf since libcurl's fprintf replacement
may not be thread-safe. */
fputs(line, keylog_file_fp);
}
#endif /* !HAVE_KEYLOG_CALLBACK */
#endif /* ENABLE_SSLKEYLOGFILE */

static const char *SSL_ERROR_to_str(int err)
{
switch(err) {
Expand Down Expand Up @@ -756,6 +888,10 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
*/
static int Curl_ossl_init(void)
{
#ifdef ENABLE_SSLKEYLOGFILE
const char *keylog_file_name;
#endif

OPENSSL_load_builtin_modules();

#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
Expand Down Expand Up @@ -792,6 +928,19 @@ static int Curl_ossl_init(void)
OpenSSL_add_all_algorithms();
#endif

#ifdef ENABLE_SSLKEYLOGFILE
keylog_file_name = curl_getenv("SSLKEYLOGFILE");
if(keylog_file_name && !keylog_file_fp) {
keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
if(keylog_file_fp) {
if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) {
fclose(keylog_file_fp);
keylog_file_fp = NULL;
}
}
}
#endif

return 1;
}

Expand Down Expand Up @@ -828,6 +977,13 @@ static void Curl_ossl_cleanup(void)
SSL_COMP_free_compression_methods();
#endif
#endif

#ifdef ENABLE_SSLKEYLOGFILE
if(keylog_file_fp) {
fclose(keylog_file_fp);
keylog_file_fp = NULL;
}
#endif
}

/*
Expand Down Expand Up @@ -2231,6 +2387,13 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
SSL_CTX_set_verify(BACKEND->ctx,
verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);

/* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */
#if defined(ENABLE_SSLKEYLOGFILE) && defined(HAVE_KEYLOG_CALLBACK)
if(keylog_file) {
SSL_CTX_set_keylog_callback(connssl->ctx, ossl_keylog_callback);
}
#endif

/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx,
Expand Down Expand Up @@ -2325,6 +2488,11 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
ERR_clear_error();

err = SSL_connect(BACKEND->handle);
/* If keylogging is enabled but the keylog callback is not supported then log
secrets here, immediately after SSL_connect by using tap_ssl_key. */
#if defined(ENABLE_SSLKEYLOGFILE) && !defined(HAVE_KEYLOG_CALLBACK)
tap_ssl_key(BACKEND->handle, &BACKEND->tap_state);
#endif

/* 1 is fine
0 is "not successful but was shut down controlled"
Expand Down

0 comments on commit 6cdba64

Please sign in to comment.