diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2018-05-11 20:06:38 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2021-03-16 18:25:55 +0900 |
commit | 461e8ab7787e9e70e5edc05bff828de3b68e2945 (patch) | |
tree | f29d0640b26c7d07bb6d6e75cec19bef0d4ccf2c | |
parent | bd3508da3c1036e8633919ec1f320e444cd2d75f (diff) | |
download | ruby-openssl-ky/ssl-sslsocket-hostname-server-side.tar.gz |
ssl: store SNI hostname in SSLSocket#hostname on the server-sideky/ssl-sslsocket-hostname-server-side
On server-side, OpenSSL::SSL::SSLSocket#hostname now returns the
hostname sent by the client using SNI.
-rw-r--r-- | ext/openssl/ossl_ssl.c | 46 | ||||
-rw-r--r-- | test/openssl/test_ssl.rb | 42 |
2 files changed, 51 insertions, 37 deletions
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index c38142bf..e28739de 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -506,56 +506,52 @@ ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) static VALUE ossl_sslctx_setup(VALUE self); static VALUE -ossl_call_servername_cb(VALUE ary) +ossl_call_servername_cb(VALUE ssl_) { - VALUE ssl_obj, sslctx_obj, cb, ret_obj; + SSL *ssl = (void *)ssl_; + const char *servername; + VALUE ssl_obj, sslctx_obj, cb, str, cb_arg, ret_obj; - Check_Type(ary, T_ARRAY); - ssl_obj = rb_ary_entry(ary, 0); + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) + return Qnil; + + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + str = rb_str_new(servername, strlen(servername)); + rb_ivar_set(ssl_obj, id_i_hostname, str); sslctx_obj = rb_attr_get(ssl_obj, id_i_context); cb = rb_attr_get(sslctx_obj, id_i_servername_cb); - if (NIL_P(cb)) return Qnil; + if (NIL_P(cb)) + return Qnil; - ret_obj = rb_funcallv(cb, id_call, 1, &ary); + cb_arg = rb_ary_new_from_args(2, ssl_obj, str); + ret_obj = rb_funcallv(cb, id_call, 1, &cb_arg); if (rb_obj_is_kind_of(ret_obj, cSSLContext)) { - SSL *ssl; SSL_CTX *ctx2; ossl_sslctx_setup(ret_obj); - GetSSL(ssl_obj, ssl); GetSSLCTX(ret_obj, ctx2); SSL_set_SSL_CTX(ssl, ctx2); rb_ivar_set(ssl_obj, id_i_context, ret_obj); } else if (!NIL_P(ret_obj)) { - ossl_raise(rb_eArgError, "servername_cb must return an " - "OpenSSL::SSL::SSLContext object or nil"); + rb_raise(rb_eArgError, "servername_cb must return an " + "OpenSSL::SSL::SSLContext object or nil"); } - - return ret_obj; + return Qnil; } static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { - VALUE ary, ssl_obj; - int state = 0; - const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - - if (!servername) - return SSL_TLSEXT_ERR_OK; - - ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - ary = rb_ary_new2(2); - rb_ary_push(ary, ssl_obj); - rb_ary_push(ary, rb_str_new2(servername)); + int state; - rb_protect(ossl_call_servername_cb, ary, &state); + rb_protect(ossl_call_servername_cb, (VALUE)ssl, &state); if (state) { + VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); return SSL_TLSEXT_ERR_ALERT_FATAL; } - return SSL_TLSEXT_ERR_OK; } diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 50a16029..6ae6ca0b 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -837,30 +837,31 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end end - def test_tlsext_hostname + def test_sni_server_callback_replace_ctx fooctx = OpenSSL::SSL::SSLContext.new fooctx.tmp_dh_callback = proc { Fixtures.pkey("dh-1") } fooctx.cert = @cli_cert fooctx.key = @cli_key - ctx_proc = proc { |ctx| + servername_cb_called = false + ctx_proc = -> ctx { ctx.servername_cb = proc { |ssl, servername| - case servername - when "foo.example.com" - fooctx - when "bar.example.com" - nil - else - raise "unreachable" - end + servername_cb_called = true + assert_equal "foo.example.com", servername + fooctx } } - start_server(ctx_proc: ctx_proc) do |port| + server_proc = -> (ctx, ssl) { + assert_equal "foo.example.com", ssl.hostname + readwrite_loop(ctx, ssl) + } + start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| sock = TCPSocket.new("127.0.0.1", port) begin ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.hostname = "foo.example.com" ssl.connect + assert_equal true, servername_cb_called assert_equal @cli_cert.serial, ssl.peer_cert.serial assert_predicate fooctx, :frozen? @@ -869,12 +870,29 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase ssl&.close sock.close end + end + end + def test_sni_server_callback_no_replace_ctx + servername_cb_called = false + ctx_proc = proc { |ctx| + ctx.servername_cb = proc { |ssl, servername| + servername_cb_called = true + assert_equal "bar.example.com", servername + nil + } + } + server_proc = -> (ctx, ssl) { + assert_equal "bar.example.com", ssl.hostname + readwrite_loop(ctx, ssl) + } + start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| sock = TCPSocket.new("127.0.0.1", port) begin ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.hostname = "bar.example.com" ssl.connect + assert_equal true, servername_cb_called assert_equal @svr_cert.serial, ssl.peer_cert.serial ssl.puts "abc"; assert_equal "abc\n", ssl.gets @@ -885,7 +903,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end end - def test_servername_cb_raises_an_exception_on_unknown_objects + def test_sni_server_callback_error hostname = 'example.org' ctx2 = OpenSSL::SSL::SSLContext.new |