[ruby/openssl] Add support to SSL_CTX_set_keylog_callback
- This callback is invoked when TLS key material is generated or
received, in order to allow applications to store this keying material
for debugging purposes.
- It is invoked with an `SSLSocket` and a string containing the key
material in the format used by NSS for its SSLKEYLOGFILE debugging
output.
- This commit adds the Ruby binding `keylog_cb` and the related tests
- It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
`SSL_CTX_set_keylog_callback()` from v3.4.2, it does nothing (see
648d39f0f0
)
https://github.com/ruby/openssl/commit/3b63232cf1
This commit is contained in:
parent
e4b1627983
commit
17998ad3bb
@ -49,7 +49,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
|
|||||||
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
|
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
|
||||||
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
|
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
|
||||||
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
|
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
|
||||||
id_i_verify_hostname;
|
id_i_verify_hostname, id_i_keylog_cb;
|
||||||
static ID id_i_io, id_i_context, id_i_hostname;
|
static ID id_i_io, id_i_context, id_i_hostname;
|
||||||
|
|
||||||
static int ossl_ssl_ex_vcb_idx;
|
static int ossl_ssl_ex_vcb_idx;
|
||||||
@ -441,6 +441,54 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
||||||
|
/*
|
||||||
|
* It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
|
||||||
|
* SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
|
||||||
|
* https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ossl_call_keylog_cb_args {
|
||||||
|
VALUE ssl_obj;
|
||||||
|
const char * line;
|
||||||
|
};
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
ossl_call_keylog_cb(VALUE args_v)
|
||||||
|
{
|
||||||
|
VALUE sslctx_obj, cb, line_v;
|
||||||
|
struct ossl_call_keylog_cb_args *args = (struct ossl_call_keylog_cb_args *) args_v;
|
||||||
|
|
||||||
|
sslctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
|
||||||
|
|
||||||
|
cb = rb_attr_get(sslctx_obj, id_i_keylog_cb);
|
||||||
|
if (NIL_P(cb)) return Qnil;
|
||||||
|
|
||||||
|
line_v = rb_str_new_cstr(args->line);
|
||||||
|
|
||||||
|
return rb_funcall(cb, id_call, 2, args->ssl_obj, line_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ossl_sslctx_keylog_cb(const SSL *ssl, const char *line)
|
||||||
|
{
|
||||||
|
VALUE ssl_obj;
|
||||||
|
struct ossl_call_keylog_cb_args args;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
OSSL_Debug("SSL keylog callback entered");
|
||||||
|
|
||||||
|
ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
|
||||||
|
args.ssl_obj = ssl_obj;
|
||||||
|
args.line = line;
|
||||||
|
|
||||||
|
rb_protect(ossl_call_keylog_cb, (VALUE)&args, &state);
|
||||||
|
if (state) {
|
||||||
|
rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
ossl_call_session_remove_cb(VALUE ary)
|
ossl_call_session_remove_cb(VALUE ary)
|
||||||
{
|
{
|
||||||
@ -911,6 +959,18 @@ ossl_sslctx_setup(VALUE self)
|
|||||||
OSSL_Debug("SSL TLSEXT servername callback added");
|
OSSL_Debug("SSL TLSEXT servername callback added");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
||||||
|
/*
|
||||||
|
* It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
|
||||||
|
* SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
|
||||||
|
* https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
|
||||||
|
*/
|
||||||
|
if (RTEST(rb_attr_get(self, id_i_keylog_cb))) {
|
||||||
|
SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb);
|
||||||
|
OSSL_Debug("SSL keylog callback added");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2783,6 +2843,29 @@ Init_ossl_ssl(void)
|
|||||||
*/
|
*/
|
||||||
rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse);
|
rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A callback invoked when TLS key material is generated or received, in
|
||||||
|
* order to allow applications to store this keying material for debugging
|
||||||
|
* purposes.
|
||||||
|
*
|
||||||
|
* The callback is invoked with an SSLSocket and a string containing the
|
||||||
|
* key material in the format used by NSS for its SSLKEYLOGFILE debugging
|
||||||
|
* output.
|
||||||
|
*
|
||||||
|
* It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
|
||||||
|
* SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
|
||||||
|
* https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
|
||||||
|
*
|
||||||
|
* === Example
|
||||||
|
*
|
||||||
|
* context.keylog_cb = proc do |_sock, line|
|
||||||
|
* File.open('ssl_keylog_file', "a") do |f|
|
||||||
|
* f.write("#{line}\n")
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
rb_attr(cSSLContext, rb_intern_const("keylog_cb"), 1, 1, Qfalse);
|
||||||
|
|
||||||
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
|
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
|
||||||
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
|
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
|
||||||
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
|
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
|
||||||
@ -3064,6 +3147,7 @@ Init_ossl_ssl(void)
|
|||||||
DefIVarID(alpn_select_cb);
|
DefIVarID(alpn_select_cb);
|
||||||
DefIVarID(servername_cb);
|
DefIVarID(servername_cb);
|
||||||
DefIVarID(verify_hostname);
|
DefIVarID(verify_hostname);
|
||||||
|
DefIVarID(keylog_cb);
|
||||||
|
|
||||||
DefIVarID(io);
|
DefIVarID(io);
|
||||||
DefIVarID(context);
|
DefIVarID(context);
|
||||||
|
@ -804,6 +804,54 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_keylog_cb
|
||||||
|
pend "Keylog callback is not supported" if !openssl?(1, 1, 1) || libressl?
|
||||||
|
|
||||||
|
prefix = 'CLIENT_RANDOM'
|
||||||
|
context = OpenSSL::SSL::SSLContext.new
|
||||||
|
context.min_version = context.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||||
|
|
||||||
|
cb_called = false
|
||||||
|
context.keylog_cb = proc do |_sock, line|
|
||||||
|
cb_called = true
|
||||||
|
assert_equal(prefix, line.split.first)
|
||||||
|
end
|
||||||
|
|
||||||
|
start_server do |port|
|
||||||
|
server_connect(port, context) do |ssl|
|
||||||
|
ssl.puts "abc"
|
||||||
|
assert_equal("abc\n", ssl.gets)
|
||||||
|
assert_equal(true, cb_called)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if tls13_supported?
|
||||||
|
prefixes = [
|
||||||
|
'SERVER_HANDSHAKE_TRAFFIC_SECRET',
|
||||||
|
'EXPORTER_SECRET',
|
||||||
|
'SERVER_TRAFFIC_SECRET_0',
|
||||||
|
'CLIENT_HANDSHAKE_TRAFFIC_SECRET',
|
||||||
|
'CLIENT_TRAFFIC_SECRET_0',
|
||||||
|
]
|
||||||
|
context = OpenSSL::SSL::SSLContext.new
|
||||||
|
context.min_version = context.max_version = OpenSSL::SSL::TLS1_3_VERSION
|
||||||
|
cb_called = false
|
||||||
|
context.keylog_cb = proc do |_sock, line|
|
||||||
|
cb_called = true
|
||||||
|
assert_not_nil(prefixes.delete(line.split.first))
|
||||||
|
end
|
||||||
|
|
||||||
|
start_server do |port|
|
||||||
|
server_connect(port, context) do |ssl|
|
||||||
|
ssl.puts "abc"
|
||||||
|
assert_equal("abc\n", ssl.gets)
|
||||||
|
assert_equal(true, cb_called)
|
||||||
|
end
|
||||||
|
assert_equal(0, prefixes.size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_tlsext_hostname
|
def test_tlsext_hostname
|
||||||
fooctx = OpenSSL::SSL::SSLContext.new
|
fooctx = OpenSSL::SSL::SSLContext.new
|
||||||
fooctx.cert = @cli_cert
|
fooctx.cert = @cli_cert
|
||||||
|
Loading…
x
Reference in New Issue
Block a user