mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/openssl/ossl_ssl.c: Introduce SSLContext#renegotiation_cb and
remove SSLContext#disable_client_renegotiation and related functionality introduced in r35797. The new callback approach gives clients maximum flexibility to decide on their own what to do on renegotiation attempts. Add documentation for SSL module and SSLError. * test/openssl/test_ssl.rb: Add a test for SSLContext#renegotiation_cb. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35994 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
f45eb45100
commit
14ba7fab58
3 changed files with 71 additions and 98 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
||||||
|
Sun Jun 10 01:41:45 2012 Martin Bosslet <Martin.Bosslet@googlemail.com>
|
||||||
|
|
||||||
|
* ext/openssl/ossl_ssl.c: Introduce SSLContext#renegotiation_cb and
|
||||||
|
remove SSLContext#disable_client_renegotiation and related
|
||||||
|
functionality introduced in r35797. The new callback approach
|
||||||
|
gives clients maximum flexibility to decide on their own what to
|
||||||
|
do on renegotiation attempts.
|
||||||
|
Add documentation for SSL module and SSLError.
|
||||||
|
* test/openssl/test_ssl.rb: Add a test for
|
||||||
|
SSLContext#renegotiation_cb.
|
||||||
|
|
||||||
Sun Jun 10 01:37:18 2012 Tanaka Akira <akr@fsij.org>
|
Sun Jun 10 01:37:18 2012 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
* process.c (rb_fork_internal): initialize exc.
|
* process.c (rb_fork_internal): initialize exc.
|
||||||
|
|
|
@ -44,7 +44,6 @@ VALUE cSSLSocket;
|
||||||
#define ossl_sslctx_set_client_cert_cb(o,v) rb_iv_set((o),"@client_cert_cb",(v))
|
#define ossl_sslctx_set_client_cert_cb(o,v) rb_iv_set((o),"@client_cert_cb",(v))
|
||||||
#define ossl_sslctx_set_tmp_dh_cb(o,v) rb_iv_set((o),"@tmp_dh_callback",(v))
|
#define ossl_sslctx_set_tmp_dh_cb(o,v) rb_iv_set((o),"@tmp_dh_callback",(v))
|
||||||
#define ossl_sslctx_set_sess_id_ctx(o, v) rb_iv_set((o),"@session_id_context",(v))
|
#define ossl_sslctx_set_sess_id_ctx(o, v) rb_iv_set((o),"@session_id_context",(v))
|
||||||
#define ossl_sslctx_set_max_handshake(o, v) rb_iv_set((o),"@max_handshake",(v))
|
|
||||||
|
|
||||||
#define ossl_sslctx_get_cert(o) rb_iv_get((o),"@cert")
|
#define ossl_sslctx_get_cert(o) rb_iv_get((o),"@cert")
|
||||||
#define ossl_sslctx_get_key(o) rb_iv_get((o),"@key")
|
#define ossl_sslctx_get_key(o) rb_iv_get((o),"@key")
|
||||||
|
@ -61,11 +60,10 @@ VALUE cSSLSocket;
|
||||||
#define ossl_sslctx_get_client_cert_cb(o) rb_iv_get((o),"@client_cert_cb")
|
#define ossl_sslctx_get_client_cert_cb(o) rb_iv_get((o),"@client_cert_cb")
|
||||||
#define ossl_sslctx_get_tmp_dh_cb(o) rb_iv_get((o),"@tmp_dh_callback")
|
#define ossl_sslctx_get_tmp_dh_cb(o) rb_iv_get((o),"@tmp_dh_callback")
|
||||||
#define ossl_sslctx_get_sess_id_ctx(o) rb_iv_get((o),"@session_id_context")
|
#define ossl_sslctx_get_sess_id_ctx(o) rb_iv_get((o),"@session_id_context")
|
||||||
#define ossl_sslctx_get_max_handshake(o) rb_iv_get((o),"@max_handshake")
|
|
||||||
|
|
||||||
static const char *ossl_sslctx_attrs[] = {
|
static const char *ossl_sslctx_attrs[] = {
|
||||||
"cert", "key", "client_ca", "ca_file", "ca_path",
|
"cert", "key", "client_ca", "ca_file", "ca_path",
|
||||||
"timeout", "verify_mode", "verify_depth", "max_handshake",
|
"timeout", "verify_mode", "verify_depth", "renegotiation_cb",
|
||||||
"verify_callback", "options", "cert_store", "extra_chain_cert",
|
"verify_callback", "options", "cert_store", "extra_chain_cert",
|
||||||
"client_cert_cb", "tmp_dh_callback", "session_id_context",
|
"client_cert_cb", "tmp_dh_callback", "session_id_context",
|
||||||
"session_get_cb", "session_new_cb", "session_remove_cb",
|
"session_get_cb", "session_new_cb", "session_remove_cb",
|
||||||
|
@ -80,7 +78,6 @@ static const char *ossl_sslctx_attrs[] = {
|
||||||
#define ossl_ssl_get_x509(o) rb_iv_get((o),"@x509")
|
#define ossl_ssl_get_x509(o) rb_iv_get((o),"@x509")
|
||||||
#define ossl_ssl_get_key(o) rb_iv_get((o),"@key")
|
#define ossl_ssl_get_key(o) rb_iv_get((o),"@key")
|
||||||
#define ossl_ssl_get_tmp_dh(o) rb_iv_get((o),"@tmp_dh")
|
#define ossl_ssl_get_tmp_dh(o) rb_iv_get((o),"@tmp_dh")
|
||||||
#define ossl_ssl_get_handshake(o) rb_iv_get((o),"@handshake")
|
|
||||||
|
|
||||||
#define ossl_ssl_set_io(o,v) rb_iv_set((o),"@io",(v))
|
#define ossl_ssl_set_io(o,v) rb_iv_set((o),"@io",(v))
|
||||||
#define ossl_ssl_set_ctx(o,v) rb_iv_set((o),"@context",(v))
|
#define ossl_ssl_set_ctx(o,v) rb_iv_set((o),"@context",(v))
|
||||||
|
@ -88,7 +85,6 @@ static const char *ossl_sslctx_attrs[] = {
|
||||||
#define ossl_ssl_set_x509(o,v) rb_iv_set((o),"@x509",(v))
|
#define ossl_ssl_set_x509(o,v) rb_iv_set((o),"@x509",(v))
|
||||||
#define ossl_ssl_set_key(o,v) rb_iv_set((o),"@key",(v))
|
#define ossl_ssl_set_key(o,v) rb_iv_set((o),"@key",(v))
|
||||||
#define ossl_ssl_set_tmp_dh(o,v) rb_iv_set((o),"@tmp_dh",(v))
|
#define ossl_ssl_set_tmp_dh(o,v) rb_iv_set((o),"@tmp_dh",(v))
|
||||||
#define ossl_ssl_set_handshake(o,v) rb_iv_set((o),"@handshake",(v))
|
|
||||||
|
|
||||||
static const char *ossl_ssl_attr_readers[] = { "io", "context", };
|
static const char *ossl_ssl_attr_readers[] = { "io", "context", };
|
||||||
static const char *ossl_ssl_attrs[] = {
|
static const char *ossl_ssl_attrs[] = {
|
||||||
|
@ -546,30 +542,34 @@ ssl_servername_cb(SSL *ssl, int *ad, void *arg)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int
|
static void
|
||||||
ssl_get_max_handshake(VALUE ssl)
|
ssl_renegotiation_cb(const SSL *ssl)
|
||||||
{
|
{
|
||||||
VALUE rb_ctx = ossl_ssl_get_ctx(ssl);
|
VALUE ssl_obj, sslctx_obj, cb;
|
||||||
VALUE max_handshake = ossl_sslctx_get_max_handshake(rb_ctx);
|
void *ptr;
|
||||||
return NIL_P(max_handshake) ? -1 : NUM2INT(max_handshake);
|
|
||||||
|
if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
|
||||||
|
ossl_raise(eSSLError, "SSL object could not be retrieved");
|
||||||
|
ssl_obj = (VALUE)ptr;
|
||||||
|
|
||||||
|
sslctx_obj = rb_iv_get(ssl_obj, "@context");
|
||||||
|
if (NIL_P(sslctx_obj)) return;
|
||||||
|
cb = rb_iv_get(sslctx_obj, "@renegotiation_cb");
|
||||||
|
if (NIL_P(cb)) return;
|
||||||
|
|
||||||
|
(void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function may serve as the entry point to support further
|
||||||
|
* callbacks. */
|
||||||
static void
|
static void
|
||||||
ssl_renegotiation_cb(const SSL *ssl, int where, int val)
|
ssl_info_cb(const SSL *ssl, int where, int val)
|
||||||
{
|
{
|
||||||
int state = SSL_state(ssl);
|
int state = SSL_state(ssl);
|
||||||
|
|
||||||
/* Count handshakes on the server */
|
|
||||||
if ((where & SSL_CB_HANDSHAKE_START) &&
|
if ((where & SSL_CB_HANDSHAKE_START) &&
|
||||||
(state & SSL_ST_ACCEPT)) {
|
(state & SSL_ST_ACCEPT)) {
|
||||||
VALUE rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
|
ssl_renegotiation_cb(ssl);
|
||||||
int max = ssl_get_max_handshake(rb_ssl);
|
|
||||||
int cur = NUM2INT(ossl_ssl_get_handshake(rb_ssl));
|
|
||||||
|
|
||||||
if (max != -1 && cur == max)
|
|
||||||
ossl_raise(eSSLError, "Client renegotations exceeded maximum");
|
|
||||||
|
|
||||||
ossl_ssl_set_handshake(rb_ssl, INT2NUM(cur + 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,51 +1009,6 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* call-seq:
|
|
||||||
* ctx.disable_client_renegotiation -> self
|
|
||||||
*
|
|
||||||
* Completely disables client-side renegotiation. Will only affect the
|
|
||||||
* behavior of a server. A server with client renegotation disabled
|
|
||||||
* will reject any client-side attempts to renegotiate the session.
|
|
||||||
*/
|
|
||||||
static VALUE
|
|
||||||
ossl_sslctx_disable_client_renegotation(VALUE self)
|
|
||||||
{
|
|
||||||
ossl_sslctx_set_max_handshake(self, INT2NUM(1));
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* call-seq:
|
|
||||||
* ctx.allow_client_renegotiation[(num_handshakes)] -> self
|
|
||||||
*
|
|
||||||
* Affects only server connections. If no argument is provided, there is no
|
|
||||||
* restriction on the number of client-side renegotiation attempts, which is
|
|
||||||
* also the default setting. If an Integer +num_handshakes+ is provided, this
|
|
||||||
* specifies the maximum number of total handshakes that are allowed before
|
|
||||||
* further attempts will be rejected. So to allow exactly one renegotiation,
|
|
||||||
* an argument of 2 would be needed (the initial handshake plus one
|
|
||||||
* renegotiation attempt). An ArgumentError will be raised for negative
|
|
||||||
* arguments or a value of 0.
|
|
||||||
*/
|
|
||||||
static VALUE
|
|
||||||
ossl_sslctx_allow_client_renegotiation(int argc, VALUE *argv, VALUE self)
|
|
||||||
{
|
|
||||||
VALUE max = Qnil;
|
|
||||||
|
|
||||||
rb_scan_args(argc, argv, "01", &max);
|
|
||||||
|
|
||||||
if (NIL_P(max)) {
|
|
||||||
ossl_sslctx_set_max_handshake(self, INT2NUM(-1));
|
|
||||||
} else {
|
|
||||||
if (NUM2INT(max) <= 0)
|
|
||||||
ossl_raise(rb_eArgError, "Maximum handshakes must be positive and non-zero");
|
|
||||||
ossl_sslctx_set_max_handshake(self, max);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSLSocket class
|
* SSLSocket class
|
||||||
*/
|
*/
|
||||||
|
@ -1120,7 +1075,6 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
|
||||||
ossl_ssl_set_io(self, io);
|
ossl_ssl_set_io(self, io);
|
||||||
ossl_ssl_set_ctx(self, ctx);
|
ossl_ssl_set_ctx(self, ctx);
|
||||||
ossl_ssl_set_sync_close(self, Qfalse);
|
ossl_ssl_set_sync_close(self, Qfalse);
|
||||||
ossl_ssl_set_handshake(self, INT2NUM(0));
|
|
||||||
ossl_sslctx_setup(ctx);
|
ossl_sslctx_setup(ctx);
|
||||||
|
|
||||||
rb_iv_set(self, "@hostname", Qnil);
|
rb_iv_set(self, "@hostname", Qnil);
|
||||||
|
@ -1171,7 +1125,7 @@ ossl_ssl_setup(VALUE self)
|
||||||
SSL_set_ex_data(ssl, ossl_ssl_ex_client_cert_cb_idx, (void*)cb);
|
SSL_set_ex_data(ssl, ossl_ssl_ex_client_cert_cb_idx, (void*)cb);
|
||||||
cb = ossl_sslctx_get_tmp_dh_cb(v_ctx);
|
cb = ossl_sslctx_get_tmp_dh_cb(v_ctx);
|
||||||
SSL_set_ex_data(ssl, ossl_ssl_ex_tmp_dh_callback_idx, (void*)cb);
|
SSL_set_ex_data(ssl, ossl_ssl_ex_tmp_dh_callback_idx, (void*)cb);
|
||||||
SSL_set_info_callback(ssl, ssl_renegotiation_cb);
|
SSL_set_info_callback(ssl, ssl_info_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
|
@ -1491,7 +1445,6 @@ ossl_ssl_close(VALUE self)
|
||||||
{
|
{
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ossl_ssl_set_handshake(self, INT2NUM(0));
|
|
||||||
Data_Get_Struct(self, SSL, ssl);
|
Data_Get_Struct(self, SSL, ssl);
|
||||||
ossl_ssl_shutdown(ssl);
|
ossl_ssl_shutdown(ssl);
|
||||||
if (RTEST(ossl_ssl_get_sync_close(self)))
|
if (RTEST(ossl_ssl_get_sync_close(self)))
|
||||||
|
@ -1805,7 +1758,18 @@ Init_ossl_ssl()
|
||||||
ossl_ssl_ex_tmp_dh_callback_idx =
|
ossl_ssl_ex_tmp_dh_callback_idx =
|
||||||
SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_tmp_dh_callback_idx",0,0,0);
|
SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_tmp_dh_callback_idx",0,0,0);
|
||||||
|
|
||||||
|
/* Document-module: OpenSSL::SSL
|
||||||
|
*
|
||||||
|
* Use SSLContext to set up the parameters for a TLS (former SSL)
|
||||||
|
* connection. Both client and server TLS connections are supported,
|
||||||
|
* SSLSocket and SSLServer may be used in conjunction with an instance
|
||||||
|
* of SSLContext to set up connections.
|
||||||
|
*/
|
||||||
mSSL = rb_define_module_under(mOSSL, "SSL");
|
mSSL = rb_define_module_under(mOSSL, "SSL");
|
||||||
|
/* Document-class: OpenSSL::SSL::SSLError
|
||||||
|
*
|
||||||
|
* Generic error class raised by SSLSocket and SSLContext.
|
||||||
|
*/
|
||||||
eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
|
eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
|
||||||
|
|
||||||
Init_ossl_ssl_session();
|
Init_ossl_ssl_session();
|
||||||
|
@ -1964,6 +1928,28 @@ Init_ossl_ssl()
|
||||||
*/
|
*/
|
||||||
rb_attr(cSSLContext, rb_intern("servername_cb"), 1, 1, Qfalse);
|
rb_attr(cSSLContext, rb_intern("servername_cb"), 1, 1, Qfalse);
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
* A callback invoked whenever a new handshake is initiated. May be used
|
||||||
|
* to disable renegotiation entirely.
|
||||||
|
*
|
||||||
|
* The callback is invoked with the active SSLSocket. The callback's
|
||||||
|
* return value is irrelevant, normal return indicates "approval" of the
|
||||||
|
* renegotiation and will continue the process. To forbid renegotiation
|
||||||
|
* and to cancel the process, an Error may be raised within the callback.
|
||||||
|
*
|
||||||
|
* === Disable client renegotiation
|
||||||
|
*
|
||||||
|
* When running a server, it is often desirable to disable client
|
||||||
|
* renegotiation entirely. You may use a callback as follows to implement
|
||||||
|
* this feature:
|
||||||
|
*
|
||||||
|
* num_handshakes = 0
|
||||||
|
* ctx.renegotiation_cb = lambda do |ssl|
|
||||||
|
* num_handshakes += 1
|
||||||
|
* raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
rb_attr(cSSLContext, rb_intern("renegotiation_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=");
|
||||||
|
@ -1971,8 +1957,6 @@ Init_ossl_ssl()
|
||||||
rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1);
|
rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1);
|
||||||
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
|
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
|
||||||
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
|
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
|
||||||
rb_define_method(cSSLContext, "disable_client_renegotiation", ossl_sslctx_disable_client_renegotation, 0);
|
|
||||||
rb_define_method(cSSLContext, "allow_client_renegotiation", ossl_sslctx_allow_client_renegotiation, -1);
|
|
||||||
|
|
||||||
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
|
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
|
||||||
|
|
||||||
|
|
|
@ -505,35 +505,13 @@ if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_disable_client_renegotiation
|
def test_renegotiation_cb
|
||||||
ctx_proc = Proc.new { |ctx| ctx.disable_client_renegotiation }
|
num_handshakes = 0
|
||||||
|
renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 }
|
||||||
|
ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb }
|
||||||
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
||||||
server_connect(port) { |ssl|
|
server_connect(port) { |ssl|
|
||||||
assert(ssl.ssl_version)
|
assert_equal(1, num_handshakes)
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_allow_client_renegotiation_args
|
|
||||||
ctx = OpenSSL::SSL::SSLContext.new
|
|
||||||
assert_raise(ArgumentError) { ctx.allow_client_renegotiation(0) }
|
|
||||||
assert_raise(ArgumentError) { ctx.allow_client_renegotiation(-1) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_allow_client_renegotiation_once
|
|
||||||
ctx_proc = Proc.new { |ctx| ctx.allow_client_renegotiation(2) }
|
|
||||||
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
|
||||||
server_connect(port) { |ssl|
|
|
||||||
assert(ssl.ssl_version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_allow_arbitrary_client_renegotiation
|
|
||||||
ctx_proc = Proc.new { |ctx| ctx.allow_client_renegotiation }
|
|
||||||
start_server_version(:SSLv23, ctx_proc) { |server, port|
|
|
||||||
server_connect(port) { |ssl|
|
|
||||||
assert(ssl.ssl_version)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue