mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/openssl/ossl_ssl.c: add ALPN support. [Feature #9390]
* ext/openssl/extconf.rb: detect ALPN support in OpenSSL * test/openssl/test_ssl.rb: test for ALPN git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51347 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
3b0d490548
commit
bc6e55c084
4 changed files with 144 additions and 4 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
Thu Jul 23 03:29:49 2015 Aaron Patterson <tenderlove@ruby-lang.org>
|
||||||
|
|
||||||
|
* ext/openssl/ossl_ssl.c: add ALPN support. [Feature #9390]
|
||||||
|
|
||||||
|
* ext/openssl/extconf.rb: detect ALPN support in OpenSSL
|
||||||
|
|
||||||
|
* test/openssl/test_ssl.rb: test for ALPN
|
||||||
|
|
||||||
Wed Jul 22 23:44:17 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Wed Jul 22 23:44:17 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* string.c (rb_str_reverse): reversed string is not a substring,
|
* string.c (rb_str_reverse): reversed string is not a substring,
|
||||||
|
|
|
@ -110,6 +110,7 @@ have_func("TLSv1_1_client_method")
|
||||||
have_func("TLSv1_2_method")
|
have_func("TLSv1_2_method")
|
||||||
have_func("TLSv1_2_server_method")
|
have_func("TLSv1_2_server_method")
|
||||||
have_func("TLSv1_2_client_method")
|
have_func("TLSv1_2_client_method")
|
||||||
|
have_func("SSL_CTX_set_alpn_select_cb")
|
||||||
have_macro("OPENSSL_NPN_NEGOTIATED", ['openssl/ssl.h']) && $defs.push("-DHAVE_OPENSSL_NPN_NEGOTIATED")
|
have_macro("OPENSSL_NPN_NEGOTIATED", ['openssl/ssl.h']) && $defs.push("-DHAVE_OPENSSL_NPN_NEGOTIATED")
|
||||||
unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h'])
|
unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h'])
|
||||||
have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME")
|
have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME")
|
||||||
|
|
|
@ -600,13 +600,13 @@ ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded)
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static VALUE
|
||||||
ssl_npn_encode_protocols(VALUE sslctx, VALUE protocols)
|
ssl_encode_npn_protocols(VALUE protocols)
|
||||||
{
|
{
|
||||||
VALUE encoded = rb_str_new2("");
|
VALUE encoded = rb_str_new2("");
|
||||||
rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded);
|
rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded);
|
||||||
StringValueCStr(encoded);
|
StringValueCStr(encoded);
|
||||||
rb_iv_set(sslctx, "@_protocols", encoded);
|
return encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -645,6 +645,34 @@ ssl_npn_select_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsi
|
||||||
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
|
||||||
|
static int
|
||||||
|
ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
VALUE sslctx_obj, cb, protocols, selected;
|
||||||
|
|
||||||
|
sslctx_obj = (VALUE) arg;
|
||||||
|
cb = rb_iv_get(sslctx_obj, "@alpn_select_cb");
|
||||||
|
protocols = rb_ary_new();
|
||||||
|
|
||||||
|
/* The format is len_1|proto_1|...|len_n|proto_n\0 */
|
||||||
|
while (in[i]) {
|
||||||
|
VALUE protocol = rb_str_new((const char *) &in[i + 1], in[i]);
|
||||||
|
rb_ary_push(protocols, protocol);
|
||||||
|
i += in[i] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = rb_funcall(cb, rb_intern("call"), 1, protocols);
|
||||||
|
*out = (unsigned char *) StringValuePtr(selected);
|
||||||
|
*outlen = RSTRING_LENINT(selected);
|
||||||
|
rb_iv_set(sslctx_obj, "@_alpn_selected", selected);
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This function may serve as the entry point to support further
|
/* This function may serve as the entry point to support further
|
||||||
|
@ -781,7 +809,7 @@ ossl_sslctx_setup(VALUE self)
|
||||||
#ifdef HAVE_OPENSSL_NPN_NEGOTIATED
|
#ifdef HAVE_OPENSSL_NPN_NEGOTIATED
|
||||||
val = rb_iv_get(self, "@npn_protocols");
|
val = rb_iv_get(self, "@npn_protocols");
|
||||||
if (!NIL_P(val)) {
|
if (!NIL_P(val)) {
|
||||||
ssl_npn_encode_protocols(self, val);
|
rb_iv_set(self, "@_protocols", ssl_encode_npn_protocols(val));
|
||||||
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *) self);
|
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *) self);
|
||||||
OSSL_Debug("SSL NPN advertise callback added");
|
OSSL_Debug("SSL NPN advertise callback added");
|
||||||
}
|
}
|
||||||
|
@ -791,6 +819,19 @@ ossl_sslctx_setup(VALUE self)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
|
||||||
|
val = rb_iv_get(self, "@alpn_protocols");
|
||||||
|
if (!NIL_P(val)) {
|
||||||
|
VALUE rprotos = ssl_encode_npn_protocols(val);
|
||||||
|
SSL_CTX_set_alpn_protos(ctx, StringValueCStr(rprotos), RSTRING_LEN(rprotos));
|
||||||
|
OSSL_Debug("SSL ALPN values added");
|
||||||
|
}
|
||||||
|
if (RTEST(rb_iv_get(self, "@alpn_select_cb"))) {
|
||||||
|
SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self);
|
||||||
|
OSSL_Debug("SSL ALPN select callback added");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
rb_obj_freeze(self);
|
rb_obj_freeze(self);
|
||||||
|
|
||||||
val = ossl_sslctx_get_sess_id_ctx(self);
|
val = ossl_sslctx_get_sess_id_ctx(self);
|
||||||
|
@ -1904,6 +1945,29 @@ ossl_ssl_npn_protocol(VALUE self)
|
||||||
else
|
else
|
||||||
return rb_str_new((const char *) out, outlen);
|
return rb_str_new((const char *) out, outlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ssl.alpn_protocol => String
|
||||||
|
*
|
||||||
|
* Returns the ALPN protocol string that was finally selected by the client
|
||||||
|
* during the handshake.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
ossl_ssl_alpn_protocol(VALUE self)
|
||||||
|
{
|
||||||
|
SSL *ssl;
|
||||||
|
const unsigned char *out;
|
||||||
|
unsigned int outlen;
|
||||||
|
|
||||||
|
ossl_ssl_data_get_struct(self, ssl);
|
||||||
|
|
||||||
|
SSL_get0_alpn_selected(ssl, &out, &outlen);
|
||||||
|
if (!outlen)
|
||||||
|
return Qnil;
|
||||||
|
else
|
||||||
|
return rb_str_new((const char *) out, outlen);
|
||||||
|
}
|
||||||
# endif
|
# endif
|
||||||
#endif /* !defined(OPENSSL_NO_SOCK) */
|
#endif /* !defined(OPENSSL_NO_SOCK) */
|
||||||
|
|
||||||
|
@ -2155,6 +2219,38 @@ Init_ossl_ssl(void)
|
||||||
rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse);
|
rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
|
||||||
|
/*
|
||||||
|
* An Enumerable of Strings. Each String represents a protocol to be
|
||||||
|
* advertised as the list of supported protocols for Application-Layer Protocol
|
||||||
|
* Negotiation. Supported in OpenSSL 1.0.1 and higher. Has no effect
|
||||||
|
* on the client side. If not set explicitly, the NPN extension will
|
||||||
|
* not be sent by the server in the handshake.
|
||||||
|
*
|
||||||
|
* === Example
|
||||||
|
*
|
||||||
|
* ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"]
|
||||||
|
*/
|
||||||
|
rb_attr(cSSLContext, rb_intern("alpn_protocols"), 1, 1, Qfalse);
|
||||||
|
/*
|
||||||
|
* A callback invoked on the server side when the server needs to select
|
||||||
|
* a protocol from the list sent by the client. Supported in OpenSSL 1.0.2
|
||||||
|
* and higher. The server MUST select a protocol of those advertised by
|
||||||
|
* the client. If none is acceptable, raising an error in the callback
|
||||||
|
* will cause the handshake to fail. Not setting this callback explicitly
|
||||||
|
* means not supporting the ALPN extension on the client - any protocols
|
||||||
|
* advertised by the server will be ignored.
|
||||||
|
*
|
||||||
|
* === Example
|
||||||
|
*
|
||||||
|
* ctx.alpn_select_cb = lambda do |protocols|
|
||||||
|
* #inspect the protocols and select one
|
||||||
|
* protocols.first
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
rb_attr(cSSLContext, rb_intern("alpn_select_cb"), 1, 1, Qfalse);
|
||||||
|
#endif
|
||||||
|
|
||||||
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_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1);
|
rb_define_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1);
|
||||||
|
@ -2267,6 +2363,9 @@ Init_ossl_ssl(void)
|
||||||
rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1);
|
rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1);
|
||||||
rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0);
|
rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0);
|
||||||
rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0);
|
rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0);
|
||||||
|
# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
|
||||||
|
rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
|
||||||
|
# endif
|
||||||
# ifdef HAVE_OPENSSL_NPN_NEGOTIATED
|
# ifdef HAVE_OPENSSL_NPN_NEGOTIATED
|
||||||
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
|
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
|
||||||
# endif
|
# endif
|
||||||
|
|
|
@ -795,6 +795,38 @@ end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
|
||||||
|
def test_alpn_protocol_selection_ary
|
||||||
|
advertised = ["http/1.1", "spdy/2"]
|
||||||
|
ctx_proc = Proc.new { |ctx|
|
||||||
|
ctx.alpn_select_cb = -> (protocols) {
|
||||||
|
protocols.first
|
||||||
|
}
|
||||||
|
ctx.alpn_protocols = advertised
|
||||||
|
}
|
||||||
|
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
||||||
|
ctx = OpenSSL::SSL::SSLContext.new
|
||||||
|
ctx.alpn_protocols = advertised
|
||||||
|
server_connect(port, ctx) { |ssl|
|
||||||
|
assert_equal(advertised.first, ssl.alpn_protocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_alpn_protocol_selection_cancel
|
||||||
|
ctx_proc = Proc.new { |ctx|
|
||||||
|
ctx.alpn_select_cb = -> (protocols) { nil }
|
||||||
|
}
|
||||||
|
assert_raises(MiniTest::Assertion) do # minitest/assertion comes from `assert_join_threads`
|
||||||
|
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
||||||
|
ctx = OpenSSL::SSL::SSLContext.new
|
||||||
|
ctx.alpn_protocols = ["http/1.1"]
|
||||||
|
assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
|
if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
|
||||||
|
|
||||||
def test_npn_protocol_selection_ary
|
def test_npn_protocol_selection_ary
|
||||||
|
|
Loading…
Reference in a new issue