apps: Add support for writing a keylog file
authorPeter Wu <peter@lekensteyn.nl>
Wed, 1 Feb 2017 18:14:27 +0000 (19:14 +0100)
committerPeter Wu <peter@lekensteyn.nl>
Tue, 7 Feb 2017 18:20:56 +0000 (19:20 +0100)
The server and client demos (s_client and s_server) are extended with a
-keylogfile option. This is similar as setting the SSLKEYLOGFILE
environment variable for NSS and creates a keylog file which is suitable
for Wireshark.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2343)

apps/s_apps.h
apps/s_cb.c
apps/s_client.c
apps/s_server.c
doc/man1/s_client.pod
doc/man1/s_server.pod

index 4c24b2eb287f91bbdbab1b60f4035bd4dfdced67..bcca6fad9e1b040057c50b292de0b784d8350b92 100644 (file)
@@ -99,4 +99,5 @@ int ssl_load_stores(SSL_CTX *ctx, const char *vfyCApath,
                     const char *chCAfile, STACK_OF(X509_CRL) *crls,
                     int crl_download);
 void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose);
+int set_keylog_file(SSL_CTX *ctx, const char *keylog_file);
 #endif
index 3f46156ef6be9f60cd5eb94cded2db15df8e0818..550969d70495d9ad82d92d2df14a5c26cf0b9751 100644 (file)
@@ -32,6 +32,7 @@ VERIFY_CB_ARGS verify_args = { 0, 0, X509_V_OK, 0 };
 static unsigned char cookie_secret[COOKIE_SECRET_LENGTH];
 static int cookie_initialized = 0;
 #endif
+static BIO *bio_keylog = NULL;
 
 static const char *lookup(int val, const STRINT_PAIR* list, const char* def)
 {
@@ -1355,3 +1356,50 @@ void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose)
     SSL_CTX_set_security_callback(ctx, security_callback_debug);
     SSL_CTX_set0_security_ex_data(ctx, &sdb);
 }
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+    if (bio_keylog == NULL) {
+        BIO_printf(bio_err, "Keylog callback is invoked without valid file!\n");
+        return;
+    }
+
+    /*
+     * There might be concurrent writers to the keylog file, so we must ensure
+     * that the given line is written at once.
+     */
+    BIO_printf(bio_keylog, "%s\n", line);
+    (void)BIO_flush(bio_keylog);
+}
+
+int set_keylog_file(SSL_CTX *ctx, const char *keylog_file)
+{
+    /* Close any open files */
+    BIO_free_all(bio_keylog);
+    bio_keylog = NULL;
+
+    if (ctx == NULL || keylog_file == NULL) {
+        /* Keylogging is disabled, OK. */
+        return 0;
+    }
+
+    /*
+     * Append rather than write in order to allow concurrent modification.
+     * Furthermore, this preserves existing keylog files which is useful when
+     * the tool is run multiple times.
+     */
+    bio_keylog = BIO_new_file(keylog_file, "a");
+    if (bio_keylog == NULL) {
+        BIO_printf(bio_err, "Error writing keylog file %s\n", keylog_file);
+        return 1;
+    }
+
+    /* Write a header for seekable, empty files (this excludes pipes). */
+    if (BIO_tell(bio_keylog) == 0) {
+        BIO_puts(bio_keylog,
+                 "# SSL/TLS secrets log file, generated by OpenSSL\n");
+        (void)BIO_flush(bio_keylog);
+    }
+    SSL_CTX_set_keylog_callback(ctx, keylog_callback);
+    return 0;
+}
index d9dbe702f9d1020d3bd58a962e9f87162fa7e8a6..ad237c3252fee6c263c320c5f855e8f1d18ccd35 100644 (file)
@@ -549,6 +549,7 @@ typedef enum OPTION_choice {
     OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME,
     OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST,
     OPT_ASYNC, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF,
+    OPT_KEYLOG_FILE,
     OPT_V_ENUM,
     OPT_X_ENUM,
     OPT_S_ENUM,
@@ -731,6 +732,7 @@ const OPTIONS s_client_options[] = {
     {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"},
     {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"},
 #endif
+    {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"},
     {NULL, OPT_EOF, 0x00, NULL}
 };
 
@@ -890,6 +892,7 @@ int s_client_main(int argc, char **argv)
     int c_status_req = 0;
 #endif
     BIO *bio_c_msg = NULL;
+    const char *keylog_file = NULL;
 
     FD_ZERO(&readfds);
     FD_ZERO(&writefds);
@@ -1358,6 +1361,9 @@ int s_client_main(int argc, char **argv)
         case OPT_READ_BUF:
             read_buf_len = atoi(opt_arg());
             break;
+        case OPT_KEYLOG_FILE:
+            keylog_file = opt_arg();
+            break;
         }
     }
     if (count4or6 >= 2) {
@@ -1706,6 +1712,9 @@ int s_client_main(int argc, char **argv)
         SSL_CTX_sess_set_new_cb(ctx, new_session_cb);
     }
 
+    if (set_keylog_file(ctx, keylog_file))
+        goto end;
+
     con = SSL_new(ctx);
     if (sess_in) {
         SSL_SESSION *sess;
@@ -2574,6 +2583,7 @@ int s_client_main(int argc, char **argv)
     OPENSSL_free(next_proto.data);
 #endif
     SSL_CTX_free(ctx);
+    set_keylog_file(NULL, NULL);
     X509_free(cert);
     sk_X509_CRL_pop_free(crls, X509_CRL_free);
     EVP_PKEY_free(key);
index f3494120e0130c391d40bd044c87b7c7aca9e33d..e064290721b5af1a255bf03ed5c37bff54c25d62 100644 (file)
@@ -719,6 +719,7 @@ typedef enum OPTION_choice {
     OPT_ID_PREFIX, OPT_RAND, OPT_SERVERNAME, OPT_SERVERNAME_FATAL,
     OPT_CERT2, OPT_KEY2, OPT_NEXTPROTONEG, OPT_ALPN,
     OPT_SRTP_PROFILES, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN,
+    OPT_KEYLOG_FILE,
     OPT_S_ENUM,
     OPT_V_ENUM,
     OPT_X_ENUM
@@ -913,6 +914,7 @@ const OPTIONS s_server_options[] = {
 #ifndef OPENSSL_NO_ENGINE
     {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
 #endif
+    {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"},
     {NULL, OPT_EOF, 0, NULL}
 };
 
@@ -988,6 +990,7 @@ int s_server_main(int argc, char *argv[])
     int no_resume_ephemeral = 0;
     unsigned int split_send_fragment = 0, max_pipelines = 0;
     const char *s_serverinfo_file = NULL;
+    const char *keylog_file = NULL;
 
     /* Init of few remaining global variables */
     local_argc = argc;
@@ -1489,6 +1492,9 @@ int s_server_main(int argc, char *argv[])
         case OPT_READ_BUF:
             read_buf_len = atoi(opt_arg());
             break;
+        case OPT_KEYLOG_FILE:
+            keylog_file = opt_arg();
+            break;
 
         }
     }
@@ -1977,6 +1983,8 @@ int s_server_main(int argc, char *argv[])
         }
     }
 #endif
+    if (set_keylog_file(ctx, keylog_file))
+        goto end;
 
     BIO_printf(bio_s_out, "ACCEPT\n");
     (void)BIO_flush(bio_s_out);
@@ -1997,6 +2005,7 @@ int s_server_main(int argc, char *argv[])
     ret = 0;
  end:
     SSL_CTX_free(ctx);
+    set_keylog_file(NULL, NULL);
     X509_free(s_cert);
     sk_X509_CRL_pop_free(crls, X509_CRL_free);
     X509_free(s_dcert);
index fb1680a1a53e5833b83725570435a0627ff7734b..f8c4a9bb045418207b4dc2600cea754d0265a0d1 100644 (file)
@@ -520,6 +520,11 @@ for SCTs.
 A file containing a list of known Certificate Transparency logs. See
 L<SSL_CTX_set_ctlog_list_file(3)> for the expected file format.
 
+=item B<-keylogfile path>
+
+Appends TLS secrets to the specified keylog file such that external programs
+(like Wireshark) can decrypt TLS connections.
+
 =back
 
 =head1 CONNECTED COMMANDS
index f1a13dc8682da051776c64cfa0510d2973c30487..337dc2c9f9b1d02c92aea276d1c6cf1a5f6305e1 100644 (file)
@@ -557,6 +557,11 @@ cause the client to disconnect due to a protocol violation.
 
 print out some session cache status information.
 
+=item B<-keylogfile path>
+
+Appends TLS secrets to the specified keylog file such that external programs
+(like Wireshark) can decrypt TLS connections.
+
 =back
 
 =head1 NOTES