Skip to content

Commit 7c4028a

Browse files
committed
TLS Fallback Signaling Cipher Suite Value
Support for fallback SCSV [RFC 7507](https://tools.ietf.org/html/rfc7507). Expected behaviour is to refuse connection if the client signals a protocol with the fallback flag but the server supports a better one (downgrade attack detection).
1 parent d834e86 commit 7c4028a

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,26 @@ ossl_sslctx_set_security_level(VALUE self, VALUE value)
11931193
return value;
11941194
}
11951195

1196+
#ifdef SSL_MODE_SEND_FALLBACK_SCSV
1197+
/*
1198+
* call-seq:
1199+
* ctx.enable_fallback_scsv() => nil
1200+
*
1201+
* Activate TLS_FALLBACK_SCSV for this context.
1202+
* See RFC 7507.
1203+
*/
1204+
static VALUE
1205+
ossl_sslctx_enable_fallback_scsv(VALUE self)
1206+
{
1207+
SSL_CTX *ctx;
1208+
1209+
GetSSLCTX(self, ctx);
1210+
SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV);
1211+
1212+
return Qnil;
1213+
}
1214+
#endif
1215+
11961216
/*
11971217
* call-seq:
11981218
* ctx.session_add(session) -> true | false
@@ -2558,6 +2578,9 @@ Init_ossl_ssl(void)
25582578
rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
25592579
rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
25602580
rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);
2581+
#ifdef SSL_MODE_SEND_FALLBACK_SCSV
2582+
rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0);
2583+
#endif
25612584

25622585
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
25632586
rb_define_alias(cSSLContext, "freeze", "setup");

test/test_ssl.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,59 @@ def test_get_ephemeral_key
12221222
end
12231223
end
12241224

1225+
def test_fallback_scsv
1226+
pend "Fallback SCSV is not supported" unless OpenSSL::SSL::SSLContext.method_defined?( :enable_fallback_scsv)
1227+
1228+
start_server do |port|
1229+
ctx = OpenSSL::SSL::SSLContext.new
1230+
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
1231+
# Here is OK
1232+
# TLS1.2 supported and this is what we ask the first time
1233+
server_connect(port, ctx)
1234+
end
1235+
1236+
ctx_proc = proc { |ctx|
1237+
ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
1238+
}
1239+
start_server(ctx_proc: ctx_proc) do |port|
1240+
ctx = OpenSSL::SSL::SSLContext.new
1241+
ctx.enable_fallback_scsv
1242+
ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
1243+
# Here is OK too
1244+
# TLS1.2 not supported, fallback to TLS1.1 and signaling the fallback
1245+
# Server doesn't support better, so connection OK
1246+
server_connect(port, ctx)
1247+
end
1248+
1249+
# Here is not OK
1250+
# TLS1.2 is supported, fallback to TLS1.1 (downgrade attack) and signaling the fallback
1251+
# Server support better, so refuse the connection
1252+
sock1, sock2 = socketpair
1253+
begin
1254+
ctx1 = OpenSSL::SSL::SSLContext.new
1255+
s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
1256+
1257+
ctx2 = OpenSSL::SSL::SSLContext.new
1258+
ctx2.enable_fallback_scsv
1259+
ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION
1260+
s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
1261+
t = Thread.new {
1262+
assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
1263+
s2.connect
1264+
}
1265+
}
1266+
1267+
assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
1268+
s1.accept
1269+
}
1270+
1271+
assert t.join
1272+
ensure
1273+
sock1.close
1274+
sock2.close
1275+
end
1276+
end
1277+
12251278
def test_dh_callback
12261279
pend "TLS 1.2 is not supported" unless tls12_supported?
12271280

0 commit comments

Comments
 (0)