mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* ext/openssl/ossl_ssl.c: Server Name Indication support.
new methods SSLContext#server_name_cb=, SSLSocket#hostname=. * test/openssl/test_ssl.rb: Tests for above. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21761 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									b9170351da
								
							
						
					
					
						commit
						7361a2ecb3
					
				
					 4 changed files with 151 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,10 @@
 | 
			
		|||
Sun Jan 25 06:44:58 2009  Technorama Ltd.  <oss-ruby@technorama.net>
 | 
			
		||||
 | 
			
		||||
	* ext/openssl/ossl_ssl.c: Server Name Indication support.
 | 
			
		||||
	  new methods SSLContext#server_name_cb=, SSLSocket#hostname=.
 | 
			
		||||
 | 
			
		||||
	* test/openssl/test_ssl.rb: Tests for above.
 | 
			
		||||
 | 
			
		||||
Sat Jan 24 08:22:35 2009  Nobuyoshi Nakada  <nobu@ruby-lang.org>
 | 
			
		||||
 | 
			
		||||
	* lib/mkmf.rb (configuration): tools under the top source
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,6 +96,9 @@ have_func("X509_STORE_set_ex_data")
 | 
			
		|||
have_func("OBJ_NAME_do_all_sorted")
 | 
			
		||||
have_func("SSL_SESSION_get_id")
 | 
			
		||||
have_func("OPENSSL_cleanse")
 | 
			
		||||
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")
 | 
			
		||||
end
 | 
			
		||||
if try_compile("#define FOO(a, ...) foo(a, ##__VA_ARGS__)\n int x(){FOO(1);FOO(1,2);FOO(1,2,3);}\n")
 | 
			
		||||
  $defs.push("-DHAVE_VA_ARGS_MACRO")
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,9 @@ static const char *ossl_sslctx_attrs[] = {
 | 
			
		|||
    "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",
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
    "servername_cb",
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define ossl_ssl_get_io(o)           rb_iv_get((o),"@io")
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +87,12 @@ static const char *ossl_sslctx_attrs[] = {
 | 
			
		|||
#define ossl_ssl_set_tmp_dh(o,v)     rb_iv_set((o),"@tmp_dh",(v))
 | 
			
		||||
 | 
			
		||||
static const char *ossl_ssl_attr_readers[] = { "io", "context", };
 | 
			
		||||
static const char *ossl_ssl_attrs[] = { "sync_close", };
 | 
			
		||||
static const char *ossl_ssl_attrs[] = {
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
    "hostname",
 | 
			
		||||
#endif
 | 
			
		||||
    "sync_close", 
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ID ID_callback_state;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -446,6 +454,66 @@ ossl_sslctx_add_extra_chain_cert_i(VALUE i, VALUE arg)
 | 
			
		|||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE ossl_sslctx_setup(VALUE self);
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
static VALUE
 | 
			
		||||
ossl_call_servername_cb(VALUE ary)
 | 
			
		||||
{
 | 
			
		||||
    VALUE ssl_obj, sslctx_obj, cb, ret_obj;
 | 
			
		||||
 | 
			
		||||
    Check_Type(ary, T_ARRAY);
 | 
			
		||||
    ssl_obj = rb_ary_entry(ary, 0);
 | 
			
		||||
 | 
			
		||||
    sslctx_obj = rb_iv_get(ssl_obj, "@context");
 | 
			
		||||
    if (NIL_P(sslctx_obj)) return Qnil;
 | 
			
		||||
    cb = rb_iv_get(sslctx_obj, "@servername_cb");
 | 
			
		||||
    if (NIL_P(cb)) return Qnil;
 | 
			
		||||
 | 
			
		||||
    ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary);
 | 
			
		||||
    if (rb_obj_is_kind_of(ret_obj, cSSLContext)) {
 | 
			
		||||
        SSL *ssl;
 | 
			
		||||
        SSL_CTX *ctx2;
 | 
			
		||||
 | 
			
		||||
        ossl_sslctx_setup(ret_obj);
 | 
			
		||||
        Data_Get_Struct(ssl_obj, SSL, ssl);
 | 
			
		||||
        Data_Get_Struct(ret_obj, SSL_CTX, ctx2);
 | 
			
		||||
        SSL_set_SSL_CTX(ssl, ctx2);
 | 
			
		||||
    } else if (!NIL_P(ret_obj)) {
 | 
			
		||||
            rb_raise(rb_eArgError, "servername_cb must return an OpenSSL::SSL::SSLContext object or nil");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret_obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
ssl_servername_cb(SSL *ssl, int *ad, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    VALUE ary, ssl_obj, ret_obj;
 | 
			
		||||
    void *ptr;
 | 
			
		||||
    int state = 0;
 | 
			
		||||
    const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
 | 
			
		||||
 | 
			
		||||
    if (!servername)
 | 
			
		||||
        return SSL_TLSEXT_ERR_OK;
 | 
			
		||||
 | 
			
		||||
    if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
 | 
			
		||||
    	return SSL_TLSEXT_ERR_ALERT_FATAL;
 | 
			
		||||
    ssl_obj = (VALUE)ptr;
 | 
			
		||||
    ary = rb_ary_new2(2);
 | 
			
		||||
    rb_ary_push(ary, ssl_obj);
 | 
			
		||||
    rb_ary_push(ary, rb_str_new2(servername));
 | 
			
		||||
 | 
			
		||||
    ret_obj = rb_protect((VALUE(*)_((VALUE)))ossl_call_servername_cb, ary, &state);
 | 
			
		||||
    if (state) {
 | 
			
		||||
        rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
 | 
			
		||||
        return SSL_TLSEXT_ERR_ALERT_FATAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return SSL_TLSEXT_ERR_OK;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * call-seq:
 | 
			
		||||
 *    ctx.setup => Qtrue # first time
 | 
			
		||||
| 
						 | 
				
			
			@ -581,6 +649,15 @@ ossl_sslctx_setup(VALUE self)
 | 
			
		|||
	SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
 | 
			
		||||
	OSSL_Debug("SSL SESSION remove callback added");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
    val = rb_iv_get(self, "@servername_cb");
 | 
			
		||||
    if (!NIL_P(val)) {
 | 
			
		||||
        SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
 | 
			
		||||
	OSSL_Debug("SSL TLSEXT servername callback added");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return Qtrue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -901,6 +978,10 @@ ossl_ssl_setup(VALUE self)
 | 
			
		|||
 | 
			
		||||
    Data_Get_Struct(self, SSL, ssl);
 | 
			
		||||
    if(!ssl){
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
	VALUE hostname = rb_iv_get(self, "@hostname");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        v_ctx = ossl_ssl_get_ctx(self);
 | 
			
		||||
        Data_Get_Struct(v_ctx, SSL_CTX, ctx);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -910,6 +991,12 @@ ossl_ssl_setup(VALUE self)
 | 
			
		|||
        }
 | 
			
		||||
        DATA_PTR(self) = ssl;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
 | 
			
		||||
        if (!NIL_P(hostname)) {
 | 
			
		||||
           if (SSL_set_tlsext_host_name(ssl, StringValuePtr(hostname)) != 1)
 | 
			
		||||
               ossl_raise(eSSLError, "SSL_set_tlsext_host_name:");
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        io = ossl_ssl_get_io(self);
 | 
			
		||||
        GetOpenFile(io, fptr);
 | 
			
		||||
        rb_io_check_readable(fptr);
 | 
			
		||||
| 
						 | 
				
			
			@ -946,7 +1033,15 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname)
 | 
			
		|||
    Data_Get_Struct(self, SSL, ssl);
 | 
			
		||||
    GetOpenFile(ossl_ssl_get_io(self), fptr);
 | 
			
		||||
    for(;;){
 | 
			
		||||
	if((ret = func(ssl)) > 0) break;
 | 
			
		||||
	ret = func(ssl);
 | 
			
		||||
 | 
			
		||||
        cb_state = rb_ivar_get(self, ID_callback_state);
 | 
			
		||||
        if (!NIL_P(cb_state))
 | 
			
		||||
            rb_jump_tag(NUM2INT(cb_state));
 | 
			
		||||
 | 
			
		||||
	if (ret > 0)
 | 
			
		||||
	    break;
 | 
			
		||||
 | 
			
		||||
	switch((ret2 = ssl_get_error(ssl, ret))){
 | 
			
		||||
	case SSL_ERROR_WANT_WRITE:
 | 
			
		||||
            rb_io_wait_writable(FPTR_TO_FD(fptr));
 | 
			
		||||
| 
						 | 
				
			
			@ -962,10 +1057,6 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname)
 | 
			
		|||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cb_state = rb_ivar_get(self, ID_callback_state);
 | 
			
		||||
    if (!NIL_P(cb_state))
 | 
			
		||||
        rb_jump_tag(NUM2INT(cb_state));
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -570,6 +570,50 @@ class OpenSSL::TestSSL < Test::Unit::TestCase
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_tlsext_hostname
 | 
			
		||||
    return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname)
 | 
			
		||||
 | 
			
		||||
    ctx_proc = Proc.new do |ctx, ssl|
 | 
			
		||||
      foo_ctx = ctx.dup
 | 
			
		||||
 | 
			
		||||
      ctx.servername_cb = Proc.new do |ssl, hostname|
 | 
			
		||||
        case hostname
 | 
			
		||||
        when 'foo.example.com'
 | 
			
		||||
          foo_ctx
 | 
			
		||||
        when 'bar.example.com'
 | 
			
		||||
          nil
 | 
			
		||||
        else
 | 
			
		||||
          raise "unknown hostname #{hostname.inspect}"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    server_proc = Proc.new do |ctx, ssl|
 | 
			
		||||
      readwrite_loop(ctx, ssl)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
 | 
			
		||||
      2.times do |i|
 | 
			
		||||
        sock = TCPSocket.new("127.0.0.1", port)
 | 
			
		||||
        ctx = OpenSSL::SSL::SSLContext.new
 | 
			
		||||
        if defined?(OpenSSL::SSL::OP_NO_TICKET)
 | 
			
		||||
          # disable RFC4507 support
 | 
			
		||||
          ctx.options = OpenSSL::SSL::OP_NO_TICKET
 | 
			
		||||
        end
 | 
			
		||||
        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 | 
			
		||||
        ssl.sync_close = true
 | 
			
		||||
        ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com'
 | 
			
		||||
        ssl.connect
 | 
			
		||||
 | 
			
		||||
        str = "x" * 100 + "\n"
 | 
			
		||||
        ssl.puts(str)
 | 
			
		||||
        assert_equal(str, ssl.gets)
 | 
			
		||||
 | 
			
		||||
        ssl.close
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue