From 33afe108de9cf24be47b9ebd740a8f5e19daca79 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin
Date: Tue, 3 May 2016 12:07:51 +0200
Subject: ssl: Add option to phase out support for sslv2 client hello
ssl servers can recognize sslv2 client hellos to interop with clients
that support higher version of SSL/TLS but also offers sslv2
Conflicts:
lib/ssl/src/tls_connection.erl
---
lib/ssl/doc/src/ssl.xml | 6 +++++
lib/ssl/src/ssl.erl | 7 +++--
lib/ssl/src/ssl_internal.hrl | 3 ++-
lib/ssl/src/tls_connection.erl | 7 ++---
lib/ssl/src/tls_handshake.erl | 48 ++++++++++++++++++++---------------
lib/ssl/test/ssl_basic_SUITE.erl | 2 +-
lib/ssl/test/ssl_handshake_SUITE.erl | 3 ++-
lib/ssl/test/ssl_npn_hello_SUITE.erl | 8 +++---
lib/ssl/test/ssl_to_openssl_SUITE.erl | 18 ++++++++++---
9 files changed, 65 insertions(+), 37 deletions(-)
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 33ece8f769..c7aa3d5b09 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -706,6 +706,12 @@ fun(srp, Username :: string(), UserState :: term()) ->
client certificate is requested. For more details see the corresponding client option.
+ {v2_hello_compatible, boolean()}
+ - If true, the server accepts clients that send hello messages on SSL-2.0 format but offers
+ supported SSL/TLS versions. Defaults to false, that is the server will not interoperate with clients that
+ offers SSL-2.0.
+
+
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 33d5c1c6d6..0058e5ec9a 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -732,7 +732,8 @@ handle_options(Opts0, Role) ->
false, Role)),
client, Role),
crl_check = handle_option(crl_check, Opts, false),
- crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}})
+ crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
+ v2_hello_compatible = handle_option(v2_hello_compatible, Opts, false)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -747,7 +748,7 @@ handle_options(Opts0, Role) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, beast_mitigation],
+ fallback, signature_algs, beast_mitigation, v2_hello_compatible],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -991,6 +992,8 @@ validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse
Value == zero_n orelse
Value == disabled ->
Value;
+validate_option(v2_hello_compatible, Value) when is_boolean(Value) ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index dddcbdeeda..c19c1787ff 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -139,7 +139,8 @@
fallback = false :: boolean(),
crl_check :: boolean() | peer | best_effort,
crl_cache,
- signature_algs
+ signature_algs,
+ v2_hello_compatible :: boolean()
}).
-record(socket_options,
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 56e516bce2..eaf2dd002d 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -399,9 +399,10 @@ handle_common_event(internal, #alert{} = Alert, StateName,
handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
StateName, #state{protocol_buffers =
#protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
- negotiated_version = Version} = State0) ->
- try
- {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
+ negotiated_version = Version,
+ ssl_options = Options} = State0) ->
+ try
+ {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options),
State =
State0#state{protocol_buffers =
Buffers#protocol_buffers{tls_handshake_buffer = Buf}},
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 871eb970eb..397f963ad5 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -33,7 +33,7 @@
-include_lib("public_key/include/public_key.hrl").
-export([client_hello/8, hello/4,
- get_tls_handshake/3, encode_handshake/2, decode_handshake/3]).
+ get_tls_handshake/4, encode_handshake/2, decode_handshake/4]).
-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake().
@@ -133,17 +133,17 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), #ssl_options{}) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-get_tls_handshake(Version, Data, <<>>) ->
- get_tls_handshake_aux(Version, Data, []);
-get_tls_handshake(Version, Data, Buffer) ->
- get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []).
+get_tls_handshake(Version, Data, <<>>, Options) ->
+ get_tls_handshake_aux(Version, Data, Options, []);
+get_tls_handshake(Version, Data, Buffer, Options) ->
+ get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), Options, []).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -184,24 +184,24 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
end.
get_tls_handshake_aux(Version, <>, Acc) ->
+ Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
Raw = <>,
- Handshake = decode_handshake(Version, Type, Body),
- get_tls_handshake_aux(Version, Rest, [{Handshake,Raw} | Acc]);
-get_tls_handshake_aux(_Version, Data, Acc) ->
+ Handshake = decode_handshake(Version, Type, Body, V2Hello),
+ get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]);
+get_tls_handshake_aux(_Version, Data, _, Acc) ->
{lists:reverse(Acc), Data}.
-decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
+decode_handshake(_, ?HELLO_REQUEST, <<>>, _) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
decode_handshake(_Version, ?CLIENT_HELLO, <>) ->
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>, true) ->
#client_hello{client_version = {Major, Minor},
random = ssl_v2:client_random(ChallengeData, CDLength),
session_id = 0,
@@ -209,12 +209,18 @@ decode_handshake(_Version, ?CLIENT_HELLO, <>, false) ->
+ throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION, ssl_v2_client_hello_no_supported));
decode_handshake(_Version, ?CLIENT_HELLO, <>) ->
-
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>, _) ->
+
DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
#client_hello{
@@ -226,7 +232,7 @@ decode_handshake(_Version, ?CLIENT_HELLO, <
+decode_handshake(Version, Tag, Msg, _) ->
ssl_handshake:decode_handshake(Version, Tag, Msg).
enc_handshake(#hello_request{}, _Version) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 99f7c9b780..efe996e57c 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -3059,7 +3059,7 @@ tls_ciphersuite_vs_version(Config) when is_list(Config) ->
>>),
{ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
{ok, <>} = gen_tcp:recv(Socket, HelloLen, 5000),
- ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
+ ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin, false),
case ServerHello of
#server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
ok;
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 26e83413c1..a671e3e307 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -99,7 +99,8 @@ decode_hello_handshake(_Config) ->
16#70, 16#64, 16#79, 16#2f, 16#32>>,
Version = {3, 0},
- {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
+ {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>,
+ #ssl_options{v2_hello_compatible = false}),
{Hello, _Data} = hd(Records),
#renegotiation_info{renegotiated_connection = <<0>>}
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 533501e788..00eb9fee4f 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -57,7 +57,7 @@ encode_and_decode_client_hello_test(Config) ->
HandShakeData = create_client_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = undefined.
%%--------------------------------------------------------------------
@@ -65,7 +65,7 @@ encode_and_decode_npn_client_hello_test(Config) ->
HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}.
%%--------------------------------------------------------------------
@@ -73,7 +73,7 @@ encode_and_decode_server_hello_test(Config) ->
HandShakeData = create_server_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = undefined.
%%--------------------------------------------------------------------
@@ -81,7 +81,7 @@ encode_and_decode_npn_server_hello_test(Config) ->
HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation,
ct:log("~p ~n", [NextProtocolNegotiation]),
NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 9df31a3381..d9a4657a79 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -131,6 +131,13 @@ end_per_suite(_Config) ->
ssl:stop(),
application:stop(crypto).
+init_per_group(basic, Config) ->
+ case ssl_test_lib:supports_ssl_tls_version(sslv2) of
+ true ->
+ [{v2_hello_compatible, true} | Config];
+ false ->
+ [{v2_hello_compatible, false} | Config]
+ end;
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
true ->
@@ -296,15 +303,18 @@ basic_erlang_server_openssl_client() ->
basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ V2Compat = proplists:get_value(v2_hello_compatible, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
+ ct:pal("v2_hello_compatible: ~p", [V2Compat]),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options,[{v2_hello_compatible, V2Compat} | ServerOpts]}]),
+
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
@@ -318,8 +328,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
+
%%--------------------------------------------------------------------
erlang_client_openssl_server() ->
[{doc,"Test erlang client with openssl server"}].
--
cgit v1.2.3