Skip to content

Commit ae15316

Browse files
committed
ssl: rework SSLContext#ssl_version=
Reimplement SSLContext#ssl_version= as a wrapper around SSLContext#min_version= and #max_version=. SSLContext#ssl_version= used to call SSL_CTX_set_ssl_version() which replaces the SSL method used for the connections created from the SSL context. This is mainly used for forcing a specific SSL/TLS protocol version. As of OpenSSL 1.1.0, however, use of the version-specific SSL methods such as TLSv1_method() is deprecated. Follow the current recommendation -- to use the generic SSL method always and to control the supported version range by SSL_CTX_set_{min,max}_proto_version(). Actually, we have already started doing a similar thing when the extension is compiled with OpenSSL 1.1.0. OpenSSL::SSL::SSLContext::METHODS, which contained the possible names of SSL methods, is not useful anymore. It is now deprecate_constant-ed.
1 parent 7446fee commit ae15316

File tree

2 files changed

+46
-95
lines changed

2 files changed

+46
-95
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -46,44 +46,6 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
4646
id_i_verify_hostname;
4747
static ID id_i_io, id_i_context, id_i_hostname;
4848

49-
/*
50-
* SSLContext class
51-
*/
52-
static const struct {
53-
const char *name;
54-
const SSL_METHOD *(*func)(void);
55-
int version;
56-
} ossl_ssl_method_tab[] = {
57-
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
58-
#define OSSL_SSL_METHOD_ENTRY(name, version) \
59-
{ #name, TLS_method, version }, \
60-
{ #name"_server", TLS_server_method, version }, \
61-
{ #name"_client", TLS_client_method, version }
62-
#else
63-
#define OSSL_SSL_METHOD_ENTRY(name, version) \
64-
{ #name, name##_method, version }, \
65-
{ #name"_server", name##_server_method, version }, \
66-
{ #name"_client", name##_client_method, version }
67-
#endif
68-
#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL2_METHOD) && defined(HAVE_SSLV2_METHOD)
69-
OSSL_SSL_METHOD_ENTRY(SSLv2, SSL2_VERSION),
70-
#endif
71-
#if !defined(OPENSSL_NO_SSL3) && !defined(OPENSSL_NO_SSL3_METHOD) && defined(HAVE_SSLV3_METHOD)
72-
OSSL_SSL_METHOD_ENTRY(SSLv3, SSL3_VERSION),
73-
#endif
74-
#if !defined(OPENSSL_NO_TLS1) && !defined(OPENSSL_NO_TLS1_METHOD)
75-
OSSL_SSL_METHOD_ENTRY(TLSv1, TLS1_VERSION),
76-
#endif
77-
#if !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD)
78-
OSSL_SSL_METHOD_ENTRY(TLSv1_1, TLS1_1_VERSION),
79-
#endif
80-
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2_METHOD)
81-
OSSL_SSL_METHOD_ENTRY(TLSv1_2, TLS1_2_VERSION),
82-
#endif
83-
OSSL_SSL_METHOD_ENTRY(SSLv23, 0),
84-
#undef OSSL_SSL_METHOD_ENTRY
85-
};
86-
8749
static int ossl_ssl_ex_vcb_idx;
8850
static int ossl_ssl_ex_ptr_idx;
8951
static int ossl_sslctx_ex_ptr_idx;
@@ -148,51 +110,6 @@ ossl_sslctx_s_alloc(VALUE klass)
148110
return obj;
149111
}
150112

151-
/*
152-
* call-seq:
153-
* ctx.ssl_version = :TLSv1
154-
* ctx.ssl_version = "SSLv23_client"
155-
*
156-
* Sets the SSL/TLS protocol version for the context. This forces connections to
157-
* use only the specified protocol version.
158-
*
159-
* You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS
160-
*/
161-
static VALUE
162-
ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method)
163-
{
164-
SSL_CTX *ctx;
165-
const char *s;
166-
VALUE m = ssl_method;
167-
int i;
168-
169-
GetSSLCTX(self, ctx);
170-
if (RB_TYPE_P(ssl_method, T_SYMBOL))
171-
m = rb_sym2str(ssl_method);
172-
s = StringValueCStr(m);
173-
for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
174-
if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) {
175-
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
176-
int version = ossl_ssl_method_tab[i].version;
177-
#endif
178-
const SSL_METHOD *method = ossl_ssl_method_tab[i].func();
179-
180-
if (SSL_CTX_set_ssl_version(ctx, method) != 1)
181-
ossl_raise(eSSLError, "SSL_CTX_set_ssl_version");
182-
183-
#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
184-
if (!SSL_CTX_set_min_proto_version(ctx, version))
185-
ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
186-
if (!SSL_CTX_set_max_proto_version(ctx, version))
187-
ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
188-
#endif
189-
return ssl_method;
190-
}
191-
}
192-
193-
ossl_raise(rb_eArgError, "unknown SSL method `%"PRIsVALUE"'.", m);
194-
}
195-
196113
static int
197114
parse_proto_version(VALUE str)
198115
{
@@ -2322,9 +2239,6 @@ ossl_ssl_tmp_key(VALUE self)
23222239
void
23232240
Init_ossl_ssl(void)
23242241
{
2325-
int i;
2326-
VALUE ary;
2327-
23282242
#if 0
23292243
mOSSL = rb_define_module("OpenSSL");
23302244
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -2621,7 +2535,6 @@ Init_ossl_ssl(void)
26212535

26222536
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
26232537
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
2624-
rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1);
26252538
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
26262539
ossl_sslctx_set_minmax_proto_version, 2);
26272540
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
@@ -2691,14 +2604,6 @@ Init_ossl_ssl(void)
26912604
rb_define_method(cSSLContext, "options", ossl_sslctx_get_options, 0);
26922605
rb_define_method(cSSLContext, "options=", ossl_sslctx_set_options, 1);
26932606

2694-
ary = rb_ary_new2(numberof(ossl_ssl_method_tab));
2695-
for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
2696-
rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name)));
2697-
}
2698-
rb_obj_freeze(ary);
2699-
/* The list of available SSL/TLS methods */
2700-
rb_define_const(cSSLContext, "METHODS", ary);
2701-
27022607
/*
27032608
* Document-class: OpenSSL::SSL::SSLSocket
27042609
*/

lib/openssl/ssl.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,52 @@ def max_version=(version)
198198
set_minmax_proto_version(@min_proto_version ||= nil, version)
199199
@max_proto_version = version
200200
end
201+
202+
# call-seq:
203+
# ctx.ssl_version = :TLSv1
204+
# ctx.ssl_version = "SSLv23"
205+
#
206+
# Sets the SSL/TLS protocol version for the context. This forces
207+
# connections to use only the specified protocol version. You can get a
208+
# list of valid versions with OpenSSL::SSL::SSLContext::METHODS.
209+
#
210+
# As the name hints, this used to call SSL_CTX_set_ssl_version() function
211+
# which sets an SSL_METHOD to be used for connections created from the
212+
# SSL_CTX.
213+
#
214+
# In OpenSSL 1.1.0, all version-specific SSL methods are deprecated, and
215+
# now this is implemented as a wrapper around #min_version= and
216+
# #max_version=. New applications should use them directly instead.
217+
def ssl_version=(meth)
218+
meth = meth.to_s if meth.is_a?(Symbol)
219+
if /_(?<type>client|server)\z/ =~ meth
220+
meth = $`
221+
if $VERBOSE
222+
warn "#{caller(1)[0]}: method type #{type.inspect} is ignored"
223+
end
224+
end
225+
version = METHODS_VERSION_MAP[meth.intern] or
226+
raise ArgumentError, "invalid SSL method name %p" % meth
227+
set_minmax_proto_version(version, version)
228+
@min_proto_version = @max_proto_version = version
229+
end
230+
231+
METHODS_VERSION_MAP = {
232+
SSLv23: 0,
233+
TLS: 0,
234+
SSLv2: OpenSSL::SSL::SSL2_VERSION,
235+
SSLv3: OpenSSL::SSL::SSL3_VERSION,
236+
TLSv1: OpenSSL::SSL::TLS1_VERSION,
237+
TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
238+
TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
239+
}.freeze
240+
private_constant :METHODS_VERSION_MAP
241+
242+
# :nodoc:
243+
METHODS = METHODS_VERSION_MAP.keys.flat_map { |name|
244+
[name, :"#{name}_client", :"#{name}_server"]
245+
}.freeze
246+
deprecate_constant :METHODS
201247
end
202248

203249
module SocketForwarder

0 commit comments

Comments
 (0)