diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 7e12456aa..90f94ec8a 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -93,6 +93,7 @@ #cmakedefine LWS_HAVE_SSL_CTX_load_verify_dir #cmakedefine LWS_HAVE_SSL_CTX_set1_param #cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites +#cmakedefine LWS_HAVE_SSL_CTX_set_keylog_callback #cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS #cmakedefine LWS_HAVE_SSL_get0_alpn_selected #cmakedefine LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 1914190f7..4e189306a 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -426,6 +426,10 @@ struct lws_context { #if defined(LWS_WITH_SERVER) char canonical_hostname[96]; #endif +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) + char keylog_file[96]; +#endif #if defined(LWS_WITH_FILE_OPS) struct lws_plat_file_ops fops_platform; diff --git a/lib/plat/unix/unix-init.c b/lib/plat/unix/unix-init.c index 3e17991a2..383a1e896 100644 --- a/lib/plat/unix/unix-init.c +++ b/lib/plat/unix/unix-init.c @@ -178,6 +178,17 @@ lws_plat_init(struct lws_context *context, return 1; } +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) + { + char *klf_env = getenv("SSLKEYLOGFILE"); + + if (klf_env) + lws_strncpy(context->keylog_file, klf_env, + sizeof(context->keylog_file)); + } +#endif + #if defined(LWS_WITH_PLUGINS) && !defined(LWS_WITH_PLUGINS_BUILTIN) { char *ld_env = getenv("LD_LIBRARY_PATH"); diff --git a/lib/plat/windows/windows-init.c b/lib/plat/windows/windows-init.c index 492b531e6..f5883ad2f 100644 --- a/lib/plat/windows/windows-init.c +++ b/lib/plat/windows/windows-init.c @@ -104,6 +104,17 @@ lws_plat_init(struct lws_context *context, } #endif +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) + { + char *klf_env = getenv("SSLKEYLOGFILE"); + + if (klf_env) + lws_strncpy(context->keylog_file, klf_env, + sizeof(context->keylog_file)); + } +#endif + for (i = 0; i < FD_HASHTABLE_MODULUS; i++) { context->fd_hashtable[i].wsi = lws_zalloc(sizeof(struct lws*) * context->max_fds, diff --git a/lib/tls/CMakeLists.txt b/lib/tls/CMakeLists.txt index ea84a9b5c..3ab5524b0 100644 --- a/lib/tls/CMakeLists.txt +++ b/lib/tls/CMakeLists.txt @@ -344,6 +344,7 @@ CHECK_SYMBOL_EXISTS(${VARIA}SSL_CTX_set_ciphersuites LWS_HAVE_SSL_CTX_set_cipher CHECK_FUNCTION_EXISTS(${VARIA}EVP_PKEY_new_raw_private_key LWS_HAVE_EVP_PKEY_new_raw_private_key PARENT_SCOPE) CHECK_FUNCTION_EXISTS(${VARIA}SSL_SESSION_set_time LWS_HAVE_SSL_SESSION_set_time PARENT_SCOPE) CHECK_SYMBOL_EXISTS(${VARIA}SSL_SESSION_up_ref LWS_HAVE_SSL_SESSION_up_ref PARENT_SCOPE) +CHECK_FUNCTION_EXISTS(${VARIA}SSL_CTX_set_keylog_callback LWS_HAVE_SSL_CTX_set_keylog_callback PARENT_SCOPE) # deprecated in openssl v3 diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c index d8c56c518..62dca7381 100644 --- a/lib/tls/openssl/openssl-client.c +++ b/lib/tls/openssl/openssl-client.c @@ -705,6 +705,60 @@ lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, return n != 1; } +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) +static void +lws_klog_dump(const SSL *ssl, const char *line) +{ + struct lws *wsi = SSL_get_ex_data(ssl, + openssl_websocket_private_data_index); + char path[128], hdr[128], ts[64]; + size_t w = 0, wx = 0; + int fd, t; + + if (!wsi || !wsi->a.context->keylog_file[0] || !wsi->a.vhost) + return; + + lws_snprintf(path, sizeof(path), "%s.%s", wsi->a.context->keylog_file, + wsi->a.vhost->name); + + fd = open(path, O_CREAT | O_RDWR | O_APPEND, 0600); + if (fd == -1) { + lwsl_vhost_warn(wsi->a.vhost, "Failed to append %s", path); + return; + } + + /* the first item in the chunk */ + if (!strncmp(line, "SERVER_HANDSHAKE_TRAFFIC_SECRET", 31)) { + w += (size_t)write(fd, "\n# ", 3); + wx += 3; + t = lwsl_timestamp(LLL_WARN, ts, sizeof(ts)); + wx += (size_t)t; + w += (size_t)write(fd, ts, (size_t)t); + + t = lws_snprintf(hdr, sizeof(hdr), "%s\n", wsi->lc.gutag); + w += (size_t)write(fd, hdr, (size_t)t); + wx += (size_t)t; + + lwsl_vhost_warn(wsi->a.vhost, "appended ssl keylog: %s", path); + } + + wx += strlen(line) + 1; + w += (size_t)write(fd, line, +#if defined(WIN32) + (unsigned int) +#endif + strlen(line)); + w += (size_t)write(fd, "\n", 1); + close(fd); + + if (w != wx) { + lwsl_vhost_warn(wsi->a.vhost, "Failed to write %s", path); + return; + } +} +#endif + int lws_tls_client_create_vhost_context(struct lws_vhost *vh, const struct lws_context_creation_info *info, @@ -887,6 +941,12 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh, vh->tls.tcr = tcr; +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) + if (vh->context->keylog_file[0]) + SSL_CTX_set_keylog_callback(vh->tls.ssl_client_ctx, lws_klog_dump); +#endif + #if defined(LWS_WITH_TLS_SESSIONS) vh->tls_session_cache_max = info->tls_session_cache_max ? info->tls_session_cache_max : 10;