diff --git a/src/session_openssl.c b/src/session_openssl.c index 0ec1d9b6..c8bad481 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,83 @@ #include #include +#define SSLKEYLOGFILE_ENV "SSLKEYLOGFILE" +/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ +static volatile FILE *keylog_file_fp = NULL; +static volatile uint32_t keylog_tls_sessions = 0; +static pthread_mutex_t keylog_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int +nc_tls_keylog_open(void) +{ + char *keylog_file_name; + + keylog_file_name = getenv(SSLKEYLOGFILE_ENV); + + if (keylog_file_name) { + pthread_mutex_lock(&keylog_mutex); + keylog_tls_sessions++; + DBL(NULL, "TID %lu create tls session: number %d", (long unsigned int)pthread_self(), keylog_tls_sessions); + if (!keylog_file_fp) { + keylog_file_fp = fopen(keylog_file_name, "a"); + DBL(NULL, "TID %lu open keylog file %s", (long unsigned int)pthread_self(), keylog_file_name); + if (keylog_file_fp) { + if (setvbuf((FILE *) keylog_file_fp, NULL, _IOLBF, 4096)) { + fclose((FILE *) keylog_file_fp); + keylog_file_fp = NULL; + } + } + } + pthread_mutex_unlock(&keylog_mutex); + } + return keylog_file_fp != NULL; +} + +static void +nc_tls_keylog_close(void) +{ + if (keylog_file_fp) { + pthread_mutex_lock(&keylog_mutex); + keylog_tls_sessions--; + DBL(NULL, "TID %lu remove tls session: number %d", (long unsigned int)pthread_self(), keylog_tls_sessions); + if (keylog_tls_sessions == 0) { + DBL(NULL, "TID %lu close keylog file", (long unsigned int)pthread_self()); + fclose((FILE *) keylog_file_fp); + keylog_file_fp = NULL; + } + pthread_mutex_unlock(&keylog_mutex); + } +} + +static void +nc_tls_keylog_write_line(const SSL *UNUSED(ssl), const char *line) +{ + /* The current maximum valid keylog line length LF and NUL is 195. */ + size_t linelen; + char buf[256]; + + if (!keylog_file_fp || !line) { + return; + } + + linelen = strlen(line); + if ((linelen == 0) || (linelen > sizeof(buf) - 2)) { + /* Empty line or too big to fit in a LF and NUL. */ + return; + } + + memcpy(buf, line, linelen); + if (line[linelen - 1] != '\n') { + buf[linelen++] = '\n'; + } + buf[linelen] = '\0'; + + /* Using fputs here instead of fprintf since libcurl's fprintf replacement + may not be thread-safe. */ + fputs(buf, (FILE *)keylog_file_fp); + return; +} + void * nc_tls_session_new_wrap(void *tls_cfg) { @@ -54,6 +132,10 @@ nc_tls_session_new_wrap(void *tls_cfg) return NULL; } + if (nc_tls_keylog_open()) { + SSL_CTX_set_keylog_callback(tls_cfg, nc_tls_keylog_write_line); + } + return session; } @@ -61,6 +143,8 @@ void nc_tls_session_destroy_wrap(void *tls_session) { SSL_free(tls_session); + + nc_tls_keylog_close(); } void * diff --git a/tests/test_tls.c b/tests/test_tls.c index c6d7c5ee..96ffc5f5 100644 --- a/tests/test_tls.c +++ b/tests/test_tls.c @@ -27,6 +27,7 @@ int TEST_PORT = 10050; const char *TEST_PORT_STR = "10050"; +const char *KEYLOG_FILE = "keylog.txt"; static void * client_thread(void *arg) @@ -54,6 +55,50 @@ client_thread(void *arg) return NULL; } +#ifndef HAVE_MBEDTLS + +/** test keylog.txt file */ +static void +assert_keylog_file() +{ + FILE *file; + char line[256]; + const char *lines_types[] = { + "SERVER_HANDSHAKE_TRAFFIC_SECRET", + "CLIENT_HANDSHAKE_TRAFFIC_SECRET", + "EXPORTER_SECRET", + "SERVER_TRAFFIC_SECRET_0", + "CLIENT_TRAFFIC_SECRET_0" + }; + int lines_types_count[] = {0, 0, 0, 0, 0}; + + fprintf(stderr, "Checking keylog file\n"); + + if (!getenv("SSLKEYLOGFILE")) { + return; + } + + file = fopen(KEYLOG_FILE, "r"); + assert_non_null(file); + + while (fgets(line, sizeof(line), file)) { + assert_true(strlen(line) > 32); + for (int i = 0; i < 5; i++) { + if (strncmp(line, lines_types[i], strlen(lines_types[i])) == 0) { + lines_types_count[i]++; + } + } + } + for (int i = 0; i < 5; i++) { + assert_int_equal(lines_types_count[i], 2); + } + + fclose(file); + remove(KEYLOG_FILE); +} + +#endif + static void test_nc_tls(void **state) { @@ -70,6 +115,10 @@ test_nc_tls(void **state) for (i = 0; i < 2; i++) { pthread_join(tids[i], NULL); } + +#ifndef HAVE_MBEDTLS + assert_keylog_file(); +#endif } static int @@ -115,11 +164,25 @@ setup_f(void **state) return 0; } +static int +setup_f_with_keylog(void **state) +{ + setenv("SSLKEYLOGFILE", KEYLOG_FILE, 1); + return setup_f(state); +} + +static void +test_nc_tls_with_keylog(void **state) +{ + test_nc_tls(state); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(test_nc_tls, setup_f, ln2_glob_test_teardown), + cmocka_unit_test_setup_teardown(test_nc_tls_with_keylog, setup_f_with_keylog, ln2_glob_test_teardown) }; /* try to get ports from the environment, otherwise use the default */ @@ -128,5 +191,6 @@ main(void) } setenv("CMOCKA_TEST_ABORT", "1", 1); + setenv("SSLKEYLOGFILE", KEYLOG_FILE, 1); return cmocka_run_group_tests(tests, NULL, NULL); }