aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/src/ssl.erl3
-rw-r--r--lib/ssl/src/ssl_alert.erl29
-rw-r--r--lib/ssl/src/ssl_alert.hrl2
-rw-r--r--lib/ssl/src/tls_connection.erl22
-rw-r--r--lib/ssl/test/make_certs.erl5
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl79
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl30
7 files changed, 139 insertions, 31 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 26947ceff9..234db21443 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -636,7 +636,8 @@ handle_options(Opts0, _Role) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), hd(Versions)),
+ ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
+ RecordCb:highest_protocol_version(Versions)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index db1535b5ec..78dc98bc25 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -31,7 +31,7 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([encode/3, alert_txt/1, reason_code/2]).
+-export([encode/3, decode/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
@@ -41,12 +41,21 @@
-spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
-%% Description:
+%% Description: Encodes an alert
%%--------------------------------------------------------------------
encode(#alert{} = Alert, Version, ConnectionStates) ->
ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
%%--------------------------------------------------------------------
+-spec decode(binary()) -> [#alert{}] | #alert{}.
+%%
+%% Description: Decode alert(s), will return a singel own alert if peer
+%% sends garbage or too many warning alerts.
+%%--------------------------------------------------------------------
+decode(Bin) ->
+ decode(Bin, [], 0).
+
+%%--------------------------------------------------------------------
-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
@@ -71,6 +80,22 @@ alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}})
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+%% It is very unlikely that an correct implementation will send more than one alert at the time
+%% So it there is more than 10 warning alerts we consider it an error
+decode(<<?BYTE(Level), ?BYTE(_), _/binary>>, _, N) when Level == ?WARNING, N > ?MAX_ALERTS ->
+ ?ALERT_REC(?FATAL, ?DECODE_ERROR);
+decode(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc, N) when Level == ?WARNING ->
+ Alert = ?ALERT_REC(Level, Description),
+ decode(Rest, [Alert | Acc], N + 1);
+decode(<<?BYTE(Level), ?BYTE(Description), _Rest/binary>>, Acc, _) when Level == ?FATAL->
+ Alert = ?ALERT_REC(Level, Description),
+ lists:reverse([Alert | Acc]); %% No need to decode rest fatal alert will end the connection
+decode(<<?BYTE(_Level), _/binary>>, _, _) ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+decode(<<>>, Acc, _) ->
+ lists:reverse(Acc, []).
+
level_txt(?WARNING) ->
"Warning:";
level_txt(?FATAL) ->
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 2d1f323085..f4f1d74264 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -104,6 +104,8 @@
-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}).
+-define(MAX_ALERTS, 10).
+
%% Alert
-record(alert, {
level,
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 930706cde6..32086ff6ce 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -362,20 +362,11 @@ encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
{Encoded, ConnectionStates, Hist}.
-
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
-
-
decode_alerts(Bin) ->
- decode_alerts(Bin, []).
-
-decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
- A = ?ALERT_REC(Level, Description),
- decode_alerts(Rest, [A | Acc]);
-decode_alerts(<<>>, Acc) ->
- lists:reverse(Acc, []).
+ ssl_alert:decode(Bin).
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
@@ -420,10 +411,13 @@ next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = S
next_state(_,Next, no_record, State) ->
{next_state, Next, State, get_timeout(State)};
-next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
- Alerts = decode_alerts(EncAlerts),
- handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
-
+next_state(Current, Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, #state{negotiated_version = Version} = State) ->
+ case decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, Current, State)
+ end;
next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
State0 = #state{protocol_buffers =
#protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 0947657ca7..15a7e118ff 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -32,6 +32,7 @@
v2_crls = true,
ecc_certs = false,
issuing_distribution_point = false,
+ crl_port = 8000,
openssl_cmd = "openssl"}).
@@ -57,6 +58,8 @@ make_config([{default_bits, Bits}|T], C) when is_integer(Bits) ->
make_config(T, C#config{default_bits = Bits});
make_config([{v2_crls, Bool}|T], C) when is_boolean(Bool) ->
make_config(T, C#config{v2_crls = Bool});
+make_config([{crl_port, Port}|T], C) when is_integer(Port) ->
+ make_config(T, C#config{crl_port = Port});
make_config([{ecc_certs, Bool}|T], C) when is_boolean(Bool) ->
make_config(T, C#config{ecc_certs = Bool});
make_config([{issuing_distribution_point, Bool}|T], C) when is_boolean(Bool) ->
@@ -423,7 +426,7 @@ ca_cnf(C) ->
"[crl_section]\n"
%% intentionally invalid
"URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
- "URI.2=http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+ "URI.2=http://localhost:",integer_to_list(C#config.crl_port),"/",C#config.commonName,"/crl.pem\n"
"\n"
"[user_cert_digital_signature_only]\n"
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 36858db4b4..a1b766e05f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -116,7 +116,9 @@ options_tests() ->
tcp_reuseaddr,
honor_server_cipher_order,
honor_client_cipher_order,
- ciphersuite_vs_version
+ ciphersuite_vs_version,
+ unordered_protocol_versions_server,
+ unordered_protocol_versions_client
].
api_tests() ->
@@ -245,6 +247,14 @@ end_per_group(_GroupName, Config) ->
Config.
%%--------------------------------------------------------------------
+init_per_testcase(Case, Config) when Case == unordered_protocol_versions_client;
+ Case == unordered_protocol_versions_server->
+ case proplists:get_value(supported, ssl:versions()) of
+ ['tlsv1.2' | _] ->
+ Config;
+ _ ->
+ {skip, "TLS 1.2 need but not supported on this platform"}
+ end;
init_per_testcase(no_authority_key_identifier, Config) ->
%% Clear cach so that root cert will not
%% be found.
@@ -401,6 +411,7 @@ protocol_versions() ->
protocol_versions(Config) when is_list(Config) ->
basic_test(Config).
+
%%--------------------------------------------------------------------
empty_protocol_versions() ->
[{doc,"Test to set an empty list of protocol versions in app environment."}].
@@ -3088,6 +3099,57 @@ versions_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+unordered_protocol_versions_server() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
+
+unordered_protocol_versions_server(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ClientOpts}]),
+ CipherSuite = first_rsa_suite(ssl:cipher_suites()),
+ ServerMsg = ClientMsg = {ok, {'tlsv1.2', CipherSuite}},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
+
+%%--------------------------------------------------------------------
+unordered_protocol_versions_client() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
+
+unordered_protocol_versions_client(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ServerOpts }]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]),
+
+ CipherSuite = first_rsa_suite(ssl:cipher_suites()),
+ ServerMsg = ClientMsg = {ok, {'tlsv1.2', CipherSuite}},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
+
+%%--------------------------------------------------------------------
server_name_indication_option() ->
[{doc,"Test API server_name_indication option to connect."}].
@@ -3683,6 +3745,10 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
connection_info_result(Socket) ->
ssl:connection_info(Socket).
+version_info_result(Socket) ->
+ {ok, {Version, _}} = ssl:connection_info(Socket),
+ {ok, Version}.
+
connect_dist_s(S) ->
Msg = term_to_binary({erlang,term}),
ok = ssl:send(S, Msg).
@@ -3768,3 +3834,14 @@ try_recv_active(Socket) ->
try_recv_active_once(Socket) ->
{error, einval} = ssl:recv(Socket, 11),
ok.
+
+first_rsa_suite([{ecdhe_rsa, _, _} = Suite | _]) ->
+ Suite;
+first_rsa_suite([{dhe_rsa, _, _} = Suite| _]) ->
+ Suite;
+first_rsa_suite([{rsa, _, _} = Suite| _]) ->
+ Suite;
+first_rsa_suite([_ | Rest]) ->
+ first_rsa_suite(Rest).
+
+
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 4eacf3adfc..bad0949ec4 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -48,8 +48,8 @@ all() ->
].
groups() ->
- [{basic, [], basic_tests()},
- {v1_crl, [], v1_crl_tests()},
+ [{basic, [], basic_tests()},
+ {v1_crl, [], v1_crl_tests()},
{idp_crl, [], idp_crl_tests()}].
basic_tests() ->
@@ -72,8 +72,8 @@ init_per_suite(Config0) ->
_ ->
TLSVersion = ?config(tls_version, Config0),
OpenSSL_version = (catch os:cmd("openssl version")),
- ct:log("TLS version: ~p~nOpenSSL version: ~p~n~n~p:module_info(): ~p~n~nssh:module_info(): ~p~n",
- [TLSVersion, OpenSSL_version, ?MODULE, ?MODULE:module_info(), ssh:module_info()]),
+ ct:log("TLS version: ~p~nOpenSSL version: ~p~n~n~p:module_info(): ~p~n~nssl:module_info(): ~p~n",
+ [TLSVersion, OpenSSL_version, ?MODULE, ?MODULE:module_info(), ssl:module_info()]),
case ssl_test_lib:enough_openssl_crl_support(OpenSSL_version) of
false ->
{skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
@@ -82,7 +82,13 @@ init_per_suite(Config0) ->
try crypto:start() of
ok ->
ssl:start(),
- [{watchdog, Dog}, {openssl_version,OpenSSL_version} | Config0]
+ {ok, Hostname0} = inet:gethostname(),
+ IPfamily =
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts,[])) of
+ true -> inet6;
+ false -> inet
+ end,
+ [{ipfamily,IPfamily}, {watchdog, Dog}, {openssl_version,OpenSSL_version} | Config0]
catch _C:_E ->
ct:log("crypto:start() caught ~p:~p",[_C,_E]),
{skip, "Crypto did not start"}
@@ -98,21 +104,23 @@ end_per_suite(_Config) ->
%%% Group init/end
init_per_group(Group, Config) ->
- ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
ssl:start(),
inets:start(),
CertDir = filename:join(?config(priv_dir, Config), Group),
DataDir = ?config(data_dir, Config),
ServerRoot = make_dir_path([?config(priv_dir,Config), Group, tmp]),
- Result = make_certs:all(DataDir, CertDir, cert_opts(Group)),
- ct:log("~p:~p~nmake_certs:all(~n DataDir=~p,~n CertDir=~p,~n ServerRoot=~p~n Opts=~p~n) returned ~p~n", [?MODULE,?LINE,DataDir, CertDir, ServerRoot, cert_opts(Group), Result]),
%% start a HTTP server to serve the CRLs
- {ok, Httpd} = inets:start(httpd, [{server_name, "localhost"}, {port, 8000},
+ {ok, Httpd} = inets:start(httpd, [{ipfamily, ?config(ipfamily,Config)},
+ {server_name, "localhost"}, {port, 0},
{server_root, ServerRoot},
{document_root, CertDir},
{modules, [mod_get]}
]),
- ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
+ [{port,Port}] = httpd:info(Httpd, [port]),
+ ct:log("~p:~p~nHTTPD IP family=~p, port=~p~n", [?MODULE, ?LINE, ?config(ipfamily,Config), Port]),
+ CertOpts = [{crl_port,Port}|cert_opts(Group)],
+ Result = make_certs:all(DataDir, CertDir, CertOpts),
+ ct:log("~p:~p~nmake_certs:all(~n DataDir=~p,~n CertDir=~p,~n ServerRoot=~p~n Opts=~p~n) returned ~p~n", [?MODULE,?LINE,DataDir, CertDir, ServerRoot, CertOpts, Result]),
[{make_cert_result, Result}, {cert_dir, CertDir}, {httpd, Httpd} | Config].
cert_opts(v1_crl) -> [{v2_crls, false}];
@@ -134,7 +142,6 @@ end_per_group(_GroupName, Config) ->
,ct:log("Stopped",[])
end,
inets:stop(),
- ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
Config.
%%%================================================================
@@ -481,7 +488,6 @@ fetch([]) ->
not_available;
fetch([{uniformResourceIdentifier, "http"++_=URL}|Rest]) ->
ct:log("~p:~p~ngetting CRL from ~p~n", [?MODULE,?LINE, URL]),
- ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
case httpc:request(get, {URL, []}, [], [{body_format, binary}]) of
{ok, {_Status, _Headers, Body}} ->
case Body of