aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2018-05-11 20:06:38 +0900
committerKazuki Yamaguchi <k@rhe.jp>2021-03-16 18:25:55 +0900
commit461e8ab7787e9e70e5edc05bff828de3b68e2945 (patch)
treef29d0640b26c7d07bb6d6e75cec19bef0d4ccf2c
parentbd3508da3c1036e8633919ec1f320e444cd2d75f (diff)
downloadruby-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.c46
-rw-r--r--test/openssl/test_ssl.rb42
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