summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--ext/openssl/ossl_ssl.c124
-rw-r--r--test/openssl/test_ssl.rb34
3 files changed, 71 insertions, 98 deletions
diff --git a/ChangeLog b/ChangeLog
index d0af902528..f721db74d6 100644
--- a/ChangeLog
+++ b/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>
* process.c (rb_fork_internal): initialize exc.
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 0eaa3c8f4b..9233488b50 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -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_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_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_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_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_max_handshake(o) rb_iv_get((o),"@max_handshake")
static const char *ossl_sslctx_attrs[] = {
"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",
"client_cert_cb", "tmp_dh_callback", "session_id_context",
"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_key(o) rb_iv_get((o),"@key")
#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_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_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_handshake(o,v) rb_iv_set((o),"@handshake",(v))
static const char *ossl_ssl_attr_readers[] = { "io", "context", };
static const char *ossl_ssl_attrs[] = {
@@ -546,30 +542,34 @@ ssl_servername_cb(SSL *ssl, int *ad, void *arg)
}
#endif
-static int
-ssl_get_max_handshake(VALUE ssl)
+static void
+ssl_renegotiation_cb(const SSL *ssl)
{
- VALUE rb_ctx = ossl_ssl_get_ctx(ssl);
- VALUE max_handshake = ossl_sslctx_get_max_handshake(rb_ctx);
- return NIL_P(max_handshake) ? -1 : NUM2INT(max_handshake);
+ VALUE ssl_obj, sslctx_obj, cb;
+ void *ptr;
+
+ 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
-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);
- /* Count handshakes on the server */
if ((where & SSL_CB_HANDSHAKE_START) &&
(state & SSL_ST_ACCEPT)) {
- VALUE rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
- 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));
+ ssl_renegotiation_cb(ssl);
}
}
@@ -1010,51 +1010,6 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE 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
*/
static void
@@ -1120,7 +1075,6 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
ossl_ssl_set_io(self, io);
ossl_ssl_set_ctx(self, ctx);
ossl_ssl_set_sync_close(self, Qfalse);
- ossl_ssl_set_handshake(self, INT2NUM(0));
ossl_sslctx_setup(ctx);
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);
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_info_callback(ssl, ssl_renegotiation_cb);
+ SSL_set_info_callback(ssl, ssl_info_cb);
}
return Qtrue;
@@ -1491,7 +1445,6 @@ ossl_ssl_close(VALUE self)
{
SSL *ssl;
- ossl_ssl_set_handshake(self, INT2NUM(0));
Data_Get_Struct(self, SSL, ssl);
ossl_ssl_shutdown(ssl);
if (RTEST(ossl_ssl_get_sync_close(self)))
@@ -1805,7 +1758,18 @@ Init_ossl_ssl()
ossl_ssl_ex_tmp_dh_callback_idx =
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");
+ /* Document-class: OpenSSL::SSL::SSLError
+ *
+ * Generic error class raised by SSLSocket and SSLContext.
+ */
eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
Init_ossl_ssl_session();
@@ -1964,6 +1928,28 @@ Init_ossl_ssl()
*/
rb_attr(cSSLContext, rb_intern("servername_cb"), 1, 1, Qfalse);
#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=");
@@ -1971,8 +1957,6 @@ Init_ossl_ssl()
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_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);
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 97b2c22472..97a3a46169 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -505,39 +505,17 @@ if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
end
- def test_disable_client_renegotiation
- ctx_proc = Proc.new { |ctx| ctx.disable_client_renegotiation }
+ def test_renegotiation_cb
+ 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|
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
-
+
private
def start_server_version(version, ctx_proc=nil, server_proc=nil, &blk)