@@ -49,7 +49,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
4949 id_i_session_id_context , id_i_session_get_cb , id_i_session_new_cb ,
5050 id_i_session_remove_cb , id_i_npn_select_cb , id_i_npn_protocols ,
5151 id_i_alpn_select_cb , id_i_alpn_protocols , id_i_servername_cb ,
52- id_i_verify_hostname ;
52+ id_i_verify_hostname , id_i_keylog_cb ;
5353static ID id_i_io , id_i_context , id_i_hostname ;
5454
5555static int ossl_ssl_ex_vcb_idx ;
@@ -441,6 +441,54 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
441441 return 0 ;
442442}
443443
444+ #if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER )
445+ /*
446+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
447+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
448+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
449+ */
450+
451+ struct ossl_call_keylog_cb_args {
452+ VALUE ssl_obj ;
453+ const char * line ;
454+ };
455+
456+ static VALUE
457+ ossl_call_keylog_cb (VALUE args_v )
458+ {
459+ VALUE sslctx_obj , cb , line_v ;
460+ struct ossl_call_keylog_cb_args * args = (struct ossl_call_keylog_cb_args * ) args_v ;
461+
462+ sslctx_obj = rb_attr_get (args -> ssl_obj , id_i_context );
463+
464+ cb = rb_attr_get (sslctx_obj , id_i_keylog_cb );
465+ if (NIL_P (cb )) return Qnil ;
466+
467+ line_v = rb_str_new_cstr (args -> line );
468+
469+ return rb_funcall (cb , id_call , 2 , args -> ssl_obj , line_v );
470+ }
471+
472+ static void
473+ ossl_sslctx_keylog_cb (const SSL * ssl , const char * line )
474+ {
475+ VALUE ssl_obj ;
476+ struct ossl_call_keylog_cb_args args ;
477+ int state = 0 ;
478+
479+ OSSL_Debug ("SSL keylog callback entered" );
480+
481+ ssl_obj = (VALUE )SSL_get_ex_data (ssl , ossl_ssl_ex_ptr_idx );
482+ args .ssl_obj = ssl_obj ;
483+ args .line = line ;
484+
485+ rb_protect (ossl_call_keylog_cb , (VALUE )& args , & state );
486+ if (state ) {
487+ rb_ivar_set (ssl_obj , ID_callback_state , INT2NUM (state ));
488+ }
489+ }
490+ #endif
491+
444492static VALUE
445493ossl_call_session_remove_cb (VALUE ary )
446494{
@@ -911,6 +959,18 @@ ossl_sslctx_setup(VALUE self)
911959 OSSL_Debug ("SSL TLSEXT servername callback added" );
912960 }
913961
962+ #if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER )
963+ /*
964+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
965+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
966+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
967+ */
968+ if (RTEST (rb_attr_get (self , id_i_keylog_cb ))) {
969+ SSL_CTX_set_keylog_callback (ctx , ossl_sslctx_keylog_cb );
970+ OSSL_Debug ("SSL keylog callback added" );
971+ }
972+ #endif
973+
914974 return Qtrue ;
915975}
916976
@@ -2779,6 +2839,29 @@ Init_ossl_ssl(void)
27792839 */
27802840 rb_attr (cSSLContext , rb_intern_const ("alpn_select_cb" ), 1 , 1 , Qfalse );
27812841
2842+ /*
2843+ * A callback invoked when TLS key material is generated or received, in
2844+ * order to allow applications to store this keying material for debugging
2845+ * purposes.
2846+ *
2847+ * The callback is invoked with an SSLSocket and a string containing the
2848+ * key material in the format used by NSS for its SSLKEYLOGFILE debugging
2849+ * output.
2850+ *
2851+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
2852+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
2853+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
2854+ *
2855+ * === Example
2856+ *
2857+ * context.keylog_cb = proc do |_sock, line|
2858+ * File.open('ssl_keylog_file', "a") do |f|
2859+ * f.write("#{line}\n")
2860+ * end
2861+ * end
2862+ */
2863+ rb_attr (cSSLContext , rb_intern_const ("keylog_cb" ), 1 , 1 , Qfalse );
2864+
27822865 rb_define_alias (cSSLContext , "ssl_timeout" , "timeout" );
27832866 rb_define_alias (cSSLContext , "ssl_timeout=" , "timeout=" );
27842867 rb_define_private_method (cSSLContext , "set_minmax_proto_version" ,
@@ -3060,6 +3143,7 @@ Init_ossl_ssl(void)
30603143 DefIVarID (alpn_select_cb );
30613144 DefIVarID (servername_cb );
30623145 DefIVarID (verify_hostname );
3146+ DefIVarID (keylog_cb );
30633147
30643148 DefIVarID (io );
30653149 DefIVarID (context );
0 commit comments