tls: expose keylog event on TLSSocket · nodejs/node@eb1f4e5 · GitHub
Skip to content

Commit eb1f4e5

Browse files
mildsunrisetargos
authored andcommitted
tls: expose keylog event on TLSSocket
Exposes SSL_CTX_set_keylog_callback in the form of a `keylog` event that is emitted on clients and servers. This enables easy debugging of TLS connections with i.e. Wireshark, which is a long-requested feature. PR-URL: #27654 Refs: #2363 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 700459e commit eb1f4e5

8 files changed

Lines changed: 143 additions & 1 deletion

File tree

doc/api/tls.md

Lines changed: 49 additions & 0 deletions

lib/_tls_wrap.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,18 @@ function onnewsession(sessionId, session) {
286286
}
287287

288288

289+
function onkeylogclient(line) {
290+
debug('client onkeylog');
291+
this[owner_symbol].emit('keylog', line);
292+
}
293+
294+
function onkeylog(line) {
295+
debug('server onkeylog');
296+
const owner = this[owner_symbol];
297+
if (owner.server)
298+
owner.server.emit('keylog', line, owner);
299+
}
300+
289301
function onocspresponse(resp) {
290302
debug('client onocspresponse');
291303
this[owner_symbol].emit('OCSPResponse', resp);
@@ -571,6 +583,7 @@ TLSSocket.prototype._init = function(socket, wrap) {
571583
ssl.onclienthello = loadSession;
572584
ssl.oncertcb = loadSNI;
573585
ssl.onnewsession = onnewsession;
586+
ssl.onkeylog = onkeylog;
574587
ssl.lastHandshakeTime = 0;
575588
ssl.handshakes = 0;
576589

@@ -580,6 +593,8 @@ TLSSocket.prototype._init = function(socket, wrap) {
580593
// Also starts the client hello parser as a side effect.
581594
ssl.enableSessionCallbacks();
582595
}
596+
if (this.server.listenerCount('keylog') > 0)
597+
ssl.enableKeylogCallback();
583598
if (this.server.listenerCount('OCSPRequest') > 0)
584599
ssl.enableCertCb();
585600
}
@@ -605,9 +620,24 @@ TLSSocket.prototype._init = function(socket, wrap) {
605620

606621
ssl.enableSessionCallbacks();
607622

608-
// Remover this listener since its no longer needed.
623+
// Remove this listener since it's no longer needed.
609624
this.removeListener('newListener', newListener);
610625
}
626+
627+
ssl.onkeylog = onkeylogclient;
628+
629+
// Only call .onkeylog if there is a keylog listener.
630+
this.on('newListener', keylogNewListener);
631+
632+
function keylogNewListener(event) {
633+
if (event !== 'keylog')
634+
return;
635+
636+
ssl.enableKeylogCallback();
637+
638+
// Remove this listener since it's no longer needed.
639+
this.removeListener('newListener', keylogNewListener);
640+
}
611641
}
612642

613643
ssl.onerror = onerror;

src/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
252252
V(onexit_string, "onexit") \
253253
V(onhandshakedone_string, "onhandshakedone") \
254254
V(onhandshakestart_string, "onhandshakestart") \
255+
V(onkeylog_string, "onkeylog") \
255256
V(onmessage_string, "onmessage") \
256257
V(onnewsession_string, "onnewsession") \
257258
V(onocspresponse_string, "onocspresponse") \

src/node_crypto.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ template SSL_SESSION* SSLWrap<TLSWrap>::GetSessionCallback(
149149
int* copy);
150150
template int SSLWrap<TLSWrap>::NewSessionCallback(SSL* s,
151151
SSL_SESSION* sess);
152+
template void SSLWrap<TLSWrap>::KeylogCallback(const SSL* s,
153+
const char* line);
152154
template void SSLWrap<TLSWrap>::OnClientHello(
153155
void* arg,
154156
const ClientHelloParser::ClientHello& hello);
@@ -1749,6 +1751,21 @@ int SSLWrap<Base>::NewSessionCallback(SSL* s, SSL_SESSION* sess) {
17491751
}
17501752

17511753

1754+
template <class Base>
1755+
void SSLWrap<Base>::KeylogCallback(const SSL* s, const char* line) {
1756+
Base* w = static_cast<Base*>(SSL_get_app_data(s));
1757+
Environment* env = w->ssl_env();
1758+
HandleScope handle_scope(env->isolate());
1759+
Context::Scope context_scope(env->context());
1760+
1761+
const size_t size = strlen(line);
1762+
Local<Value> line_bf = Buffer::Copy(env, line, 1 + size).ToLocalChecked();
1763+
char* data = Buffer::Data(line_bf);
1764+
data[size] = '\n';
1765+
w->MakeCallback(env->onkeylog_string(), 1, &line_bf);
1766+
}
1767+
1768+
17521769
template <class Base>
17531770
void SSLWrap<Base>::OnClientHello(void* arg,
17541771
const ClientHelloParser::ClientHello& hello) {

src/node_crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class SSLWrap {
256256
int* copy);
257257
#endif
258258
static int NewSessionCallback(SSL* s, SSL_SESSION* sess);
259+
static void KeylogCallback(const SSL* s, const char* line);
259260
static void OnClientHello(void* arg,
260261
const ClientHelloParser::ClientHello& hello);
261262

src/tls_wrap.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,15 @@ void TLSWrap::EnableSessionCallbacks(
912912
wrap);
913913
}
914914

915+
void TLSWrap::EnableKeylogCallback(
916+
const FunctionCallbackInfo<Value>& args) {
917+
TLSWrap* wrap;
918+
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
919+
CHECK_NOT_NULL(wrap->sc_);
920+
SSL_CTX_set_keylog_callback(wrap->sc_->ctx_.get(),
921+
SSLWrap<TLSWrap>::KeylogCallback);
922+
}
923+
915924
// Check required capabilities were not excluded from the OpenSSL build:
916925
// - OPENSSL_NO_SSL_TRACE excludes SSL_trace()
917926
// - OPENSSL_NO_STDIO excludes BIO_new_fp()
@@ -1105,6 +1114,7 @@ void TLSWrap::Initialize(Local<Object> target,
11051114
env->SetProtoMethod(t, "start", Start);
11061115
env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
11071116
env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks);
1117+
env->SetProtoMethod(t, "enableKeylogCallback", EnableKeylogCallback);
11081118
env->SetProtoMethod(t, "enableTrace", EnableTrace);
11091119
env->SetProtoMethod(t, "destroySSL", DestroySSL);
11101120
env->SetProtoMethod(t, "enableCertCb", EnableCertCb);

src/tls_wrap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class TLSWrap : public AsyncWrap,
160160
static void SetVerifyMode(const v8::FunctionCallbackInfo<v8::Value>& args);
161161
static void EnableSessionCallbacks(
162162
const v8::FunctionCallbackInfo<v8::Value>& args);
163+
static void EnableKeylogCallback(
164+
const v8::FunctionCallbackInfo<v8::Value>& args);
163165
static void EnableTrace(const v8::FunctionCallbackInfo<v8::Value>& args);
164166
static void EnableCertCb(const v8::FunctionCallbackInfo<v8::Value>& args);
165167
static void DestroySSL(const v8::FunctionCallbackInfo<v8::Value>& args);
Lines changed: 32 additions & 0 deletions

0 commit comments

Comments
 (0)