File 0758-ssl-Allow-any-03-XX-TLS-record-version-in-client-hel.patch of Package erlang

From d70a53ced9a46af43bb42213e9b5706857620698 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Thu, 18 Nov 2021 12:19:35 +0100
Subject: [PATCH 1/2] ssl: Allow any {03,XX} TLS record version in client hello
 for maximum interoperability

closes #5380
---
 lib/ssl/src/tls_gen_connection.erl |  24 ++-
 lib/ssl/test/Makefile              |   1 +
 lib/ssl/test/ssl_reject_SUITE.erl  | 275 +++++++++++++++++++++++++++++
 3 files changed, 294 insertions(+), 6 deletions(-)
 create mode 100644 lib/ssl/test/ssl_reject_SUITE.erl

diff --git a/lib/ssl/src/tls_gen_connection.erl b/lib/ssl/src/tls_gen_connection.erl
index 59d30d7cb0..1d2bc122ef 100644
--- a/lib/ssl/src/tls_gen_connection.erl
+++ b/lib/ssl/src/tls_gen_connection.erl
@@ -442,15 +442,23 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
 
 send_alert(Alert, #state{static_env = #static_env{socket = Socket,
                                                   transport_cb = Transport},
-                         connection_env = #connection_env{negotiated_version = Version},
-                         ssl_options = #{log_level := LogLevel},
+                         connection_env = #connection_env{negotiated_version = Version0},
+                         ssl_options = #{log_level := LogLevel,
+                                         versions := Versions},
                          connection_states = ConnectionStates0} = StateData0) ->
+    Version = available_version(Version0, Versions),
     {BinMsg, ConnectionStates} =
         encode_alert(Alert, Version, ConnectionStates0),
     tls_socket:send(Transport, Socket, BinMsg),
     ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
     StateData0#state{connection_states = ConnectionStates}.
 
+available_version(undefined, Versions) ->
+    [Version| _] = lists:reverse(Versions),
+    Version;
+available_version(NegotiatedVersion, _) ->
+    NegotiatedVersion.
+
 %% If an ALERT sent in the connection state, should cause the TLS
 %% connection to end, we need to synchronize with the tls_sender
 %% process so that the ALERT if possible (that is the tls_sender process is
@@ -539,13 +547,17 @@ next_tls_record(Data, StateName,
         %% After the version is negotiated all subsequent TLS records shall have
         %% the proper legacy_record_version (= negotiated_version).
         %% Note: TLS record version {3,4} is used internally in TLS 1.3 and at this
-        %% point it is the same as the negotiated protocol version.
-        %% TODO: Refactor state machine and introduce a record_protocol_version beside
-        %% the negotiated_version.
+        %% point it is the same as the negotiated protocol version. TLS-1.3
+        %% uses TLS-1.2 as record version.
         case StateName of
             State when State =:= hello orelse
                        State =:= start ->
-                [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS];
+                %% Allow any {03,XX} TLS record version for the hello message
+                %% for maximum interopability and compliance with TLS-1.2 spec.
+                %% This does not allow SSL-3.0 connections, that we do not support
+                %% or interfere with TLS-1.3 extensions to handle version negotiation.
+                AllHelloVersions = [ 'sslv3' | ?ALL_AVAILABLE_VERSIONS],
+                [tls_record:protocol_version(Vsn) || Vsn <- AllHelloVersions];
             _ ->
                 State0#state.connection_env#connection_env.negotiated_version
         end,
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index e37fc04dee..3866e275ec 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -56,6 +56,7 @@ MODULES = \
 	openssl_sni_SUITE\
 	ssl_mfl_SUITE\
 	openssl_mfl_SUITE\
+	ssl_reject_SUITE\
         ssl_renegotiate_SUITE\
 	openssl_renegotiate_SUITE\
 	openssl_reject_SUITE\
diff --git a/lib/ssl/test/ssl_reject_SUITE.erl b/lib/ssl/test/ssl_reject_SUITE.erl
new file mode 100644
index 0000000000..9ac7c47d66
--- /dev/null
+++ b/lib/ssl/test/ssl_reject_SUITE.erl
@@ -0,0 +1,275 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2021-2021. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssl_reject_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssl/src/ssl_record.hrl").
+-include_lib("ssl/src/ssl_alert.hrl").
+-include_lib("ssl/src/ssl_handshake.hrl").
+
+%% Common test
+-export([all/0,
+         groups/0,
+         init_per_suite/1,
+         init_per_group/2,
+         init_per_testcase/2,
+         end_per_suite/1,
+         end_per_group/2,
+         end_per_testcase/2
+        ]).
+
+%% Test cases
+-export([reject_sslv2/0,
+         reject_sslv2/1,
+         reject_sslv3/0,
+         reject_sslv3/1,
+         accept_sslv3_record_hello/0,
+         accept_sslv3_record_hello/1
+        ]).
+
+-define(TLS_MAJOR, 3).
+-define(SSL_3_0_MAJOR, 3).
+-define(SSL_3_0_MINOR, 0).
+-define(TLS_1_0_MINOR, 1).
+-define(TLS_1_1_MINOR, 2).
+-define(TLS_1_2_MINOR, 3).
+-define(TLS_1_3_MINOR, 4).
+-define(SSL_2_0_MAJOR, 0).
+-define(SSL_2_0_MINOR, 1).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+    [
+     {group, 'tlsv1.3'},
+     {group, 'tlsv1.2'},
+     {group, 'tlsv1.1'},
+     {group, 'tlsv1'}
+     ].
+
+groups() ->
+    [{'tlsv1.3', [], all_versions_tests()},
+     {'tlsv1.2', [], all_versions_tests()},
+     {'tlsv1.1', [], all_versions_tests()},
+     {'tlsv1', [], all_versions_tests()}
+    ].
+
+all_versions_tests() ->
+    [
+     reject_sslv2,
+     reject_sslv3,
+     accept_sslv3_record_hello
+    ].
+
+init_per_suite(Config0) ->
+    catch crypto:stop(),
+    try crypto:start() of
+        ok ->
+            ssl_test_lib:clean_start(),
+            ssl_test_lib:make_rsa_cert(Config0)
+    catch _:_  ->
+            {skip, "Crypto did not start"}
+    end.
+
+end_per_suite(_Config) ->
+    ssl:stop(),
+    application:stop(crypto),
+    application:stop(ssl).
+
+init_per_group(GroupName, Config) ->
+    ssl_test_lib:init_per_group(GroupName, Config).
+
+end_per_group(GroupName, Config) ->
+    ssl_test_lib:end_per_group(GroupName, Config).
+
+init_per_testcase(_TestCase, Config) ->
+    ct:timetrap({seconds, 10}),
+    Config.
+
+end_per_testcase(_, Config) ->
+    Config.
+
+%%--------------------------------------------------------------------
+%% Test cases  -----------------------------------
+%%--------------------------------------------------------------------
+
+reject_sslv2() ->
+    [{doc,"Test that SSL v2 clients are rejected"}].
+
+reject_sslv2(Config) when is_list(Config) ->
+    ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+    Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+                                              {from, self()},
+                                              {options, ServerOpts}]),
+    Port = ssl_test_lib:inet_port(Server),
+
+    %% SSL-2.0 Hello 
+    ClientHello = <<128,43,?CLIENT_HELLO, ?SSL_2_0_MAJOR, ?SSL_2_0_MINOR,
+                    0,18,0,0,0,16,7,0,192,3,0,128,1,0,128,6,0,64,4,0,
+                    128,2,0,128,115,245,33,148,17,175,69,226,204,214,132,216,182,
+                    41,238,196>>,
+    
+    {ok, Socket} = gen_tcp:connect(Hostname, Port, [{active, false}]),
+
+    gen_tcp:send(Socket, ClientHello),
+
+    %% v2 is considered total garbage
+    ssl_test_lib:check_server_alert(Server, unexpected_message),
+    client_rejected(Socket, unexpected_message).
+
+reject_sslv3() ->
+    [{doc,"Test that SSL v3 clients are rejected"}].
+
+reject_sslv3(Config) when is_list(Config) ->
+    Version = proplists:get_value(version, Config),
+    ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+    Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+                                              {from, self()},
+                                              {options, ServerOpts}]),
+    Port = ssl_test_lib:inet_port(Server),
+
+    %% SSL-3.0 Hello
+    ClientHello =
+        <<?HANDSHAKE, ?SSL_3_0_MAJOR, ?SSL_3_0_MINOR,0,162, ?CLIENT_HELLO, 0,0,158, 
+          ?TLS_MAJOR, ?SSL_3_0_MINOR, 97,160,130,59,226,182,64,143,134,112,117,
+          64,10,57,164,101,182,215,0,199,145,232,172,194,45,242,48,176,5,153,
+          101,54,0,0,26,0,255,192,10,192,20,192,5,192,15,192,9,192,19,192,4,192,
+          14,0,57,0,56,0,51,0,50,1,0,0,91,0,0,0,19,0,17,0,0,14,119,119,119,46,101,
+          114,108,97,110,103,46,111,114,103,0,10,0,58,0,56,0,14,0,13,0,25,0,28,0,
+          11,0,12,0,27,0,24,0,9,0,10,0,26,0,22,0,23,0,8,0,6,0,7,0,20,0,21,0,4,0,5,
+          0,18,0,19,0,1,0,2,0,3,0,15,0,16,0,17,0,11,0,2,1,0>>,
+
+    {ok, Socket} = gen_tcp:connect(Hostname, Port, [{active, false}]),
+    gen_tcp:send(Socket, ClientHello),
+    %% v3 is not a supported protocol version (but hello record could have 3.0 for legacy interop)
+    case Version of
+        'tlsv1.3' ->
+            ssl_test_lib:check_server_alert(Server, illegal_parameter),
+            client_rejected(Socket, illegal_parameter);
+        _  ->
+            ssl_test_lib:check_server_alert(Server, protocol_version),
+            client_rejected(Socket, protocol_version)
+    end.
+
+accept_sslv3_record_hello() ->
+    [{doc,"Test that ssl v3 record in clients hellos are ignored when higher version are advertised"}].
+
+accept_sslv3_record_hello(Config) when is_list(Config) ->
+    Version = proplists:get_value(version, Config),
+    ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+    Allversions = all_versions(),
+
+    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+                                        {from, self()},
+                                        {options, [{versions, Allversions} | ServerOpts]}]),
+    Port = ssl_test_lib:inet_port(Server),
+
+    %% TLS-1.X Hello with SSL-3.0 record version
+    ClientHello = hello_with_3_0_record(Version),
+
+    {ok, Socket} = gen_tcp:connect(Hostname, Port, [{active, false}]),
+    gen_tcp:send(Socket, ClientHello),
+    case gen_tcp:recv(Socket, 3, 5000) of
+        %% Minor needs to be a TLS version that is a version 
+        %% above SSL-3.0 
+        {ok, [?HANDSHAKE, ?TLS_MAJOR, Minor]} when Minor > ?SSL_3_0_MINOR ->
+            ok;
+        {error, timout} ->       
+            ct:fail(ssl3_record_not_accepted)
+    end.
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all_versions() ->
+    [Version || Version  <- ['tlsv1.3', 'tlsv1.2', 'tlsv1.1', tlsv1],
+                ssl_test_lib:sufficient_crypto_support(Version)].
+
+client_rejected(Socket, Alert) ->
+    Num = alert_num(Alert),
+    case gen_tcp:recv(Socket, 7, 6000) of
+        {ok,[?ALERT, _Major, _Minor, _Len1, _Len2, _Level, Num]} ->
+            ok;
+        Other ->
+            ct:fail(Other)
+    end.
+
+alert_num(unexpected_message) ->
+    ?UNEXPECTED_MESSAGE;
+alert_num(illegal_parameter) ->
+    ?ILLEGAL_PARAMETER;
+alert_num(protocol_version) ->
+    ?PROTOCOL_VERSION.
+
+hello_with_3_0_record('tlsv1') ->
+    <<?HANDSHAKE, ?TLS_MAJOR, ?SSL_3_0_MINOR, 0,162, ?CLIENT_HELLO,0,0,158,
+      ?TLS_MAJOR, ?TLS_1_0_MINOR, 97,160,130,59,226,182,64,143,134,112,117,
+      64,10,57,164,101,182,215,0,199,145,232,172,194,45,242,48,176,5,153,
+      101,54,0,0,26,0,255,192,10,192,20,192,5,192,15,192,9,192,19,192,4,192,
+      14,0,57,0,56,0,51,0,50,1,0,0,91,0,0,0,19,0,17,0,0,14,119,119,119,46,101,
+      114,108,97,110,103,46,111,114,103,0,10,0,58,0,56,0,14,0,13,0,25,0,28,0,
+      11,0,12,0,27,0,24,0,9,0,10,0,26,0,22,0,23,0,8,0,6,0,7,0,20,0,21,0,4,0,5,
+      0,18,0,19,0,1,0,2,0,3,0,15,0,16,0,17,0,11,0,2,1,0>>;
+hello_with_3_0_record('tlsv1.1') ->
+    <<?HANDSHAKE, ?TLS_MAJOR, ?SSL_3_0_MINOR, 0,162, ?CLIENT_HELLO, 0,0,158,
+      ?TLS_MAJOR, ?TLS_1_1_MINOR, 97,160,130,59,226,182,64,143,134,112,117,
+      64,10,57,164,101,182,215,0,199,145,232,172,194,45,242,48,176,5,153,
+      101,54,0,0,26,0,255,192,10,192,20,192,5,192,15,192,9,192,19,192,4,192,
+      14,0,57,0,56,0,51,0,50,1,0,0,91,0,0,0,19,0,17,0,0,14,119,119,119,46,101,
+      114,108,97,110,103,46,111,114,103,0,10,0,58,0,56,0,14,0,13,0,25,0,28,0,
+      11,0,12,0,27,0,24,0,9,0,10,0,26,0,22,0,23,0,8,0,6,0,7,0,20,0,21,0,4,0,5,
+      0,18,0,19,0,1,0,2,0,3,0,15,0,16,0,17,0,11,0,2,1,0>>;
+hello_with_3_0_record('tlsv1.2') ->
+    <<?HANDSHAKE, ?TLS_MAJOR, ?SSL_3_0_MINOR, 0,252, ?CLIENT_HELLO, 0,0,248,
+      ?TLS_MAJOR, ?TLS_1_2_MINOR, 97,160,7,242,30,221,248,238,18,3,225,13,40,18,16,117,30,
+       159,250,156,175,90,184,65,177,226,217,125,205,227,110,154,0,0,88,0,255,
+       192,44,192,48,192,173,192,175,192,36,192,40,204,169,204,168,192,43,192,
+       47,192,172,192,174,192,46,192,50,192,38,192,42,192,45,192,49,192,35,192,
+       39,192,37,192,41,0,159,0,163,0,107,0,106,0,158,0,162,204,170,0,103,0,64,
+       192,10,192,20,192,5,192,15,192,9,192,19,192,4,192,14,0,57,0,56,0,51,0,50,
+       1,0,0,119,0,0,0,19,0,17,0,0,14,119,119,119,46,101,114,108,97,110,103,46,
+       111,114,103,0,13,0,24,0,22,6,3,6,1,5,3,5,1,4,3,4,1,3,3,3,1,2,3,2,1,2,2,0,
+       10,0,58,0,56,0,14,0,13,0,25,0,28,0,11,0,12,0,27,0,24,0,9,0,10,0,26,0,22,
+       0,23,0,8,0,6,0,7,0,20,0,21,0,4,0,5,0,18,0,19,0,1,0,2,0,3,0,15,0,16,0,17,
+       0,11,0,2,1,0>>;
+hello_with_3_0_record('tlsv1.3') ->
+    <<?HANDSHAKE, ?TLS_MAJOR, ?SSL_3_0_MINOR, 0,219,?CLIENT_HELLO, 0,0,215,
+      ?TLS_MAJOR, ?TLS_1_2_MINOR, %% TLS_1.3 has LEGACY version TLS-1.2 here
+      97,160,140,70,177,254,168,106,75,198,216,169,71,146,133,144,28,135,
+      35,26,222,109,13,169,12,61,229,79,110,238,192,242,32,179,104,86,13,116,
+      85,208,242,78,97,216,13,252,63,99,225,0,237,43,221,117,25,238,128,174,
+      158,218,232,249,211,93,96,0,12,0,255,19,2,19,1,19,3,19,4,19,5,1,0,0,
+      130,0,0,0,19,0,17,0,0,14,119,119,119,46,101,114,108,97,110,103,46,111,
+      114,103,0,13,0,34,0,32,6,3,5,3,4,3,8,11,8,10,8,9,8,6,8,5,8,4,8,7,8,8,6,
+      1,5,1,4,1,2,3,2,1,0,51,0,38,0,36,0,29,0,32,171,47,137,226,39,16,202,89,
+      42,42,32,73,84,134,110,74,110,163,140,111,177,126,133,118,141,2,153,
+      156,157,205,101,69,0,10,0,10,0,8,0,29,0,30,0,23,0,24,0,11,0,2,1,0,0,
+      ?SUPPORTED_VERSIONS_EXT,0,3,2,?TLS_MAJOR, ?TLS_1_3_MINOR>>.
-- 
2.31.1

openSUSE Build Service is sponsored by