1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/ext/openssl/ossl_ssl.c

2751 lines
78 KiB
C
Raw Normal View History

/*
* 'OpenSSL for Ruby' project
* Copyright (C) 2000-2002 GOTOU Yuuzou <gotoyuzo@notwork.org>
* Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
* Copyright (C) 2001-2007 Technorama Ltd. <oss-ruby@technorama.net>
* All rights reserved.
*/
/*
* This program is licensed under the same licence as Ruby.
* (See the file 'LICENCE'.)
*/
#include "ossl.h"
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
#ifdef _WIN32
# define TO_SOCKET(s) _get_osfhandle(s)
#else
# define TO_SOCKET(s) (s)
#endif
#define GetSSLCTX(obj, ctx) do { \
TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \
} while (0)
VALUE mSSL;
static VALUE mSSLExtConfig;
static VALUE eSSLError;
VALUE cSSLContext;
VALUE cSSLSocket;
static VALUE eSSLErrorWaitReadable;
static VALUE eSSLErrorWaitWritable;
static ID ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
id_npn_protocols_encoded;
static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
id_i_verify_depth, id_i_verify_callback, id_i_client_ca,
id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert,
id_i_client_cert_cb, id_i_tmp_ecdh_callback, id_i_timeout,
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
id_i_verify_hostname;
static ID id_i_io, id_i_context, id_i_hostname;
/*
* SSLContext class
*/
static const struct {
const char *name;
SSL_METHOD *(*func)(void); /* FIXME: constify when dropping 0.9.8 */
int version;
} ossl_ssl_method_tab[] = {
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
#define OSSL_SSL_METHOD_ENTRY(name, version) \
{ #name, (SSL_METHOD *(*)(void))TLS_method, version }, \
{ #name"_server", (SSL_METHOD *(*)(void))TLS_server_method, version }, \
{ #name"_client", (SSL_METHOD *(*)(void))TLS_client_method, version }
#else
#define OSSL_SSL_METHOD_ENTRY(name, version) \
{ #name, (SSL_METHOD *(*)(void))name##_method, version }, \
{ #name"_server", (SSL_METHOD *(*)(void))name##_server_method, version }, \
{ #name"_client", (SSL_METHOD *(*)(void))name##_client_method, version }
#endif
#if defined(HAVE_SSLV2_METHOD)
OSSL_SSL_METHOD_ENTRY(SSLv2, SSL2_VERSION),
#endif
#if defined(HAVE_SSLV3_METHOD)
OSSL_SSL_METHOD_ENTRY(SSLv3, SSL3_VERSION),
#endif
OSSL_SSL_METHOD_ENTRY(TLSv1, TLS1_VERSION),
#if defined(HAVE_TLSV1_1_METHOD)
OSSL_SSL_METHOD_ENTRY(TLSv1_1, TLS1_1_VERSION),
#endif
#if defined(HAVE_TLSV1_2_METHOD)
OSSL_SSL_METHOD_ENTRY(TLSv1_2, TLS1_2_VERSION),
#endif
OSSL_SSL_METHOD_ENTRY(SSLv23, 0),
#undef OSSL_SSL_METHOD_ENTRY
};
static int ossl_ssl_ex_vcb_idx;
static int ossl_ssl_ex_store_p;
static int ossl_ssl_ex_ptr_idx;
static void
ossl_sslctx_free(void *ptr)
{
SSL_CTX *ctx = ptr;
#if !defined(HAVE_X509_STORE_UP_REF)
if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1)
ctx->cert_store = NULL;
#endif
SSL_CTX_free(ctx);
}
static const rb_data_type_t ossl_sslctx_type = {
"OpenSSL/SSL/CTX",
{
0, ossl_sslctx_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
static VALUE
ossl_sslctx_s_alloc(VALUE klass)
{
SSL_CTX *ctx;
long mode = SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
VALUE obj;
#ifdef SSL_MODE_RELEASE_BUFFERS
mode |= SSL_MODE_RELEASE_BUFFERS;
#endif
obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0);
ctx = SSL_CTX_new(SSLv23_method());
if (!ctx) {
ossl_raise(eSSLError, "SSL_CTX_new");
}
SSL_CTX_set_mode(ctx, mode);
RTYPEDDATA_DATA(obj) = ctx;
SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj);
#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
/* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It
* allows to specify multiple curve names and OpenSSL will select
* automatically from them. In OpenSSL 1.0.2, the automatic selection has to
* be enabled explicitly. But OpenSSL 1.1.0 removed the knob and it is
* always enabled. To uniform the behavior, we enable the automatic
* selection also in 1.0.2. Users can still disable ECDH by removing ECDH
* cipher suites by SSLContext#ciphers=. */
if (!SSL_CTX_set_ecdh_auto(ctx, 1))
ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
#endif
return obj;
}
/*
* call-seq:
* ctx.ssl_version = :TLSv1
* ctx.ssl_version = "SSLv23_client"
*
* Sets the SSL/TLS protocol version for the context. This forces connections to
* use only the specified protocol version.
*
* You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS
*/
static VALUE
ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method)
{
SSL_CTX *ctx;
* sprintf.c (rb_str_format): allow %c to print one character string (e.g. ?x). * lib/tempfile.rb (Tempfile::make_tmpname): put dot between basename and pid. [ruby-talk:196272] * parse.y (do_block): remove -> style block. * parse.y (parser_yylex): remove tLAMBDA_ARG. * eval.c (rb_call0): binding for the return event hook should have consistent scope. [ruby-core:07928] * eval.c (proc_invoke): return behavior should depend whether it is surrounded by a lambda or a mere block. * eval.c (formal_assign): handles post splat arguments. * eval.c (rb_call0): ditto. * st.c (strhash): use FNV-1a hash. * parse.y (parser_yylex): removed experimental ';;' terminator. * eval.c (rb_node_arity): should be aware of post splat arguments. * eval.c (rb_proc_arity): ditto. * parse.y (f_args): syntax rule enhanced to support arguments after the splat. * parse.y (block_param): ditto for block parameters. * parse.y (f_post_arg): mandatory formal arguments after the splat argument. * parse.y (new_args_gen): generate nodes for mandatory formal arguments after the splat argument. * eval.c (rb_eval): dispatch mandatory formal arguments after the splat argument. * parse.y (args): allow more than one splat in the argument list. * parse.y (method_call): allow aref [] to accept all kind of method argument, including assocs, splat, and block argument. * eval.c (SETUP_ARGS0): prepare block argument as well. * lib/mathn.rb (Integer): remove Integer#gcd2. [ruby-core:07931] * eval.c (error_line): print receivers true/false/nil specially. * eval.c (rb_proc_yield): handles parameters in yield semantics. * eval.c (nil_yield): gives LocalJumpError to denote no block error. * io.c (rb_io_getc): now takes one-character string. * string.c (rb_str_hash): use FNV-1a hash from Fowler/Noll/Vo hashing algorithm. * string.c (rb_str_aref): str[0] now returns 1 character string, instead of a fixnum. [Ruby2] * parse.y (parser_yylex): ?c now returns 1 character string, instead of a fixnum. [Ruby2] * string.c (rb_str_aset): no longer support fixnum insertion. * eval.c (umethod_bind): should not update original class. [ruby-dev:28636] * eval.c (ev_const_get): should support constant access from within instance_eval(). [ruby-dev:28327] * time.c (time_timeval): should round for usec floating number. [ruby-core:07896] * time.c (time_add): ditto. * dir.c (sys_warning): should not call a vararg function rb_sys_warning() indirectly. [ruby-core:07886] * numeric.c (flo_divmod): the first element of Float#divmod should be an integer. [ruby-dev:28589] * test/ruby/test_float.rb: add tests for divmod, div, modulo and remainder. * re.c (rb_reg_initialize): should not allow modifying literal regexps. frozen check moved from rb_reg_initialize_m as well. * re.c (rb_reg_initialize): should not modify untainted objects in safe levels higher than 3. * re.c (rb_memcmp): type change from char* to const void*. * dir.c (dir_close): should not close untainted dir stream. * dir.c (GetDIR): add tainted/frozen check for each dir operation. * lib/rdoc/parsers/parse_rb.rb (RDoc::RubyParser::parse_symbol_arg): typo fixed. a patch from Florian Gross <florg at florg.net>. * eval.c (EXEC_EVENT_HOOK): trace_func may remove itself from event_hooks. no guarantee for arbitrary hook deletion. [ruby-dev:28632] * util.c (ruby_strtod): differ addition to minimize error. [ruby-dev:28619] * util.c (ruby_strtod): should not raise ERANGE when the input string does not have any digits. [ruby-dev:28629] * eval.c (proc_invoke): should restore old ruby_frame->block. thanks to ts <decoux at moulon.inra.fr>. [ruby-core:07833] also fix [ruby-dev:28614] as well. * signal.c (trap): sig should be less then NSIG. Coverity found this bug. a patch from Kevin Tew <tewk at tewk.com>. [ruby-core:07823] * math.c (math_log2): add new method inspired by [ruby-talk:191237]. * math.c (math_log): add optional base argument to Math::log(). [ruby-talk:191308] * ext/syck/emitter.c (syck_scan_scalar): avoid accessing uninitialized array element. a patch from Pat Eyler <rubypate at gmail.com>. [ruby-core:07809] * array.c (rb_ary_fill): initialize local variables first. a patch from Pat Eyler <rubypate at gmail.com>. [ruby-core:07810] * ext/syck/yaml2byte.c (syck_yaml2byte_handler): need to free type_tag. a patch from Pat Eyler <rubypate at gmail.com>. [ruby-core:07808] * ext/socket/socket.c (make_hostent_internal): accept ai_family check from Sam Roberts <sroberts at uniserve.com>. [ruby-core:07691] * util.c (ruby_strtod): should not cut off 18 digits for no reason. [ruby-core:07796] * array.c (rb_ary_fill): internalize local variable "beg" to pacify Coverity. [ruby-core:07770] * pack.c (pack_unpack): now supports CRLF newlines. a patch from <tommy at tmtm.org>. [ruby-dev:28601] * applied code clean-up patch from Stefan Huehner <stefan at huehner.org>. [ruby-core:07764] * lib/jcode.rb (String::tr_s): should have translated non squeezing character sequence (i.e. a character) as well. thanks to Hiroshi Ichikawa <gimite at gimite.ddo.jp> [ruby-list:42090] * ext/socket/socket.c: document update patch from Sam Roberts <sroberts at uniserve.com>. [ruby-core:07701] * lib/mathn.rb (Integer): need not to remove gcd2. a patch from NARUSE, Yui <naruse at airemix.com>. [ruby-dev:28570] * parse.y (arg): too much NEW_LIST() * eval.c (SETUP_ARGS0): remove unnecessary access to nd_alen. * eval.c (rb_eval): use ARGSCAT for NODE_OP_ASGN1. [ruby-dev:28585] * parse.y (arg): use NODE_ARGSCAT for placeholder. * lib/getoptlong.rb (GetoptLong::get): RDoc update patch from mathew <meta at pobox.com>. [ruby-core:07738] * variable.c (rb_const_set): raise error when no target klass is supplied. [ruby-dev:28582] * prec.c (prec_prec_f): documentation patch from <gerardo.santana at gmail.com>. [ruby-core:07689] * bignum.c (rb_big_pow): second operand may be too big even if it's a Fixnum. [ruby-talk:187984] * README.EXT: update symbol description. [ruby-talk:188104] * COPYING: explicitly note GPLv2. [ruby-talk:187922] * parse.y: remove some obsolete syntax rules (unparenthesized method calls in argument list). * eval.c (rb_call0): insecure calling should be checked for non NODE_SCOPE method invocations too. * eval.c (rb_alias): should preserve the current safe level as well as method definition. * process.c (rb_f_sleep): remove RDoc description about SIGALRM which is not valid on the current implementation. [ruby-dev:28464] Thu Mar 23 21:40:47 2006 K.Kosako <sndgk393 AT ybb.ne.jp> * eval.c (method_missing): should support argument splat in super. a bug in combination of super, splat and method_missing. [ruby-talk:185438] * configure.in: Solaris SunPro compiler -rapth patch from <kuwa at labs.fujitsu.com>. [ruby-dev:28443] * configure.in: remove enable_rpath=no for Solaris. [ruby-dev:28440] * ext/win32ole/win32ole.c (ole_val2olevariantdata): change behavior of converting OLE Variant object with VT_ARRAY|VT_UI1 and Ruby String object. * ruby.1: a clarification patch from David Lutterkort <dlutter at redhat.com>. [ruby-core:7508] * lib/rdoc/ri/ri_paths.rb (RI::Paths): adding paths from rubygems directories. a patch from Eric Hodel <drbrain at segment7.net>. [ruby-core:07423] * eval.c (rb_clear_cache_by_class): clearing wrong cache. * ext/extmk.rb: use :remove_destination to install extension libraries to avoid SEGV. [ruby-dev:28417] * eval.c (rb_thread_fd_writable): should not re-schedule output from KILLED thread (must be error printing). * array.c (rb_ary_flatten_bang): allow specifying recursion level. [ruby-talk:182170] * array.c (rb_ary_flatten): ditto. * gc.c (add_heap): a heap_slots may overflow. a patch from Stefan Weil <weil at mail.berlios.de>. * eval.c (rb_call): use separate cache for fcall/vcall invocation. * eval.c (rb_eval): NODE_FCALL, NODE_VCALL can call local functions. * eval.c (rb_mod_local): a new method to specify newly added visibility "local". * eval.c (search_method): search for local methods which are visible only from the current class. * class.c (rb_class_local_methods): a method to list local methods. * object.c (Init_Object): add BasicObject class as a top level BlankSlate class. * ruby.h (SYM2ID): should not cast to signed long. [ruby-core:07414] * class.c (rb_include_module): allow module duplication. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10235 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2006-06-09 17:20:17 -04:00
const char *s;
VALUE m = ssl_method;
int i;
GetSSLCTX(self, ctx);
if (RB_TYPE_P(ssl_method, T_SYMBOL))
m = rb_sym2str(ssl_method);
s = StringValueCStr(m);
for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) {
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
int version = ossl_ssl_method_tab[i].version;
#endif
SSL_METHOD *method = ossl_ssl_method_tab[i].func();
if (SSL_CTX_set_ssl_version(ctx, method) != 1)
ossl_raise(eSSLError, "SSL_CTX_set_ssl_version");
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
if (!SSL_CTX_set_min_proto_version(ctx, version))
ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
if (!SSL_CTX_set_max_proto_version(ctx, version))
ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
#endif
return ssl_method;
}
}
ossl_raise(rb_eArgError, "unknown SSL method `%"PRIsVALUE"'.", m);
}
static VALUE
ossl_call_client_cert_cb(VALUE obj)
{
VALUE ctx_obj, cb, ary, cert, key;
ctx_obj = rb_attr_get(obj, id_i_context);
cb = rb_attr_get(ctx_obj, id_i_client_cert_cb);
if (NIL_P(cb))
return Qnil;
ary = rb_funcall(cb, rb_intern("call"), 1, obj);
Check_Type(ary, T_ARRAY);
GetX509CertPtr(cert = rb_ary_entry(ary, 0));
GetPrivPKeyPtr(key = rb_ary_entry(ary, 1));
return rb_ary_new3(2, cert, key);
}
static int
ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
{
VALUE obj, ret;
obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
ret = rb_protect(ossl_call_client_cert_cb, obj, NULL);
if (NIL_P(ret))
return 0;
*x509 = DupX509CertPtr(RARRAY_AREF(ret, 0));
*pkey = DupPKeyPtr(RARRAY_AREF(ret, 1));
return 1;
}
#if !defined(OPENSSL_NO_DH) || \
!defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
struct tmp_dh_callback_args {
VALUE ssl_obj;
ID id;
int type;
int is_export;
int keylength;
};
static EVP_PKEY *
ossl_call_tmp_dh_callback(struct tmp_dh_callback_args *args)
{
VALUE cb, dh;
EVP_PKEY *pkey;
cb = rb_funcall(args->ssl_obj, args->id, 0);
if (NIL_P(cb))
return NULL;
dh = rb_funcall(cb, rb_intern("call"), 3,
args->ssl_obj, INT2NUM(args->is_export), INT2NUM(args->keylength));
pkey = GetPKeyPtr(dh);
if (EVP_PKEY_base_id(pkey) != args->type)
return NULL;
return pkey;
}
#endif
#if !defined(OPENSSL_NO_DH)
static DH *
ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
{
VALUE rb_ssl;
EVP_PKEY *pkey;
struct tmp_dh_callback_args args;
int state;
rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
args.ssl_obj = rb_ssl;
args.id = id_tmp_dh_callback;
args.is_export = is_export;
args.keylength = keylength;
args.type = EVP_PKEY_DH;
pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
(VALUE)&args, &state);
if (state) {
rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
return NULL;
}
if (!pkey)
return NULL;
return EVP_PKEY_get0_DH(pkey);
}
#endif /* OPENSSL_NO_DH */
#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
static EC_KEY *
ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength)
{
VALUE rb_ssl;
EVP_PKEY *pkey;
struct tmp_dh_callback_args args;
int state;
rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
args.ssl_obj = rb_ssl;
args.id = id_tmp_ecdh_callback;
args.is_export = is_export;
args.keylength = keylength;
args.type = EVP_PKEY_EC;
pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
(VALUE)&args, &state);
if (state) {
rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
return NULL;
}
if (!pkey)
return NULL;
return EVP_PKEY_get0_EC_KEY(pkey);
}
#endif
static VALUE
call_verify_certificate_identity(VALUE ctx_v)
{
X509_STORE_CTX *ctx = (X509_STORE_CTX *)ctx_v;
SSL *ssl;
VALUE ssl_obj, hostname, cert_obj;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
hostname = rb_attr_get(ssl_obj, id_i_hostname);
if (!RTEST(hostname)) {
rb_warning("verify_hostname requires hostname to be set");
return Qtrue;
}
cert_obj = ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx));
return rb_funcall(mSSL, rb_intern("verify_certificate_identity"), 2,
cert_obj, hostname);
}
static int
ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
VALUE cb, ssl_obj, sslctx_obj, verify_hostname, ret;
SSL *ssl;
int status;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx);
ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
sslctx_obj = rb_attr_get(ssl_obj, id_i_context);
verify_hostname = rb_attr_get(sslctx_obj, id_i_verify_hostname);
if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) &&
!X509_STORE_CTX_get_error_depth(ctx)) {
ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status);
if (status) {
rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status));
return 0;
}
preverify_ok = ret == Qtrue;
}
return ossl_verify_cb_call(cb, preverify_ok, ctx);
}
static VALUE
ossl_call_session_get_cb(VALUE ary)
{
VALUE ssl_obj, cb;
Check_Type(ary, T_ARRAY);
ssl_obj = rb_ary_entry(ary, 0);
cb = rb_funcall(ssl_obj, rb_intern("session_get_cb"), 0);
if (NIL_P(cb)) return Qnil;
return rb_funcall(cb, rb_intern("call"), 1, ary);
}
/* this method is currently only called for servers (in OpenSSL <= 0.9.8e) */
static SSL_SESSION *
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy)
#else
ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
#endif
{
VALUE ary, ssl_obj, ret_obj;
SSL_SESSION *sess;
void *ptr;
int state = 0;
OSSL_Debug("SSL SESSION get callback entered");
if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
return NULL;
ssl_obj = (VALUE)ptr;
ary = rb_ary_new2(2);
rb_ary_push(ary, ssl_obj);
rb_ary_push(ary, rb_str_new((const char *)buf, len));
ret_obj = rb_protect(ossl_call_session_get_cb, ary, &state);
if (state) {
rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
return NULL;
}
if (!rb_obj_is_instance_of(ret_obj, cSSLSession))
return NULL;
SafeGetSSLSession(ret_obj, sess);
*copy = 1;
return sess;
}
static VALUE
ossl_call_session_new_cb(VALUE ary)
{
VALUE ssl_obj, cb;
Check_Type(ary, T_ARRAY);
ssl_obj = rb_ary_entry(ary, 0);
cb = rb_funcall(ssl_obj, rb_intern("session_new_cb"), 0);
if (NIL_P(cb)) return Qnil;
return rb_funcall(cb, rb_intern("call"), 1, ary);
}
/* return 1 normal. return 0 removes the session */
static int
ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
{
VALUE ary, ssl_obj, sess_obj;
void *ptr;
int state = 0;
OSSL_Debug("SSL SESSION new callback entered");
if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
return 1;
ssl_obj = (VALUE)ptr;
sess_obj = rb_obj_alloc(cSSLSession);
SSL_SESSION_up_ref(sess);
DATA_PTR(sess_obj) = sess;
ary = rb_ary_new2(2);
rb_ary_push(ary, ssl_obj);
rb_ary_push(ary, sess_obj);
rb_protect(ossl_call_session_new_cb, ary, &state);
if (state) {
rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
}
/*
* return 0 which means to OpenSSL that the session is still
* valid (since we created Ruby Session object) and was not freed by us
* with SSL_SESSION_free(). Call SSLContext#remove_session(sess) in
* session_get_cb block if you don't want OpenSSL to cache the session
* internally.
*/
return 0;
}
static VALUE
ossl_call_session_remove_cb(VALUE ary)
{
VALUE sslctx_obj, cb;
Check_Type(ary, T_ARRAY);
sslctx_obj = rb_ary_entry(ary, 0);
cb = rb_attr_get(sslctx_obj, id_i_session_remove_cb);
if (NIL_P(cb)) return Qnil;
return rb_funcall(cb, rb_intern("call"), 1, ary);
}
static void
ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)
{
VALUE ary, sslctx_obj, sess_obj;
void *ptr;
int state = 0;
OSSL_Debug("SSL SESSION remove callback entered");
if ((ptr = SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_ptr_idx)) == NULL)
return;
sslctx_obj = (VALUE)ptr;
sess_obj = rb_obj_alloc(cSSLSession);
SSL_SESSION_up_ref(sess);
DATA_PTR(sess_obj) = sess;
ary = rb_ary_new2(2);
rb_ary_push(ary, sslctx_obj);
rb_ary_push(ary, sess_obj);
rb_protect(ossl_call_session_remove_cb, ary, &state);
if (state) {
/*
the SSL_CTX is frozen, nowhere to save state.
there is no common accessor method to check it either.
rb_ivar_set(sslctx_obj, ID_callback_state, INT2NUM(state));
*/
}
}
static VALUE
ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
{
X509 *x509;
SSL_CTX *ctx;
GetSSLCTX(arg, ctx);
x509 = DupX509CertPtr(i);
if(!SSL_CTX_add_extra_chain_cert(ctx, x509)){
ossl_raise(eSSLError, NULL);
}
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_attr_get(ssl_obj, id_i_context);
cb = rb_attr_get(sslctx_obj, id_i_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);
GetSSL(ssl_obj, ssl);
GetSSLCTX(ret_obj, ctx2);
SSL_set_SSL_CTX(ssl, ctx2);
rb_ivar_set(ssl_obj, id_i_context, ret_obj);
} else if (!NIL_P(ret_obj)) {
ossl_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;
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));
rb_protect(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
static void
ssl_renegotiation_cb(const SSL *ssl)
{
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_attr_get(ssl_obj, id_i_context);
cb = rb_attr_get(sslctx_obj, id_i_renegotiation_cb);
if (NIL_P(cb)) return;
(void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj);
}
#if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \
defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
static VALUE
ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded)
{
int len = RSTRING_LENINT(cur);
char len_byte;
if (len < 1 || len > 255)
ossl_raise(eSSLError, "Advertised protocol must have length 1..255");
/* Encode the length byte */
len_byte = len;
rb_str_buf_cat(encoded, &len_byte, 1);
rb_str_buf_cat(encoded, RSTRING_PTR(cur), len);
return Qnil;
}
static VALUE
ssl_encode_npn_protocols(VALUE protocols)
{
VALUE encoded = rb_str_new(NULL, 0);
rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded);
return encoded;
}
struct npn_select_cb_common_args {
VALUE cb;
const unsigned char *in;
unsigned inlen;
};
static VALUE
npn_select_cb_common_i(VALUE tmp)
{
struct npn_select_cb_common_args *args = (void *)tmp;
const unsigned char *in = args->in, *in_end = in + args->inlen;
unsigned char l;
long len;
VALUE selected, protocols = rb_ary_new();
/* assume OpenSSL verifies this format */
/* The format is len_1|proto_1|...|len_n|proto_n */
while (in < in_end) {
l = *in++;
rb_ary_push(protocols, rb_str_new((const char *)in, l));
in += l;
}
selected = rb_funcall(args->cb, rb_intern("call"), 1, protocols);
StringValue(selected);
len = RSTRING_LEN(selected);
if (len < 1 || len >= 256) {
ossl_raise(eSSLError, "Selected protocol name must have length 1..255");
}
return selected;
}
static int
ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen)
{
VALUE selected;
int status;
struct npn_select_cb_common_args args;
args.cb = cb;
args.in = in;
args.inlen = inlen;
selected = rb_protect(npn_select_cb_common_i, (VALUE)&args, &status);
if (status) {
VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status));
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
*out = (unsigned char *)RSTRING_PTR(selected);
*outlen = (unsigned char)RSTRING_LEN(selected);
return SSL_TLSEXT_ERR_OK;
}
#endif
#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
static int
ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg)
{
VALUE protocols = (VALUE)arg;
*out = (const unsigned char *) RSTRING_PTR(protocols);
*outlen = RSTRING_LENINT(protocols);
return SSL_TLSEXT_ERR_OK;
}
static int
ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
VALUE sslctx_obj, cb;
sslctx_obj = (VALUE) arg;
cb = rb_attr_get(sslctx_obj, id_i_npn_select_cb);
return ssl_npn_select_cb_common(ssl, cb, (const unsigned char **)out,
outlen, in, inlen);
}
#endif
#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)
{
VALUE sslctx_obj, cb;
sslctx_obj = (VALUE) arg;
cb = rb_attr_get(sslctx_obj, id_i_alpn_select_cb);
return ssl_npn_select_cb_common(ssl, cb, out, outlen, in, inlen);
}
#endif
/* This function may serve as the entry point to support further callbacks. */
static void
ssl_info_cb(const SSL *ssl, int where, int val)
{
int is_server = SSL_is_server((SSL *)ssl);
if (is_server && where & SSL_CB_HANDSHAKE_START) {
ssl_renegotiation_cb(ssl);
}
}
/*
* Gets various OpenSSL options.
*/
static VALUE
ossl_sslctx_get_options(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
return LONG2NUM(SSL_CTX_get_options(ctx));
}
/*
* Sets various OpenSSL options.
*/
static VALUE
ossl_sslctx_set_options(VALUE self, VALUE options)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
if (NIL_P(options)) {
SSL_CTX_set_options(ctx, SSL_OP_ALL);
} else {
SSL_CTX_set_options(ctx, NUM2LONG(options));
}
return self;
}
/*
* call-seq:
* ctx.setup => Qtrue # first time
* ctx.setup => nil # thereafter
*
* This method is called automatically when a new SSLSocket is created.
* However, it is not thread-safe and must be called before creating
* SSLSocket objects in a multi-threaded program.
*/
static VALUE
ossl_sslctx_setup(VALUE self)
{
SSL_CTX *ctx;
X509 *cert = NULL, *client_ca = NULL;
EVP_PKEY *key = NULL;
char *ca_path = NULL, *ca_file = NULL;
int verify_mode;
long i;
VALUE val;
if(OBJ_FROZEN(self)) return Qnil;
GetSSLCTX(self, ctx);
#if !defined(OPENSSL_NO_DH)
SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
#endif
#if !defined(OPENSSL_NO_EC)
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
/* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0,
* but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */
if (RTEST(rb_attr_get(self, id_i_tmp_ecdh_callback))) {
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead");
SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback);
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
/* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores
* tmp_ecdh_callback. So disable ecdh_auto. */
if (!SSL_CTX_set_ecdh_auto(ctx, 0))
ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
# endif
# else
ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; "
"use #ecdh_curves= instead");
# endif
}
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
#endif /* OPENSSL_NO_EC */
val = rb_attr_get(self, id_i_cert_store);
if (!NIL_P(val)) {
X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */
SSL_CTX_set_cert_store(ctx, store);
#if !defined(HAVE_X509_STORE_UP_REF)
/*
* WORKAROUND:
* X509_STORE can count references, but
* X509_STORE_free() doesn't care it.
* So we won't increment it but mark it by ex_data.
*/
SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void *)1);
#else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */
X509_STORE_up_ref(store);
#endif
}
val = rb_attr_get(self, id_i_extra_chain_cert);
if(!NIL_P(val)){
rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self);
}
/* private key may be bundled in certificate file. */
val = rb_attr_get(self, id_i_cert);
cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */
val = rb_attr_get(self, id_i_key);
key = NIL_P(val) ? NULL : GetPrivPKeyPtr(val); /* NO DUP NEEDED */
if (cert && key) {
if (!SSL_CTX_use_certificate(ctx, cert)) {
/* Adds a ref => Safe to FREE */
ossl_raise(eSSLError, "SSL_CTX_use_certificate");
}
if (!SSL_CTX_use_PrivateKey(ctx, key)) {
/* Adds a ref => Safe to FREE */
ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
}
if (!SSL_CTX_check_private_key(ctx)) {
ossl_raise(eSSLError, "SSL_CTX_check_private_key");
}
}
val = rb_attr_get(self, id_i_client_ca);
if(!NIL_P(val)){
if (RB_TYPE_P(val, T_ARRAY)) {
for(i = 0; i < RARRAY_LEN(val); i++){
client_ca = GetX509CertPtr(RARRAY_AREF(val, i));
if (!SSL_CTX_add_client_CA(ctx, client_ca)){
/* Copies X509_NAME => FREE it. */
ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
}
}
}
else{
client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */
if (!SSL_CTX_add_client_CA(ctx, client_ca)){
/* Copies X509_NAME => FREE it. */
ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
}
}
}
val = rb_attr_get(self, id_i_ca_file);
ca_file = NIL_P(val) ? NULL : StringValueCStr(val);
val = rb_attr_get(self, id_i_ca_path);
ca_path = NIL_P(val) ? NULL : StringValueCStr(val);
if(ca_file || ca_path){
if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
rb_warning("can't set verify locations");
}
val = rb_attr_get(self, id_i_verify_mode);
verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val);
SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback);
if (RTEST(rb_attr_get(self, id_i_client_cert_cb)))
SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb);
val = rb_attr_get(self, id_i_timeout);
if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val));
val = rb_attr_get(self, id_i_verify_depth);
if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
val = rb_attr_get(self, id_i_npn_protocols);
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
rb_ivar_set(self, id_npn_protocols_encoded, encoded);
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded);
OSSL_Debug("SSL NPN advertise callback added");
}
if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) {
SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self);
OSSL_Debug("SSL NPN select callback added");
}
#endif
#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
val = rb_attr_get(self, id_i_alpn_protocols);
if (!NIL_P(val)) {
VALUE rprotos = ssl_encode_npn_protocols(val);
/* returns 0 on success */
if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos),
RSTRING_LENINT(rprotos)))
ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos");
OSSL_Debug("SSL ALPN values added");
}
if (RTEST(rb_attr_get(self, id_i_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);
val = rb_attr_get(self, id_i_session_id_context);
if (!NIL_P(val)){
StringValue(val);
if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val),
RSTRING_LENINT(val))){
ossl_raise(eSSLError, "SSL_CTX_set_session_id_context");
}
}
if (RTEST(rb_attr_get(self, id_i_session_get_cb))) {
SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb);
OSSL_Debug("SSL SESSION get callback added");
}
if (RTEST(rb_attr_get(self, id_i_session_new_cb))) {
SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb);
OSSL_Debug("SSL SESSION new callback added");
}
if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) {
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_attr_get(self, id_i_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;
}
static VALUE
ossl_ssl_cipher_to_ary(const SSL_CIPHER *cipher)
{
VALUE ary;
int bits, alg_bits;
ary = rb_ary_new2(4);
rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_name(cipher)));
rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_version(cipher)));
bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
rb_ary_push(ary, INT2NUM(bits));
rb_ary_push(ary, INT2NUM(alg_bits));
return ary;
}
/*
* call-seq:
* ctx.ciphers => [[name, version, bits, alg_bits], ...]
*
* The list of cipher suites configured for this context.
*/
static VALUE
ossl_sslctx_get_ciphers(VALUE self)
{
SSL_CTX *ctx;
STACK_OF(SSL_CIPHER) *ciphers;
const SSL_CIPHER *cipher;
VALUE ary;
int i, num;
GetSSLCTX(self, ctx);
if(!ctx){
rb_warning("SSL_CTX is not initialized.");
return Qnil;
}
ciphers = SSL_CTX_get_ciphers(ctx);
if (!ciphers)
return rb_ary_new();
num = sk_SSL_CIPHER_num(ciphers);
ary = rb_ary_new2(num);
for(i = 0; i < num; i++){
cipher = sk_SSL_CIPHER_value(ciphers, i);
rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher));
}
return ary;
}
/*
* call-seq:
* ctx.ciphers = "cipher1:cipher2:..."
* ctx.ciphers = [name, ...]
* ctx.ciphers = [[name, version, bits, alg_bits], ...]
*
* Sets the list of available cipher suites for this context. Note in a server
* context some ciphers require the appropriate certificates. For example, an
* RSA cipher suite can only be chosen when an RSA certificate is available.
*/
static VALUE
ossl_sslctx_set_ciphers(VALUE self, VALUE v)
{
SSL_CTX *ctx;
VALUE str, elem;
int i;
rb_check_frozen(self);
if (NIL_P(v))
return v;
else if (RB_TYPE_P(v, T_ARRAY)) {
str = rb_str_new(0, 0);
for (i = 0; i < RARRAY_LEN(v); i++) {
elem = rb_ary_entry(v, i);
if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0);
elem = rb_String(elem);
rb_str_append(str, elem);
if (i < RARRAY_LEN(v)-1) rb_str_cat2(str, ":");
}
} else {
str = v;
StringValue(str);
}
GetSSLCTX(self, ctx);
if(!ctx){
ossl_raise(eSSLError, "SSL_CTX is not initialized.");
return Qnil;
}
if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) {
ossl_raise(eSSLError, "SSL_CTX_set_cipher_list");
}
return v;
}
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
#if !defined(OPENSSL_NO_EC)
/*
* call-seq:
* ctx.ecdh_curves = curve_list -> curve_list
*
* Sets the list of "supported elliptic curves" for this context.
*
* For a TLS client, the list is directly used in the Supported Elliptic Curves
* Extension. For a server, the list is used by OpenSSL to determine the set of
* shared curves. OpenSSL will pick the most appropriate one from it.
*
* Note that this works differently with old OpenSSL (<= 1.0.1). Only one curve
* can be set, and this has no effect for TLS clients.
*
* === Example
* ctx1 = OpenSSL::SSL::SSLContext.new
* ctx1.ecdh_curves = "X25519:P-256:P-224"
* svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1)
* Thread.new { svr.accept }
*
* ctx2 = OpenSSL::SSL::SSLContext.new
* ctx2.ecdh_curves = "P-256"
* cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2)
* cli.connect
*
* p cli.tmp_key.group.curve_name
* # => "prime256v1" (is an alias for NIST P-256)
*/
static VALUE
ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
StringValueCStr(arg);
#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST)
if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg)))
ossl_raise(eSSLError, NULL);
#else
/* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to
* SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */
{
VALUE curve, splitted;
EC_KEY *ec;
int nid;
splitted = rb_str_split(arg, ":");
if (!RARRAY_LEN(splitted))
ossl_raise(eSSLError, "invalid input format");
curve = RARRAY_AREF(splitted, 0);
StringValueCStr(curve);
/* SSL_CTX_set1_curves_list() accepts NIST names */
nid = EC_curve_nist2nid(RSTRING_PTR(curve));
if (nid == NID_undef)
nid = OBJ_txt2nid(RSTRING_PTR(curve));
if (nid == NID_undef)
ossl_raise(eSSLError, "unknown curve name");
ec = EC_KEY_new_by_curve_name(nid);
if (!ec)
ossl_raise(eSSLError, NULL);
EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
if (!SSL_CTX_set_tmp_ecdh(ctx, ec)) {
EC_KEY_free(ec);
ossl_raise(eSSLError, "SSL_CTX_set_tmp_ecdh");
}
EC_KEY_free(ec);
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
/* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto
* is enabled. So disable ecdh_auto. */
if (!SSL_CTX_set_ecdh_auto(ctx, 0))
ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
# endif
}
#endif
return arg;
}
#else
#define ossl_sslctx_set_ecdh_curves rb_f_notimplement
#endif
/*
* call-seq:
* ctx.security_level -> Integer
*
* Returns the security level for the context.
*
* See also OpenSSL::SSL::SSLContext#security_level=.
*/
static VALUE
ossl_sslctx_get_security_level(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL)
return INT2NUM(SSL_CTX_get_security_level(ctx));
#else
(void)ctx;
return INT2FIX(0);
#endif
}
/*
* call-seq:
* ctx.security_level = integer
*
* Sets the security level for the context. OpenSSL limits parameters according
* to the level. The "parameters" include: ciphersuites, curves, key sizes,
* certificate signature algorithms, protocol version and so on. For example,
* level 1 rejects parameters offering below 80 bits of security, such as
* ciphersuites using MD5 for the MAC or RSA keys shorter than 1024 bits.
*
* Note that attempts to set such parameters with insufficient security are
* also blocked. You need to lower the level first.
*
* This feature is not supported in OpenSSL < 1.1.0, and setting the level to
* other than 0 will raise NotImplementedError. Level 0 means everything is
* permitted, the same behavior as previous versions of OpenSSL.
*
* See the manpage of SSL_CTX_set_security_level(3) for details.
*/
static VALUE
ossl_sslctx_set_security_level(VALUE self, VALUE value)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL)
SSL_CTX_set_security_level(ctx, NUM2INT(value));
#else
(void)ctx;
if (NUM2INT(value) != 0)
ossl_raise(rb_eNotImpError, "setting security level to other than 0 is "
"not supported in this version of OpenSSL");
#endif
return value;
}
/*
* call-seq:
* ctx.session_add(session) -> true | false
*
* Adds +session+ to the session cache.
*/
static VALUE
ossl_sslctx_session_add(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
SafeGetSSLSession(arg, sess);
return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse;
}
/*
* call-seq:
* ctx.session_remove(session) -> true | false
*
* Removes +session+ from the session cache.
*/
static VALUE
ossl_sslctx_session_remove(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
SafeGetSSLSession(arg, sess);
return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse;
}
/*
* call-seq:
* ctx.session_cache_mode -> Integer
*
* The current session cache mode.
*/
static VALUE
ossl_sslctx_get_session_cache_mode(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
return LONG2NUM(SSL_CTX_get_session_cache_mode(ctx));
}
/*
* call-seq:
* ctx.session_cache_mode=(integer) -> Integer
*
* Sets the SSL session cache mode. Bitwise-or together the desired
* SESSION_CACHE_* constants to set. See SSL_CTX_set_session_cache_mode(3) for
* details.
*/
static VALUE
ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
SSL_CTX_set_session_cache_mode(ctx, NUM2LONG(arg));
return arg;
}
/*
* call-seq:
* ctx.session_cache_size -> Integer
*
* Returns the current session cache size. Zero is used to represent an
* unlimited cache size.
*/
static VALUE
ossl_sslctx_get_session_cache_size(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
return LONG2NUM(SSL_CTX_sess_get_cache_size(ctx));
}
/*
* call-seq:
* ctx.session_cache_size=(integer) -> Integer
*
* Sets the session cache size. Returns the previously valid session cache
* size. Zero is used to represent an unlimited session cache size.
*/
static VALUE
ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
SSL_CTX_sess_set_cache_size(ctx, NUM2LONG(arg));
return arg;
}
/*
* call-seq:
* ctx.session_cache_stats -> Hash
*
* Returns a Hash containing the following keys:
*
* :accept:: Number of started SSL/TLS handshakes in server mode
* :accept_good:: Number of established SSL/TLS sessions in server mode
* :accept_renegotiate:: Number of start renegotiations in server mode
* :cache_full:: Number of sessions that were removed due to cache overflow
* :cache_hits:: Number of successfully reused connections
* :cache_misses:: Number of sessions proposed by clients that were not found
* in the cache
* :cache_num:: Number of sessions in the internal session cache
* :cb_hits:: Number of sessions retrieved from the external cache in server
* mode
* :connect:: Number of started SSL/TLS handshakes in client mode
* :connect_good:: Number of established SSL/TLS sessions in client mode
* :connect_renegotiate:: Number of start renegotiations in client mode
* :timeouts:: Number of sessions proposed by clients that were found in the
* cache but had expired due to timeouts
*/
static VALUE
ossl_sslctx_get_session_cache_stats(VALUE self)
{
SSL_CTX *ctx;
VALUE hash;
GetSSLCTX(self, ctx);
hash = rb_hash_new();
rb_hash_aset(hash, ID2SYM(rb_intern("cache_num")), LONG2NUM(SSL_CTX_sess_number(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect")), LONG2NUM(SSL_CTX_sess_connect(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect_good")), LONG2NUM(SSL_CTX_sess_connect_good(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect_renegotiate")), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept")), LONG2NUM(SSL_CTX_sess_accept(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept_good")), LONG2NUM(SSL_CTX_sess_accept_good(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept_renegotiate")), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_hits")), LONG2NUM(SSL_CTX_sess_hits(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cb_hits")), LONG2NUM(SSL_CTX_sess_cb_hits(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_misses")), LONG2NUM(SSL_CTX_sess_misses(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_full")), LONG2NUM(SSL_CTX_sess_cache_full(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("timeouts")), LONG2NUM(SSL_CTX_sess_timeouts(ctx)));
return hash;
}
/*
* call-seq:
* ctx.flush_sessions(time | nil) -> self
*
* Removes sessions in the internal cache that have expired at +time+.
*/
static VALUE
ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
{
VALUE arg1;
SSL_CTX *ctx;
time_t tm = 0;
rb_scan_args(argc, argv, "01", &arg1);
GetSSLCTX(self, ctx);
if (NIL_P(arg1)) {
tm = time(0);
} else if (rb_obj_is_instance_of(arg1, rb_cTime)) {
tm = NUM2LONG(rb_funcall(arg1, rb_intern("to_i"), 0));
} else {
ossl_raise(rb_eArgError, "arg must be Time or nil");
}
SSL_CTX_flush_sessions(ctx, (long)tm);
return self;
}
/*
* SSLSocket class
*/
#ifndef OPENSSL_NO_SOCK
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
static inline int
ssl_started(SSL *ssl)
{
/* the FD is set in ossl_ssl_setup(), called by #connect or #accept */
return SSL_get_fd(ssl) >= 0;
}
static void
ossl_ssl_free(void *ssl)
{
SSL_free(ssl);
}
const rb_data_type_t ossl_ssl_type = {
"OpenSSL/SSL",
{
0, ossl_ssl_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
static VALUE
ossl_ssl_s_alloc(VALUE klass)
{
return TypedData_Wrap_Struct(klass, &ossl_ssl_type, NULL);
}
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
/*
* call-seq:
* SSLSocket.new(io) => aSSLSocket
* SSLSocket.new(io, ctx) => aSSLSocket
*
* Creates a new SSL socket from +io+ which must be a real IO object (not an
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
* IO-like object that responds to read/write).
*
* If +ctx+ is provided the SSL Sockets initial params will be taken from
* the context.
*
* The OpenSSL::Buffering module provides additional IO methods.
*
* This method will freeze the SSLContext if one is provided;
* however, session management is still allowed in the frozen SSLContext.
*/
static VALUE
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
{
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
VALUE io, v_ctx, verify_cb;
SSL *ssl;
SSL_CTX *ctx;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl);
if (ssl)
ossl_raise(eSSLError, "SSL already initialized");
if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1)
v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0);
GetSSLCTX(v_ctx, ctx);
rb_ivar_set(self, id_i_context, v_ctx);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
ossl_sslctx_setup(v_ctx);
if (rb_respond_to(io, rb_intern("nonblock=")))
rb_funcall(io, rb_intern("nonblock="), 1, Qtrue);
rb_ivar_set(self, id_i_io, io);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
ssl = SSL_new(ctx);
if (!ssl)
ossl_raise(eSSLError, NULL);
RTYPEDDATA_DATA(self) = ssl;
SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self);
SSL_set_info_callback(ssl, ssl_info_cb);
verify_cb = rb_attr_get(v_ctx, id_i_verify_callback);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb);
rb_call_super(0, NULL);
return self;
}
static VALUE
ossl_ssl_setup(VALUE self)
{
VALUE io;
SSL *ssl;
rb_io_t *fptr;
GetSSL(self, ssl);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
if (ssl_started(ssl))
return Qtrue;
io = rb_attr_get(self, id_i_io);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
rb_io_check_writable(fptr);
SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr)));
return Qtrue;
}
#ifdef _WIN32
#define ssl_get_error(ssl, ret) (errno = rb_w32_map_errno(WSAGetLastError()), SSL_get_error((ssl), (ret)))
#else
#define ssl_get_error(ssl, ret) SSL_get_error((ssl), (ret))
#endif
static void
write_would_block(int nonblock)
{
if (nonblock)
ossl_raise(eSSLErrorWaitWritable, "write would block");
}
static void
read_would_block(int nonblock)
{
if (nonblock)
ossl_raise(eSSLErrorWaitReadable, "read would block");
}
static int
no_exception_p(VALUE opts)
{
if (RB_TYPE_P(opts, T_HASH) &&
rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse)
return 1;
return 0;
}
static VALUE
ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
{
SSL *ssl;
rb_io_t *fptr;
int ret, ret2;
VALUE cb_state;
int nonblock = opts != Qfalse;
rb_ivar_set(self, ID_callback_state, Qnil);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
GetOpenFile(rb_attr_get(self, id_i_io), fptr);
for(;;){
ret = func(ssl);
cb_state = rb_attr_get(self, ID_callback_state);
if (!NIL_P(cb_state)) {
/* must cleanup OpenSSL error stack before re-raising */
ossl_clear_error();
rb_jump_tag(NUM2INT(cb_state));
}
if (ret > 0)
break;
switch((ret2 = ssl_get_error(ssl, ret))){
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
rb_io_wait_writable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
rb_io_wait_readable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_SYSCALL:
if (errno) rb_sys_fail(funcname);
ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
default:
ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
}
}
return self;
}
/*
* call-seq:
* ssl.connect => self
*
* Initiates an SSL/TLS handshake with a server. The handshake may be started
* after unencrypted data has been sent over the socket.
*/
static VALUE
ossl_ssl_connect(VALUE self)
{
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse);
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
}
/*
* call-seq:
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
* ssl.connect_nonblock([options]) => self
*
* Initiates the SSL/TLS handshake as a client in non-blocking manner.
*
* # emulates blocking connect
* begin
* ssl.connect_nonblock
* rescue IO::WaitReadable
* IO.select([s2])
* retry
* rescue IO::WaitWritable
* IO.select(nil, [s2])
* retry
* end
*
* By specifying a keyword argument _exception_ to +false+, you can indicate
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
* that connect_nonblock should not raise an IO::WaitReadable or
* IO::WaitWritable exception, but return the symbol +:wait_readable+ or
* +:wait_writable+ instead.
*/
static VALUE
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self)
{
VALUE opts;
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
rb_scan_args(argc, argv, "0:", &opts);
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_connect, "SSL_connect", opts);
}
/*
* call-seq:
* ssl.accept => self
*
* Waits for a SSL/TLS client to initiate a handshake. The handshake may be
* started after unencrypted data has been sent over the socket.
*/
static VALUE
ossl_ssl_accept(VALUE self)
{
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qfalse);
}
/*
* call-seq:
accept_nonblock supports "exception: false" This is analogous to functionality found in IO#read_nonblock and IO#wait_nonblock. Raising exceptions for common failures on non-blocking servers is expensive and makes $DEBUG too noisy. Benchmark results: user system total real default 2.790000 0.870000 3.660000 ( 3.671597) exception: false 1.120000 0.800000 1.920000 ( 1.922032) exception: false (cached arg) 0.820000 0.770000 1.590000 ( 1.589267) --------------------- benchmark script ------------------------ require 'socket' require 'benchmark' require 'tmpdir' nr = 1000000 Dir.mktmpdir('nb_bench') do |path| sock_path = "#{path}/test.sock" s = UNIXServer.new(sock_path) Benchmark.bmbm do |x| x.report("default") do nr.times do begin s.accept_nonblock rescue IO::WaitReadable end end end x.report("exception: false") do nr.times do begin s.accept_nonblock(exception: false) rescue IO::WaitReadable abort "should not raise" end end end x.report("exception: false (cached arg)") do arg = { exception: false } nr.times do begin s.accept_nonblock(arg) rescue IO::WaitReadable abort "should not raise" end end end end end * ext/socket/init.c (rsock_s_accept_nonblock): support exception: false [ruby-core:66385] [Feature #10532] * ext/socket/init.c (rsock_init_socket_init): define new symbols * ext/socket/rubysocket.h: adjust prototype * ext/socket/socket.c (sock_accept_nonblock): support exception: false * ext/openssl/ossl_ssl.c (ossl_ssl_accept_nonblock): ditto * ext/socket/socket.c (Init_socket): adjust accept_nonblock definition * ext/openssl/ossl_ssl.c (Init_ossl_ssl): ditto * ext/socket/tcpserver.c (rsock_init_tcpserver): ditto * ext/socket/unixserver.c (rsock_init_unixserver): ditto * ext/socket/tcpserver.c (tcp_accept_nonblock): adjust rsock_s_accept_nonblock call * ext/socket/unixserver.c (unix_accept_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_start_ssl): support no_exception * ext/openssl/ossl_ssl.c (ossl_ssl_connect): adjust ossl_start_ssl call * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_ssl_accept): ditto * test/socket/test_nonblock.rb (test_accept_nonblock): test for "exception :false" * test/socket/test_tcp.rb (test_accept_nonblock): new test * test/socket/test_unix.rb (test_accept_nonblock): ditto * test/openssl/test_pair.rb (test_accept_nonblock_no_exception): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-03-11 23:03:04 -04:00
* ssl.accept_nonblock([options]) => self
*
* Initiates the SSL/TLS handshake as a server in non-blocking manner.
*
* # emulates blocking accept
* begin
* ssl.accept_nonblock
* rescue IO::WaitReadable
* IO.select([s2])
* retry
* rescue IO::WaitWritable
* IO.select(nil, [s2])
* retry
* end
*
* By specifying a keyword argument _exception_ to +false+, you can indicate
accept_nonblock supports "exception: false" This is analogous to functionality found in IO#read_nonblock and IO#wait_nonblock. Raising exceptions for common failures on non-blocking servers is expensive and makes $DEBUG too noisy. Benchmark results: user system total real default 2.790000 0.870000 3.660000 ( 3.671597) exception: false 1.120000 0.800000 1.920000 ( 1.922032) exception: false (cached arg) 0.820000 0.770000 1.590000 ( 1.589267) --------------------- benchmark script ------------------------ require 'socket' require 'benchmark' require 'tmpdir' nr = 1000000 Dir.mktmpdir('nb_bench') do |path| sock_path = "#{path}/test.sock" s = UNIXServer.new(sock_path) Benchmark.bmbm do |x| x.report("default") do nr.times do begin s.accept_nonblock rescue IO::WaitReadable end end end x.report("exception: false") do nr.times do begin s.accept_nonblock(exception: false) rescue IO::WaitReadable abort "should not raise" end end end x.report("exception: false (cached arg)") do arg = { exception: false } nr.times do begin s.accept_nonblock(arg) rescue IO::WaitReadable abort "should not raise" end end end end end * ext/socket/init.c (rsock_s_accept_nonblock): support exception: false [ruby-core:66385] [Feature #10532] * ext/socket/init.c (rsock_init_socket_init): define new symbols * ext/socket/rubysocket.h: adjust prototype * ext/socket/socket.c (sock_accept_nonblock): support exception: false * ext/openssl/ossl_ssl.c (ossl_ssl_accept_nonblock): ditto * ext/socket/socket.c (Init_socket): adjust accept_nonblock definition * ext/openssl/ossl_ssl.c (Init_ossl_ssl): ditto * ext/socket/tcpserver.c (rsock_init_tcpserver): ditto * ext/socket/unixserver.c (rsock_init_unixserver): ditto * ext/socket/tcpserver.c (tcp_accept_nonblock): adjust rsock_s_accept_nonblock call * ext/socket/unixserver.c (unix_accept_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_start_ssl): support no_exception * ext/openssl/ossl_ssl.c (ossl_ssl_connect): adjust ossl_start_ssl call * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_ssl_accept): ditto * test/socket/test_nonblock.rb (test_accept_nonblock): test for "exception :false" * test/socket/test_tcp.rb (test_accept_nonblock): new test * test/socket/test_unix.rb (test_accept_nonblock): ditto * test/openssl/test_pair.rb (test_accept_nonblock_no_exception): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-03-11 23:03:04 -04:00
* that accept_nonblock should not raise an IO::WaitReadable or
* IO::WaitWritable exception, but return the symbol +:wait_readable+ or
* +:wait_writable+ instead.
*/
static VALUE
accept_nonblock supports "exception: false" This is analogous to functionality found in IO#read_nonblock and IO#wait_nonblock. Raising exceptions for common failures on non-blocking servers is expensive and makes $DEBUG too noisy. Benchmark results: user system total real default 2.790000 0.870000 3.660000 ( 3.671597) exception: false 1.120000 0.800000 1.920000 ( 1.922032) exception: false (cached arg) 0.820000 0.770000 1.590000 ( 1.589267) --------------------- benchmark script ------------------------ require 'socket' require 'benchmark' require 'tmpdir' nr = 1000000 Dir.mktmpdir('nb_bench') do |path| sock_path = "#{path}/test.sock" s = UNIXServer.new(sock_path) Benchmark.bmbm do |x| x.report("default") do nr.times do begin s.accept_nonblock rescue IO::WaitReadable end end end x.report("exception: false") do nr.times do begin s.accept_nonblock(exception: false) rescue IO::WaitReadable abort "should not raise" end end end x.report("exception: false (cached arg)") do arg = { exception: false } nr.times do begin s.accept_nonblock(arg) rescue IO::WaitReadable abort "should not raise" end end end end end * ext/socket/init.c (rsock_s_accept_nonblock): support exception: false [ruby-core:66385] [Feature #10532] * ext/socket/init.c (rsock_init_socket_init): define new symbols * ext/socket/rubysocket.h: adjust prototype * ext/socket/socket.c (sock_accept_nonblock): support exception: false * ext/openssl/ossl_ssl.c (ossl_ssl_accept_nonblock): ditto * ext/socket/socket.c (Init_socket): adjust accept_nonblock definition * ext/openssl/ossl_ssl.c (Init_ossl_ssl): ditto * ext/socket/tcpserver.c (rsock_init_tcpserver): ditto * ext/socket/unixserver.c (rsock_init_unixserver): ditto * ext/socket/tcpserver.c (tcp_accept_nonblock): adjust rsock_s_accept_nonblock call * ext/socket/unixserver.c (unix_accept_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_start_ssl): support no_exception * ext/openssl/ossl_ssl.c (ossl_ssl_connect): adjust ossl_start_ssl call * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_ssl_accept): ditto * test/socket/test_nonblock.rb (test_accept_nonblock): test for "exception :false" * test/socket/test_tcp.rb (test_accept_nonblock): new test * test/socket/test_unix.rb (test_accept_nonblock): ditto * test/openssl/test_pair.rb (test_accept_nonblock_no_exception): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-03-11 23:03:04 -04:00
ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self)
{
VALUE opts;
accept_nonblock supports "exception: false" This is analogous to functionality found in IO#read_nonblock and IO#wait_nonblock. Raising exceptions for common failures on non-blocking servers is expensive and makes $DEBUG too noisy. Benchmark results: user system total real default 2.790000 0.870000 3.660000 ( 3.671597) exception: false 1.120000 0.800000 1.920000 ( 1.922032) exception: false (cached arg) 0.820000 0.770000 1.590000 ( 1.589267) --------------------- benchmark script ------------------------ require 'socket' require 'benchmark' require 'tmpdir' nr = 1000000 Dir.mktmpdir('nb_bench') do |path| sock_path = "#{path}/test.sock" s = UNIXServer.new(sock_path) Benchmark.bmbm do |x| x.report("default") do nr.times do begin s.accept_nonblock rescue IO::WaitReadable end end end x.report("exception: false") do nr.times do begin s.accept_nonblock(exception: false) rescue IO::WaitReadable abort "should not raise" end end end x.report("exception: false (cached arg)") do arg = { exception: false } nr.times do begin s.accept_nonblock(arg) rescue IO::WaitReadable abort "should not raise" end end end end end * ext/socket/init.c (rsock_s_accept_nonblock): support exception: false [ruby-core:66385] [Feature #10532] * ext/socket/init.c (rsock_init_socket_init): define new symbols * ext/socket/rubysocket.h: adjust prototype * ext/socket/socket.c (sock_accept_nonblock): support exception: false * ext/openssl/ossl_ssl.c (ossl_ssl_accept_nonblock): ditto * ext/socket/socket.c (Init_socket): adjust accept_nonblock definition * ext/openssl/ossl_ssl.c (Init_ossl_ssl): ditto * ext/socket/tcpserver.c (rsock_init_tcpserver): ditto * ext/socket/unixserver.c (rsock_init_unixserver): ditto * ext/socket/tcpserver.c (tcp_accept_nonblock): adjust rsock_s_accept_nonblock call * ext/socket/unixserver.c (unix_accept_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_start_ssl): support no_exception * ext/openssl/ossl_ssl.c (ossl_ssl_connect): adjust ossl_start_ssl call * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_ssl_accept): ditto * test/socket/test_nonblock.rb (test_accept_nonblock): test for "exception :false" * test/socket/test_tcp.rb (test_accept_nonblock): new test * test/socket/test_unix.rb (test_accept_nonblock): ditto * test/openssl/test_pair.rb (test_accept_nonblock_no_exception): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-03-11 23:03:04 -04:00
rb_scan_args(argc, argv, "0:", &opts);
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_accept, "SSL_accept", opts);
}
static VALUE
ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
{
SSL *ssl;
int ilen, nread = 0;
VALUE len, str;
rb_io_t *fptr;
VALUE io, opts = Qnil;
if (nonblock) {
rb_scan_args(argc, argv, "11:", &len, &str, &opts);
} else {
rb_scan_args(argc, argv, "11", &len, &str);
}
ilen = NUM2INT(len);
if(NIL_P(str)) str = rb_str_new(0, ilen);
else{
StringValue(str);
rb_str_modify(str);
rb_str_resize(str, ilen);
}
if(ilen == 0) return str;
GetSSL(self, ssl);
io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
if (ssl_started(ssl)) {
if(!nonblock && SSL_pending(ssl) <= 0)
rb_thread_wait_fd(FPTR_TO_FD(fptr));
for (;;){
nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LENINT(str));
switch(ssl_get_error(ssl, nread)){
case SSL_ERROR_NONE:
goto end;
case SSL_ERROR_ZERO_RETURN:
if (no_exception_p(opts)) { return Qnil; }
rb_eof_error();
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
rb_io_wait_writable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
rb_io_wait_readable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_SYSCALL:
if (!ERR_peek_error()) {
if (errno)
rb_sys_fail(0);
else {
/*
* The underlying BIO returned 0. This is actually a
* protocol error. But unfortunately, not all
* implementations cleanly shutdown the TLS connection
* but just shutdown/close the TCP connection. So report
* EOF for now...
*/
if (no_exception_p(opts)) { return Qnil; }
rb_eof_error();
}
}
default:
ossl_raise(eSSLError, "SSL_read");
}
}
}
else {
ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread");
rb_warning("SSL session is not started yet.");
if (nonblock)
return rb_funcall(io, meth, 3, len, str, opts);
else
return rb_funcall(io, meth, 2, len, str);
}
end:
rb_str_set_len(str, nread);
OBJ_TAINT(str);
return str;
}
/*
* call-seq:
* ssl.sysread(length) => string
* ssl.sysread(length, buffer) => buffer
*
* Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+
* is provided the data will be written into it.
*/
static VALUE
ossl_ssl_read(int argc, VALUE *argv, VALUE self)
{
return ossl_ssl_read_internal(argc, argv, self, 0);
}
/*
* call-seq:
* ssl.sysread_nonblock(length) => string
* ssl.sysread_nonblock(length, buffer) => buffer
* ssl.sysread_nonblock(length[, buffer [, opts]) => buffer
*
* A non-blocking version of #sysread. Raises an SSLError if reading would
* block. If "exception: false" is passed, this method returns a symbol of
* :wait_readable, :wait_writable, or nil, rather than raising an exception.
*
* Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+
* is provided the data will be written into it.
*/
static VALUE
ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self)
{
return ossl_ssl_read_internal(argc, argv, self, 1);
}
static VALUE
ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
{
SSL *ssl;
int nwrite = 0;
rb_io_t *fptr;
int nonblock = opts != Qfalse;
VALUE io;
StringValue(str);
GetSSL(self, ssl);
io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
if (ssl_started(ssl)) {
for (;;){
int num = RSTRING_LENINT(str);
/* SSL_write(3ssl) manpage states num == 0 is undefined */
if (num == 0)
goto end;
nwrite = SSL_write(ssl, RSTRING_PTR(str), num);
switch(ssl_get_error(ssl, nwrite)){
case SSL_ERROR_NONE:
goto end;
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
rb_io_wait_writable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
rb_io_wait_readable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_SYSCALL:
if (errno) rb_sys_fail(0);
default:
ossl_raise(eSSLError, "SSL_write");
}
}
}
else {
ID meth = nonblock ?
rb_intern("write_nonblock") : rb_intern("syswrite");
rb_warning("SSL session is not started yet.");
if (nonblock)
return rb_funcall(io, meth, 2, str, opts);
else
return rb_funcall(io, meth, 1, str);
}
end:
return INT2NUM(nwrite);
}
/*
* call-seq:
* ssl.syswrite(string) => Integer
*
* Writes +string+ to the SSL connection.
*/
static VALUE
ossl_ssl_write(VALUE self, VALUE str)
{
return ossl_ssl_write_internal(self, str, Qfalse);
}
/*
* call-seq:
* ssl.syswrite_nonblock(string) => Integer
*
* Writes +string+ to the SSL connection in a non-blocking manner. Raises an
* SSLError if writing would block.
*/
static VALUE
ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self)
{
VALUE str, opts;
rb_scan_args(argc, argv, "1:", &str, &opts);
return ossl_ssl_write_internal(self, str, opts);
}
/*
* call-seq:
* ssl.stop => nil
*
* Sends "close notify" to the peer and tries to shut down the SSL connection
* gracefully.
*/
static VALUE
ossl_ssl_stop(VALUE self)
{
SSL *ssl;
int ret;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
if (!ssl_started(ssl))
return Qnil;
ret = SSL_shutdown(ssl);
if (ret == 1) /* Have already received close_notify */
return Qnil;
if (ret == 0) /* Sent close_notify, but we don't wait for reply */
return Qnil;
/*
* XXX: Something happened. Possibly it failed because the underlying socket
* is not writable/readable, since it is in non-blocking mode. We should do
* some proper error handling using SSL_get_error() and maybe retry, but we
* can't block here. Give up for now.
*/
ossl_clear_error();
return Qnil;
}
/*
* call-seq:
* ssl.cert => cert or nil
*
* The X509 certificate for this socket endpoint.
*/
static VALUE
ossl_ssl_get_cert(VALUE self)
{
SSL *ssl;
X509 *cert = NULL;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
/*
* Is this OpenSSL bug? Should add a ref?
* TODO: Ask for.
*/
cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */
if (!cert) {
return Qnil;
}
return ossl_x509_new(cert);
}
/*
* call-seq:
* ssl.peer_cert => cert or nil
*
* The X509 certificate for this socket's peer.
*/
static VALUE
ossl_ssl_get_peer_cert(VALUE self)
{
SSL *ssl;
X509 *cert = NULL;
VALUE obj;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */
if (!cert) {
return Qnil;
}
obj = ossl_x509_new(cert);
X509_free(cert);
return obj;
}
/*
* call-seq:
* ssl.peer_cert_chain => [cert, ...] or nil
*
* The X509 certificate chain for this socket's peer.
*/
static VALUE
ossl_ssl_get_peer_cert_chain(VALUE self)
{
SSL *ssl;
STACK_OF(X509) *chain;
X509 *cert;
VALUE ary;
int i, num;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
chain = SSL_get_peer_cert_chain(ssl);
if(!chain) return Qnil;
num = sk_X509_num(chain);
ary = rb_ary_new2(num);
for (i = 0; i < num; i++){
cert = sk_X509_value(chain, i);
rb_ary_push(ary, ossl_x509_new(cert));
}
return ary;
}
/*
* call-seq:
* ssl.ssl_version => String
*
* Returns a String representing the SSL/TLS version that was negotiated
* for the connection, for example "TLSv1.2".
*/
static VALUE
ossl_ssl_get_version(VALUE self)
{
SSL *ssl;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
return rb_str_new2(SSL_get_version(ssl));
}
/*
* call-seq:
* ssl.cipher => [name, version, bits, alg_bits]
*
* The cipher being used for the current connection
*/
static VALUE
ossl_ssl_get_cipher(VALUE self)
{
SSL *ssl;
SSL_CIPHER *cipher;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
cipher = (SSL_CIPHER *)SSL_get_current_cipher(ssl);
return ossl_ssl_cipher_to_ary(cipher);
}
/*
* call-seq:
* ssl.state => string
*
* A description of the current connection state. This is for diagnostic
* purposes only.
*/
static VALUE
ossl_ssl_get_state(VALUE self)
{
SSL *ssl;
VALUE ret;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
ret = rb_str_new2(SSL_state_string(ssl));
if (ruby_verbose) {
rb_str_cat2(ret, ": ");
rb_str_cat2(ret, SSL_state_string_long(ssl));
}
return ret;
}
/*
* call-seq:
* ssl.pending => Integer
*
* The number of bytes that are immediately available for reading.
*/
static VALUE
ossl_ssl_pending(VALUE self)
{
SSL *ssl;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
return INT2NUM(SSL_pending(ssl));
}
/*
* call-seq:
* ssl.session_reused? -> true | false
*
* Returns true if a reused session was negotiated during the handshake.
*/
static VALUE
ossl_ssl_session_reused(VALUE self)
{
SSL *ssl;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
* encoding.c (rb_enc_codepoint_len): Use UNREACHABLE to avoid "control reaches end of non-void function" warnings. [ruby-trunk - Bug #6066] * re.c (name_to_backref_number): ditto. * object.c (rb_Float): ditto. * io.c (io_readpartial): ditto. * io.c (io_read_nonblock): ditto. * pack.c (rb_uv_to_utf8): ditto. * proc.c (rb_method_entry_arity): ditto. * vm_method.c (rb_f_notimplement): ditto. * struct.c (rb_struct_aset_id): ditto. * class.c (rb_scan_args): ditto. * process.c (rlimit_resource_type): ditto. * process.c (rlimit_resource_value): ditto. * process.c (p_uid_switch): ditto. * process.c (p_gid_switch): ditto. * ext/digest/digest.c (rb_digest_instance_update): ditto. * ext/digest/digest.c (rb_digest_instance_finish): ditto. * ext/digest/digest.c (rb_digest_instance_reset): ditto. * ext/digest/digest.c (rb_digest_instance_block_length): ditto. * ext/bigdecimal/bigdecimal.c (BigDecimalCmp): ditto. * ext/dl/handle.c (rb_dlhandle_close): ditto. * ext/tk/tcltklib.c (pending_exception_check0): ditto. * ext/tk/tcltklib.c (pending_exception_check1): ditto. * ext/tk/tcltklib.c (ip_cancel_eval_core): ditto. * ext/tk/tcltklib.c (lib_get_reltype_name): ditto. * ext/tk/tcltklib.c (create_dummy_encoding_for_tk_core): ditto. * ext/tk/tkutil/tkutil.c (tk_hash_kv): ditto. * ext/openssl/ossl_ssl.c (ossl_ssl_session_reused): ditto. * ext/openssl/ossl_pkey_ec.c (ossl_ec_key_dsa_verify_asn1): ditto. * ext/openssl/ossl_pkey_ec.c (ossl_ec_point_is_at_infinit): ditto. * ext/openssl/ossl_pkey_ec.c (ossl_ec_point_is_on_curve): ditto. * ext/fiddle/conversions.c (generic_to_value): ditto. * ext/socket/raddrinfo.c (rsock_io_socket_addrinfo): ditto. * ext/socket/socket.c (sock_s_getnameinfo): ditto. * ext/ripper/eventids2.c (ripper_token2eventid): ditto. * cont.c (return_fiber): ditto. * dmydln.c (dln_load): ditto. * vm_insnhelper.c (vm_search_normal_superclass): ditto. * bignum.c (big_fdiv): ditto. * marshal.c (r_symlink): ditto. * marshal.c (r_symbol): ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35321 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-04-13 19:45:37 -04:00
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
return SSL_session_reused(ssl) ? Qtrue : Qfalse;
}
/*
* call-seq:
* ssl.session = session -> session
*
* Sets the Session to be used when the connection is established.
*/
static VALUE
ossl_ssl_set_session(VALUE self, VALUE arg1)
{
SSL *ssl;
SSL_SESSION *sess;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
SafeGetSSLSession(arg1, sess);
if (SSL_set_session(ssl, sess) != 1)
ossl_raise(eSSLError, "SSL_set_session");
return arg1;
}
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
/*
* call-seq:
* ssl.hostname = hostname -> hostname
*
* Sets the server hostname used for SNI. This needs to be set before
* SSLSocket#connect.
*/
static VALUE
ossl_ssl_set_hostname(VALUE self, VALUE arg)
{
SSL *ssl;
char *hostname = NULL;
GetSSL(self, ssl);
if (!NIL_P(arg))
hostname = StringValueCStr(arg);
if (!SSL_set_tlsext_host_name(ssl, hostname))
ossl_raise(eSSLError, NULL);
/* for SSLSocket#hostname */
rb_ivar_set(self, id_i_hostname, arg);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
return arg;
}
#endif
/*
* call-seq:
* ssl.verify_result => Integer
*
* Returns the result of the peer certificates verification. See verify(1)
* for error values and descriptions.
*
* If no peer certificate was presented X509_V_OK is returned.
*/
static VALUE
ossl_ssl_get_verify_result(VALUE self)
{
SSL *ssl;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
return INT2NUM(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;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
ca = SSL_get_client_CA_list(ssl);
return ossl_x509name_sk2ary(ca);
}
# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
/*
* call-seq:
* ssl.npn_protocol => String | nil
*
* Returns the protocol string that was finally selected by the client
* during the handshake.
*/
static VALUE
ossl_ssl_npn_protocol(VALUE self)
{
SSL *ssl;
const unsigned char *out;
unsigned int outlen;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
SSL_get0_next_proto_negotiated(ssl, &out, &outlen);
if (!outlen)
return Qnil;
else
return rb_str_new((const char *) out, outlen);
}
# endif
# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
/*
* call-seq:
* ssl.alpn_protocol => String | nil
*
* Returns the ALPN protocol string that was finally selected by the server
* during the handshake.
*/
static VALUE
ossl_ssl_alpn_protocol(VALUE self)
{
SSL *ssl;
const unsigned char *out;
unsigned int outlen;
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
GetSSL(self, ssl);
SSL_get0_alpn_selected(ssl, &out, &outlen);
if (!outlen)
return Qnil;
else
return rb_str_new((const char *) out, outlen);
}
# endif
# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
/*
* call-seq:
* ssl.tmp_key => PKey or nil
*
* Returns the ephemeral key used in case of forward secrecy cipher.
*/
static VALUE
ossl_ssl_tmp_key(VALUE self)
{
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
SSL *ssl;
EVP_PKEY *key;
GetSSL(self, ssl);
if (!SSL_get_server_tmp_key(ssl, &key))
return Qnil;
return ossl_pkey_new(key);
}
# endif /* defined(HAVE_SSL_GET_SERVER_TMP_KEY) */
#endif /* !defined(OPENSSL_NO_SOCK) */
#undef rb_intern
#define rb_intern(s) rb_intern_const(s)
void
Init_ossl_ssl(void)
{
int i;
VALUE ary;
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable");
rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
#endif
ID_callback_state = rb_intern("callback_state");
ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_vcb_idx",0,0,0);
ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_store_p",0,0,0);
ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ptr_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-module: OpenSSL::ExtConfig
*
* This module contains configuration information about the SSL extension,
* for example if socket support is enabled, or the host name TLS extension
* is enabled. Constants in this module will always be defined, but contain
* `true` or `false` values depending on the configuration of your OpenSSL
* installation.
*/
mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig");
/* Document-class: OpenSSL::SSL::SSLError
*
* Generic error class raised by SSLSocket and SSLContext.
*/
eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
eSSLErrorWaitReadable = rb_define_class_under(mSSL, "SSLErrorWaitReadable", eSSLError);
rb_include_module(eSSLErrorWaitReadable, rb_mWaitReadable);
eSSLErrorWaitWritable = rb_define_class_under(mSSL, "SSLErrorWaitWritable", eSSLError);
rb_include_module(eSSLErrorWaitWritable, rb_mWaitWritable);
Init_ossl_ssl_session();
/* Document-class: OpenSSL::SSL::SSLContext
*
* An SSLContext is used to set various options regarding certificates,
* algorithms, verification, session caching, etc. The SSLContext is
* used to create an SSLSocket.
*
* All attributes must be set before creating an SSLSocket as the
* SSLContext will be frozen afterward.
*/
cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject);
rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc);
rb_undef_method(cSSLContext, "initialize_copy");
/*
* Context certificate
*/
rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse);
/*
* Context private key
*/
rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse);
/*
* A certificate or Array of certificates that will be sent to the client.
*/
rb_attr(cSSLContext, rb_intern("client_ca"), 1, 1, Qfalse);
/*
* The path to a file containing a PEM-format CA certificate
*/
rb_attr(cSSLContext, rb_intern("ca_file"), 1, 1, Qfalse);
/*
* The path to a directory containing CA certificates in PEM format.
*
* Files are looked up by subject's X509 name's hash value.
*/
rb_attr(cSSLContext, rb_intern("ca_path"), 1, 1, Qfalse);
/*
* Maximum session lifetime in seconds.
*/
rb_attr(cSSLContext, rb_intern("timeout"), 1, 1, Qfalse);
/*
* Session verification mode.
*
* Valid modes are VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE,
* VERIFY_FAIL_IF_NO_PEER_CERT and defined on OpenSSL::SSL
*
* The default mode is VERIFY_NONE, which does not perform any verification
* at all.
*
* See SSL_CTX_set_verify(3) for details.
*/
rb_attr(cSSLContext, rb_intern("verify_mode"), 1, 1, Qfalse);
/*
* Number of CA certificates to walk when verifying a certificate chain.
*/
rb_attr(cSSLContext, rb_intern("verify_depth"), 1, 1, Qfalse);
/*
* A callback for additional certificate verification. The callback is
* invoked for each certificate in the chain.
*
* The callback is invoked with two values. +preverify_ok+ indicates
* indicates if the verification was passed (true) or not (false).
* +store_context+ is an OpenSSL::X509::StoreContext containing the
* context used for certificate verification.
*
* If the callback returns false, the chain verification is immediately
* stopped and a bad_certificate alert is then sent.
*/
rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse);
/*
* Whether to check the server certificate is valid for the hostname.
*
* In order to make this work, verify_mode must be set to VERIFY_PEER and
* the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=.
*/
rb_attr(cSSLContext, rb_intern("verify_hostname"), 1, 1, Qfalse);
/*
* An OpenSSL::X509::Store used for certificate verification.
*/
rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse);
/*
* An Array of extra X509 certificates to be added to the certificate
* chain.
*/
rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse);
/*
* A callback invoked when a client certificate is requested by a server
* and no certificate has been set.
*
* The callback is invoked with a Session and must return an Array
* containing an OpenSSL::X509::Certificate and an OpenSSL::PKey. If any
* other value is returned the handshake is suspended.
*/
rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse);
#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
/*
* A callback invoked when ECDH parameters are required.
*
* The callback is invoked with the Session for the key exchange, an
* flag indicating the use of an export cipher and the keylength
* required.
*
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
* The callback is deprecated. This does not work with recent versions of
* OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead.
*/
rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse);
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
#endif
/*
* Sets the context in which a session can be reused. This allows
* sessions for multiple applications to be distinguished, for example, by
* name.
*/
rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse);
/*
* A callback invoked on a server when a session is proposed by the client
* but the session could not be found in the server's internal cache.
*
* The callback is invoked with the SSLSocket and session id. The
* callback may return a Session from an external cache.
*/
rb_attr(cSSLContext, rb_intern("session_get_cb"), 1, 1, Qfalse);
/*
* A callback invoked when a new session was negotiated.
*
* The callback is invoked with an SSLSocket. If false is returned the
* session will be removed from the internal cache.
*/
rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse);
/*
* A callback invoked when a session is removed from the internal cache.
*
* The callback is invoked with an SSLContext and a Session.
*/
rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse);
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
#else
rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qfalse);
#endif
#ifdef TLS_DH_anon_WITH_AES_256_GCM_SHA384
rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qtrue);
#else
rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", 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);
#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
/*
* An Enumerable of Strings. Each String represents a protocol to be
* advertised as the list of supported protocols for Next 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.npn_protocols = ["http/1.1", "spdy/2"]
*/
rb_attr(cSSLContext, rb_intern("npn_protocols"), 1, 1, Qfalse);
/*
* A callback invoked on the client side when the client needs to select
* a protocol from the list sent by the server. Supported in OpenSSL 1.0.1
* and higher. The client MUST select a protocol of those advertised by
* the server. 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 NPN extension on the client - any protocols
* advertised by the server will be ignored.
*
* === Example
*
* ctx.npn_select_cb = lambda do |protocols|
* # inspect the protocols and select one
* protocols.first
* end
*/
rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse);
#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.2 and higher. Has no
* effect on the server side. If not set explicitly, the ALPN extension will
* not be included 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 callback must return 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 server - any protocols
* advertised by the client 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_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);
openssl: add SSLContext#ecdh_curves= * ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-30 05:30:38 -04:00
rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
rb_define_alias(cSSLContext, "freeze", "setup");
/*
* No session caching for client or server
*/
rb_define_const(cSSLContext, "SESSION_CACHE_OFF", LONG2NUM(SSL_SESS_CACHE_OFF));
/*
* Client sessions are added to the session cache
*/
rb_define_const(cSSLContext, "SESSION_CACHE_CLIENT", LONG2NUM(SSL_SESS_CACHE_CLIENT)); /* doesn't actually do anything in 0.9.8e */
/*
* Server sessions are added to the session cache
*/
rb_define_const(cSSLContext, "SESSION_CACHE_SERVER", LONG2NUM(SSL_SESS_CACHE_SERVER));
/*
* Both client and server sessions are added to the session cache
*/
rb_define_const(cSSLContext, "SESSION_CACHE_BOTH", LONG2NUM(SSL_SESS_CACHE_BOTH)); /* no different than CACHE_SERVER in 0.9.8e */
/*
* Normally the session cache is checked for expired sessions every 255
* connections. Since this may lead to a delay that cannot be controlled,
* the automatic flushing may be disabled and #flush_sessions can be
* called explicitly.
*/
rb_define_const(cSSLContext, "SESSION_CACHE_NO_AUTO_CLEAR", LONG2NUM(SSL_SESS_CACHE_NO_AUTO_CLEAR));
/*
* Always perform external lookups of sessions even if they are in the
* internal cache.
*
* This flag has no effect on clients
*/
rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_LOOKUP", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL_LOOKUP));
/*
* Never automatically store sessions in the internal store.
*/
rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_STORE", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL_STORE));
/*
* Enables both SESSION_CACHE_NO_INTERNAL_LOOKUP and
* SESSION_CACHE_NO_INTERNAL_STORE.
*/
rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL));
rb_define_method(cSSLContext, "session_add", ossl_sslctx_session_add, 1);
rb_define_method(cSSLContext, "session_remove", ossl_sslctx_session_remove, 1);
rb_define_method(cSSLContext, "session_cache_mode", ossl_sslctx_get_session_cache_mode, 0);
rb_define_method(cSSLContext, "session_cache_mode=", ossl_sslctx_set_session_cache_mode, 1);
rb_define_method(cSSLContext, "session_cache_size", ossl_sslctx_get_session_cache_size, 0);
rb_define_method(cSSLContext, "session_cache_size=", ossl_sslctx_set_session_cache_size, 1);
rb_define_method(cSSLContext, "session_cache_stats", ossl_sslctx_get_session_cache_stats, 0);
rb_define_method(cSSLContext, "flush_sessions", ossl_sslctx_flush_sessions, -1);
rb_define_method(cSSLContext, "options", ossl_sslctx_get_options, 0);
rb_define_method(cSSLContext, "options=", ossl_sslctx_set_options, 1);
ary = rb_ary_new2(numberof(ossl_ssl_method_tab));
for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name)));
}
rb_obj_freeze(ary);
/* The list of available SSL/TLS methods */
rb_define_const(cSSLContext, "METHODS", ary);
/*
* Document-class: OpenSSL::SSL::SSLSocket
*/
cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
#ifdef OPENSSL_NO_SOCK
rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1);
#else
rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse);
rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1);
rb_undef_method(cSSLSocket, "initialize_copy");
rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0);
connect_nonblock supports "exception: false" This is for consistency with accept_nonblock arguments and gives a minor speedup from avoiding exceptions. [ruby-core:68838] [Feature #11024] * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): support `exception: false' * (get_no_exception): move function location * ext/socket/socket.c (sock_connect_nonblock): support `exception: false' * test/openssl/test_pair.rb (test_connect_accept_nonblock_no_exception): test `exception: false' on connect, rename from `test_accept_nonblock_no_exception' * test/socket/test_nonblock.rb (test_connect_nonblock_no_exception): new test Benchmark results: default 0.050000 0.100000 0.150000 ( 0.151307) exception: false 0.030000 0.080000 0.110000 ( 0.108840) ----------------------------8<----------------------- require 'socket' require 'benchmark' require 'io/wait' require 'tmpdir' host = '127.0.0.1' serv = TCPServer.new(host, 0) # UNIX sockets may not hit EINPROGRESS nr = 5000 # few iterations to avoid running out of ports addr = serv.getsockname pid = fork do begin serv.accept.close rescue => e warn "#$$: #{e.message} (#{e.class})" end while true end at_exit { Process.kill(:TERM, pid) } serv.close Benchmark.bmbm do |x| x.report("default") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin s.connect_nonblock(addr) rescue IO::WaitWritable s.wait_writable end s.close end end x.report("exception: false") do nr.times do s = Socket.new(:INET, :STREAM) s.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) case s.connect_nonblock(addr, exception: false) when :wait_writable s.wait_writable end s.close end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-04-11 21:41:51 -04:00
rb_define_method(cSSLSocket, "connect_nonblock", ossl_ssl_connect_nonblock, -1);
rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0);
accept_nonblock supports "exception: false" This is analogous to functionality found in IO#read_nonblock and IO#wait_nonblock. Raising exceptions for common failures on non-blocking servers is expensive and makes $DEBUG too noisy. Benchmark results: user system total real default 2.790000 0.870000 3.660000 ( 3.671597) exception: false 1.120000 0.800000 1.920000 ( 1.922032) exception: false (cached arg) 0.820000 0.770000 1.590000 ( 1.589267) --------------------- benchmark script ------------------------ require 'socket' require 'benchmark' require 'tmpdir' nr = 1000000 Dir.mktmpdir('nb_bench') do |path| sock_path = "#{path}/test.sock" s = UNIXServer.new(sock_path) Benchmark.bmbm do |x| x.report("default") do nr.times do begin s.accept_nonblock rescue IO::WaitReadable end end end x.report("exception: false") do nr.times do begin s.accept_nonblock(exception: false) rescue IO::WaitReadable abort "should not raise" end end end x.report("exception: false (cached arg)") do arg = { exception: false } nr.times do begin s.accept_nonblock(arg) rescue IO::WaitReadable abort "should not raise" end end end end end * ext/socket/init.c (rsock_s_accept_nonblock): support exception: false [ruby-core:66385] [Feature #10532] * ext/socket/init.c (rsock_init_socket_init): define new symbols * ext/socket/rubysocket.h: adjust prototype * ext/socket/socket.c (sock_accept_nonblock): support exception: false * ext/openssl/ossl_ssl.c (ossl_ssl_accept_nonblock): ditto * ext/socket/socket.c (Init_socket): adjust accept_nonblock definition * ext/openssl/ossl_ssl.c (Init_ossl_ssl): ditto * ext/socket/tcpserver.c (rsock_init_tcpserver): ditto * ext/socket/unixserver.c (rsock_init_unixserver): ditto * ext/socket/tcpserver.c (tcp_accept_nonblock): adjust rsock_s_accept_nonblock call * ext/socket/unixserver.c (unix_accept_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_start_ssl): support no_exception * ext/openssl/ossl_ssl.c (ossl_ssl_connect): adjust ossl_start_ssl call * ext/openssl/ossl_ssl.c (ossl_ssl_connect_nonblock): ditto * ext/openssl/ossl_ssl.c (ossl_ssl_accept): ditto * test/socket/test_nonblock.rb (test_accept_nonblock): test for "exception :false" * test/socket/test_tcp.rb (test_accept_nonblock): new test * test/socket/test_unix.rb (test_accept_nonblock): ditto * test/openssl/test_pair.rb (test_accept_nonblock_no_exception): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-03-11 23:03:04 -04:00
rb_define_method(cSSLSocket, "accept_nonblock", ossl_ssl_accept_nonblock, -1);
rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1);
rb_define_private_method(cSSLSocket, "sysread_nonblock", ossl_ssl_read_nonblock, -1);
rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1);
rb_define_private_method(cSSLSocket, "syswrite_nonblock", ossl_ssl_write_nonblock, -1);
rb_define_private_method(cSSLSocket, "stop", ossl_ssl_stop, 0);
rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0);
rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0);
rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0);
rb_define_method(cSSLSocket, "ssl_version", ossl_ssl_get_version, 0);
rb_define_method(cSSLSocket, "cipher", ossl_ssl_get_cipher, 0);
rb_define_method(cSSLSocket, "state", ossl_ssl_get_state, 0);
rb_define_method(cSSLSocket, "pending", ossl_ssl_pending, 0);
rb_define_method(cSSLSocket, "session_reused?", ossl_ssl_session_reused, 0);
/* implementation of OpenSSL::SSL::SSLSocket#session is in lib/openssl/ssl.rb */
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);
openssl: move SSLSocket#initialize to C extension * ext/openssl/lib/openssl/ssl.rb (SSLSocket): Move the implementation of SSLSocket#initialize to C. Initialize the SSL (OpenSSL object) in it. Currently this is delayed until ossl_ssl_setup(), which is called from SSLSocket#accept or #connect. Say we call SSLSocket#hostname= with an illegal value. We expect an exception to be raised in #hostname= but actually we get it in the later SSLSocket#connect. Because the SSL is not ready at #hostname=, the actual call of SSL_set_tlsext_host_name() is also delayed. This also fixes: [ruby-dev:49376] [Bug #11724] * ext/openssl/ossl_ssl.c (ossl_ssl_initialize): Added. Almost the same as the Ruby version but this instantiate the SSL object at the same time. (ossl_ssl_setup): Adjust to the changes. Just set the underlying IO to the SSL. (ssl_started): Added. Make use of SSL_get_fd(). This returns -1 if not yet set by SSL_set_fd(). (ossl_ssl_data_get_struct): Removed. Now GetSSL() checks that the SSL exists. (ossl_ssl_set_session): Don't call ossl_ssl_setup() here as now the SSL is already instantiated in #initialize. (ossl_ssl_shutdown, ossl_start_ssl, ossl_ssl_read_internal, ossl_ssl_write_internal, ossl_ssl_stop, ossl_ssl_get_cert, ossl_ssl_get_peer_cert, ossl_ssl_get_peer_cert_chain, ossl_ssl_get_version, ossl_ssl_get_cipher, ossl_ssl_get_state, ossl_ssl_pending, ossl_ssl_session_reused, ossl_ssl_get_verify_result, ossl_ssl_get_client_ca_list, ossl_ssl_npn_protocol, ossl_ssl_alpn_protocol, ossl_ssl_tmp_key): Use GetSSL() instead of ossl_ssl_data_get_struct(). Use ssl_started(). (Init_ossl_ssl): Add method declarations of SSLSocket#{initialize, hostname=}. * ext/openssl/ossl_ssl.h (GetSSL): Check that the SSL is not NULL. It should not be NULL because we now set it in #initialize. * ext/openssl/ossl_ssl_session.c (ossl_ssl_session_initialize): No need to check if the SSL is NULL. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55191 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-28 01:00:36 -04:00
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
/* #hostname is defined in lib/openssl/ssl.rb */
rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1);
#endif
# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
# endif
# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
# endif
# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
# endif
#endif
#define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, LONG2NUM(SSL_##x))
ossl_ssl_def_const(VERIFY_NONE);
ossl_ssl_def_const(VERIFY_PEER);
ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT);
ossl_ssl_def_const(VERIFY_CLIENT_ONCE);
/* Introduce constants included in OP_ALL. These constants are mostly for
* unset some bits in OP_ALL such as;
* ctx.options = OP_ALL & ~OP_DONT_INSERT_EMPTY_FRAGMENTS
*/
ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG);
ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG);
ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG);
ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER);
ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING);
ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG);
ossl_ssl_def_const(OP_TLS_D5_BUG);
ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG);
ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS);
ossl_ssl_def_const(OP_ALL);
ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
ossl_ssl_def_const(OP_SINGLE_ECDH_USE);
ossl_ssl_def_const(OP_SINGLE_DH_USE);
ossl_ssl_def_const(OP_EPHEMERAL_RSA);
ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE);
ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG);
ossl_ssl_def_const(OP_NO_SSLv2);
ossl_ssl_def_const(OP_NO_SSLv3);
ossl_ssl_def_const(OP_NO_TLSv1);
#if defined(SSL_OP_NO_TLSv1_1)
ossl_ssl_def_const(OP_NO_TLSv1_1);
#endif
#if defined(SSL_OP_NO_TLSv1_2)
ossl_ssl_def_const(OP_NO_TLSv1_2);
#endif
#if defined(SSL_OP_NO_TICKET)
ossl_ssl_def_const(OP_NO_TICKET);
#endif
#if defined(SSL_OP_NO_COMPRESSION)
ossl_ssl_def_const(OP_NO_COMPRESSION);
#endif
ossl_ssl_def_const(OP_PKCS1_CHECK_1);
ossl_ssl_def_const(OP_PKCS1_CHECK_2);
ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG);
ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
sym_exception = ID2SYM(rb_intern("exception"));
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
id_tmp_dh_callback = rb_intern("tmp_dh_callback");
id_tmp_ecdh_callback = rb_intern("tmp_ecdh_callback");
id_npn_protocols_encoded = rb_intern("npn_protocols_encoded");
#define DefIVarID(name) do \
id_i_##name = rb_intern("@"#name); while (0)
DefIVarID(cert_store);
DefIVarID(ca_file);
DefIVarID(ca_path);
DefIVarID(verify_mode);
DefIVarID(verify_depth);
DefIVarID(verify_callback);
DefIVarID(client_ca);
DefIVarID(renegotiation_cb);
DefIVarID(cert);
DefIVarID(key);
DefIVarID(extra_chain_cert);
DefIVarID(client_cert_cb);
DefIVarID(tmp_ecdh_callback);
DefIVarID(timeout);
DefIVarID(session_id_context);
DefIVarID(session_get_cb);
DefIVarID(session_new_cb);
DefIVarID(session_remove_cb);
DefIVarID(npn_select_cb);
DefIVarID(npn_protocols);
DefIVarID(alpn_protocols);
DefIVarID(alpn_select_cb);
DefIVarID(servername_cb);
DefIVarID(verify_hostname);
DefIVarID(io);
DefIVarID(context);
DefIVarID(hostname);
}