[ruby/openssl] ssl: separate SSLContext#min_version= and #max_version=
Make these methods simple wrappers around SSL_CTX_set_{min,max}_proto_version(). When we introduced these methods in commit https://github.com/ruby/openssl/commit/18603949d316 [1], which went to v2.1.0, we added a private method to SSLContext that set both the minimum and maximum protocol versions at the same time. This was to allow emulating the behavior using SSL options on older OpenSSL versions that lack SSL_CTX_set_{min,max}_proto_version(). Since we no longer support OpenSSL 1.0.2, the related code has already been removed. In OpenSSL 1.1.1 or later, setting the minimum or maximum version to 0 is not equivalent to leaving it unset. Similar to SSL options, which we avoid overwriting as of commit https://github.com/ruby/openssl/commit/00bec0d905d5 and commit https://github.com/ruby/openssl/commit/77c3db2d6587 [2], a system-wide configuration file may define a default protocol version bounds. Setting the minimum version should not unset the maximum version, and vice versa. [1] https://github.com/ruby/openssl/pull/142 [2] https://github.com/ruby/openssl/pull/767 https://github.com/ruby/openssl/commit/5766386321
This commit is contained in:
parent
8cbff4fe45
commit
5a14f53695
@ -153,43 +153,6 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
|
||||
return params
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
# ctx.min_version = :TLS1_2
|
||||
# ctx.min_version = nil
|
||||
#
|
||||
# Sets the lower bound on the supported SSL/TLS protocol version. The
|
||||
# version may be specified by an integer constant named
|
||||
# OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
|
||||
#
|
||||
# Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
|
||||
# options by #options= once you have called #min_version= or
|
||||
# #max_version=.
|
||||
#
|
||||
# === Example
|
||||
# ctx = OpenSSL::SSL::SSLContext.new
|
||||
# ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
|
||||
# ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
#
|
||||
# sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
|
||||
# sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
|
||||
def min_version=(version)
|
||||
set_minmax_proto_version(version, @max_proto_version ||= nil)
|
||||
@min_proto_version = version
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
# ctx.max_version = :TLS1_2
|
||||
# ctx.max_version = nil
|
||||
#
|
||||
# Sets the upper bound of the supported SSL/TLS protocol version. See
|
||||
# #min_version= for the possible values.
|
||||
def max_version=(version)
|
||||
set_minmax_proto_version(@min_proto_version ||= nil, version)
|
||||
@max_proto_version = version
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# ctx.ssl_version = :TLSv1
|
||||
# ctx.ssl_version = "SSLv23"
|
||||
@ -214,8 +177,7 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
|
||||
end
|
||||
version = METHODS_MAP[meth.intern] or
|
||||
raise ArgumentError, "unknown SSL method `%s'" % meth
|
||||
set_minmax_proto_version(version, version)
|
||||
@min_proto_version = @max_proto_version = version
|
||||
self.min_version = self.max_version = version
|
||||
end
|
||||
|
||||
METHODS_MAP = {
|
||||
|
@ -96,61 +96,6 @@ ossl_sslctx_s_alloc(VALUE klass)
|
||||
return obj;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_proto_version(VALUE str)
|
||||
{
|
||||
int i;
|
||||
static const struct {
|
||||
const char *name;
|
||||
int version;
|
||||
} map[] = {
|
||||
{ "SSL2", SSL2_VERSION },
|
||||
{ "SSL3", SSL3_VERSION },
|
||||
{ "TLS1", TLS1_VERSION },
|
||||
{ "TLS1_1", TLS1_1_VERSION },
|
||||
{ "TLS1_2", TLS1_2_VERSION },
|
||||
{ "TLS1_3", TLS1_3_VERSION },
|
||||
};
|
||||
|
||||
if (NIL_P(str))
|
||||
return 0;
|
||||
if (RB_INTEGER_TYPE_P(str))
|
||||
return NUM2INT(str);
|
||||
|
||||
if (SYMBOL_P(str))
|
||||
str = rb_sym2str(str);
|
||||
StringValue(str);
|
||||
for (i = 0; i < numberof(map); i++)
|
||||
if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str)))
|
||||
return map[i].version;
|
||||
rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ctx.set_minmax_proto_version(min, max) -> nil
|
||||
*
|
||||
* Sets the minimum and maximum supported protocol versions. See #min_version=
|
||||
* and #max_version=.
|
||||
*/
|
||||
static VALUE
|
||||
ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v)
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
int min, max;
|
||||
|
||||
GetSSLCTX(self, ctx);
|
||||
min = parse_proto_version(min_v);
|
||||
max = parse_proto_version(max_v);
|
||||
|
||||
if (!SSL_CTX_set_min_proto_version(ctx, min))
|
||||
ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
|
||||
if (!SSL_CTX_set_max_proto_version(ctx, max))
|
||||
ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ossl_call_client_cert_cb(VALUE obj)
|
||||
{
|
||||
@ -915,6 +860,93 @@ ossl_sslctx_setup(VALUE self)
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_proto_version(VALUE str)
|
||||
{
|
||||
int i;
|
||||
static const struct {
|
||||
const char *name;
|
||||
int version;
|
||||
} map[] = {
|
||||
{ "SSL2", SSL2_VERSION },
|
||||
{ "SSL3", SSL3_VERSION },
|
||||
{ "TLS1", TLS1_VERSION },
|
||||
{ "TLS1_1", TLS1_1_VERSION },
|
||||
{ "TLS1_2", TLS1_2_VERSION },
|
||||
{ "TLS1_3", TLS1_3_VERSION },
|
||||
};
|
||||
|
||||
if (NIL_P(str))
|
||||
return 0;
|
||||
if (RB_INTEGER_TYPE_P(str))
|
||||
return NUM2INT(str);
|
||||
|
||||
if (SYMBOL_P(str))
|
||||
str = rb_sym2str(str);
|
||||
StringValue(str);
|
||||
for (i = 0; i < numberof(map); i++)
|
||||
if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str)))
|
||||
return map[i].version;
|
||||
rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
* ctx.min_version = :TLS1_2
|
||||
* ctx.min_version = nil
|
||||
*
|
||||
* Sets the lower bound on the supported SSL/TLS protocol version. The
|
||||
* version may be specified by an integer constant named
|
||||
* OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
|
||||
*
|
||||
* === Example
|
||||
* ctx = OpenSSL::SSL::SSLContext.new
|
||||
* ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
|
||||
* ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
*
|
||||
* sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
|
||||
* sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
|
||||
*/
|
||||
static VALUE
|
||||
ossl_sslctx_set_min_version(VALUE self, VALUE v)
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
int version;
|
||||
|
||||
rb_check_frozen(self);
|
||||
GetSSLCTX(self, ctx);
|
||||
version = parse_proto_version(v);
|
||||
|
||||
if (!SSL_CTX_set_min_proto_version(ctx, version))
|
||||
ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
* ctx.max_version = :TLS1_2
|
||||
* ctx.max_version = nil
|
||||
*
|
||||
* Sets the upper bound of the supported SSL/TLS protocol version. See
|
||||
* #min_version= for the possible values.
|
||||
*/
|
||||
static VALUE
|
||||
ossl_sslctx_set_max_version(VALUE self, VALUE v)
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
int version;
|
||||
|
||||
rb_check_frozen(self);
|
||||
GetSSLCTX(self, ctx);
|
||||
version = parse_proto_version(v);
|
||||
|
||||
if (!SSL_CTX_set_max_proto_version(ctx, version))
|
||||
ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
|
||||
return v;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ossl_ssl_cipher_to_ary(const SSL_CIPHER *cipher)
|
||||
{
|
||||
@ -2846,8 +2878,8 @@ Init_ossl_ssl(void)
|
||||
|
||||
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
|
||||
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
|
||||
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
|
||||
ossl_sslctx_set_minmax_proto_version, 2);
|
||||
rb_define_method(cSSLContext, "min_version=", ossl_sslctx_set_min_version, 1);
|
||||
rb_define_method(cSSLContext, "max_version=", ossl_sslctx_set_max_version, 1);
|
||||
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
|
||||
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
|
||||
rb_define_method(cSSLContext, "ciphersuites=", ossl_sslctx_set_ciphersuites, 1);
|
||||
|
@ -1375,6 +1375,50 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
|
||||
}
|
||||
end
|
||||
|
||||
def test_minmax_version_system_default
|
||||
omit "LibreSSL does not support OPENSSL_CONF" if libressl?
|
||||
|
||||
Tempfile.create("openssl.cnf") { |f|
|
||||
f.puts(<<~EOF)
|
||||
openssl_conf = default_conf
|
||||
[default_conf]
|
||||
ssl_conf = ssl_sect
|
||||
[ssl_sect]
|
||||
system_default = ssl_default_sect
|
||||
[ssl_default_sect]
|
||||
MaxProtocol = TLSv1.2
|
||||
EOF
|
||||
f.close
|
||||
|
||||
start_server(ignore_listener_error: true) do |port|
|
||||
assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
|
||||
sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.connect
|
||||
assert_equal("TLSv1.2", ssl.ssl_version)
|
||||
ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
|
||||
ssl.close
|
||||
end;
|
||||
|
||||
assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
|
||||
sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
||||
ctx.max_version = nil
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.connect
|
||||
assert_equal("TLSv1.3", ssl.ssl_version)
|
||||
ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
|
||||
ssl.close
|
||||
end;
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_options_disable_versions
|
||||
# It's recommended to use SSLContext#{min,max}_version= instead in real
|
||||
# applications. The purpose of this test case is to check that SSL options
|
||||
|
Loading…
x
Reference in New Issue
Block a user