From 1dcd4b325ee9074952461d1748d881ea27da05d5 Mon Sep 17 00:00:00 2001 From: emboss Date: Thu, 30 Jun 2011 14:48:52 +0000 Subject: [PATCH] * ext/openssl/ossl.c/.h: Added ossl_x509_name_sk2ary. * ext/openssl/ossl.c: Replaced ossl_x509_ary2k by generic macro to simplify future conversions. * ext/openssl/ossl_ssl.c: Implement SSLSocket#client_ca. * test/openssl/test_ssl.rb: Add test for SSLSocket#client_ca. Thanks to Ippei Obayashi for providing the patch! [ Ruby 1.9 - Feature #4481 ] [ruby-core:35461] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32337 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 10 +++++ ext/openssl/ossl.c | 88 +++++++++++++++++++++------------------- ext/openssl/ossl.h | 1 + ext/openssl/ossl_ssl.c | 28 +++++++++++++ test/openssl/test_ssl.rb | 22 ++++++++++ 5 files changed, 108 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index dead81a448..3fe7e82517 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Thu Jun 30 23:43:30 2011 Martin Bosslet + + * ext/openssl/ossl.c/.h: Added ossl_x509_name_sk2ary. + * ext/openssl/ossl.c: Replaced ossl_x509_ary2k by generic macro to + simplify future conversions. + * ext/openssl/ossl_ssl.c: Implement SSLSocket#client_ca. + * test/openssl/test_ssl.rb: Add test for SSLSocket#client_ca. + Thanks to Ippei Obayashi for providing the patch! + [ Ruby 1.9 - Feature #4481 ] [ruby-core:35461] + Thu Jun 30 22:38:58 2011 Koichi Sasada * benchmark/bm_vm2_defined_method.rb: added to measure performance of diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 1abedf1237..c5321b60ae 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -47,48 +47,53 @@ string2hex(const unsigned char *buf, int buf_len, char **hexbuf, int *hexbuf_len /* * Data Conversion */ -STACK_OF(X509) * -ossl_x509_ary2sk0(VALUE ary) -{ - STACK_OF(X509) *sk; - VALUE val; - X509 *x509; - int i; - - Check_Type(ary, T_ARRAY); - sk = sk_X509_new_null(); - if (!sk) ossl_raise(eOSSLError, NULL); - - for (i = 0; i < RARRAY_LEN(ary); i++) { - val = rb_ary_entry(ary, i); - if (!rb_obj_is_kind_of(val, cX509Cert)) { - sk_X509_pop_free(sk, X509_free); - ossl_raise(eOSSLError, "object not X509 cert in array"); - } - x509 = DupX509CertPtr(val); /* NEED TO DUP */ - sk_X509_push(sk, x509); - } - return sk; -} - -STACK_OF(X509) * -ossl_protect_x509_ary2sk(VALUE ary, int *status) -{ - return (STACK_OF(X509)*)rb_protect((VALUE(*)_((VALUE)))ossl_x509_ary2sk0, - ary, status); -} - -STACK_OF(X509) * -ossl_x509_ary2sk(VALUE ary) -{ - STACK_OF(X509) *sk; - int status = 0; - - sk = ossl_protect_x509_ary2sk(ary, &status); - if(status) rb_jump_tag(status); - - return sk; +#define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ +STACK_OF(type) * \ +ossl_##name##_ary2sk0(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + VALUE val; \ + type *x; \ + int i; \ + \ + Check_Type(ary, T_ARRAY); \ + sk = sk_##type##_new_null(); \ + if (!sk) ossl_raise(eOSSLError, NULL); \ + \ + for (i = 0; i < RARRAY_LEN(ary); i++) { \ + val = rb_ary_entry(ary, i); \ + if (!rb_obj_is_kind_of(val, expected_class)) { \ + sk_##type##_pop_free(sk, type##_free); \ + ossl_raise(eOSSLError, "object in array not" \ + " of class ##type##"); \ + } \ + x = dup(val); /* NEED TO DUP */ \ + sk_##type##_push(sk, x); \ + } \ + return sk; \ +} \ + \ +STACK_OF(type) * \ +ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ +{ \ + return (STACK_OF(type)*)rb_protect( \ + (VALUE(*)_((VALUE)))ossl_##name##_ary2sk0, \ + ary, \ + status); \ +} \ + \ +STACK_OF(type) * \ +ossl_##name##_ary2sk(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + int status = 0; \ + \ + sk = ossl_protect_##name##_ary2sk(ary, &status); \ + if (status) rb_jump_tag(status); \ + \ + return sk; \ } +OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) #define OSSL_IMPL_SK2ARY(name, type) \ VALUE \ @@ -117,6 +122,7 @@ ossl_##name##_sk2ary(STACK_OF(type) *sk) \ } OSSL_IMPL_SK2ARY(x509, X509) OSSL_IMPL_SK2ARY(x509crl, X509_CRL) +OSSL_IMPL_SK2ARY(x509name, X509_NAME) static VALUE ossl_str_new(int size) diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index b112aebf10..c97dc5ac51 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -126,6 +126,7 @@ STACK_OF(X509) *ossl_x509_ary2sk(VALUE); STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*); VALUE ossl_x509_sk2ary(STACK_OF(X509) *certs); VALUE ossl_x509crl_sk2ary(STACK_OF(X509_CRL) *crl); +VALUE ossl_x509name_sk2ary(STACK_OF(X509_NAME) *names); VALUE ossl_buf2str(char *buf, int len); #define ossl_str_adjust(str, p) \ do{\ diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 6fa48bac41..c18435e35e 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1643,6 +1643,33 @@ ossl_ssl_get_verify_result(VALUE self) return INT2FIX(SSL_get_verify_result(ssl)); } +/* + * call-seq: + * ssl.client_ca => [x509name, ...] + * + * Returns the list of client CAs. Please note that in contrast to + * SSLContext#client_ca= no array of X509::Certificate is returned but + * X509::Name instances of the CA's subject distinguished name. + * + * In server mode, returns the list set by SSLContext#client_ca=. + * In client mode, returns the list of client CAs sent from the server. + */ +static VALUE +ossl_ssl_get_client_ca_list(VALUE self) +{ + SSL *ssl; + STACK_OF(X509_NAME) *ca; + + Data_Get_Struct(self, SSL, ssl); + if (!ssl) { + rb_warning("SSL session is not started yet."); + return Qnil; + } + + ca = SSL_get_client_CA_list(ssl); + return ossl_x509name_sk2ary(ca); +} + void Init_ossl_ssl() { @@ -1930,6 +1957,7 @@ Init_ossl_ssl() rb_define_method(cSSLSocket, "session_reused?", ossl_ssl_session_reused, 0); 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, "client_ca", ossl_ssl_get_client_ca_list, 0); #define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, INT2NUM(SSL_##x)) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 77dcff8d44..8c3d00e5e5 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -135,6 +135,28 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase } end + def test_client_ca + ctx_proc = Proc.new do |ctx| + ctx.client_ca = [@ca_cert] + end + + vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + start_server(PORT, vflag, true, :ctx_proc => ctx_proc){|server, port| + ctx = OpenSSL::SSL::SSLContext.new + client_ca_from_server = nil + ctx.client_cert_cb = Proc.new do |sslconn| + client_ca_from_server = sslconn.client_ca + [@cli_cert, @cli_key] + end + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + ssl.connect + assert_equal([@ca], client_ca_from_server) + ssl.close + } + end + def test_starttls start_server(PORT, OpenSSL::SSL::VERIFY_NONE, false){|server, port| sock = TCPSocket.new("127.0.0.1", port)