aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/internal_doc/ssl-implementation.txt52
-rw-r--r--lib/ssl/src/Makefile3
-rw-r--r--lib/ssl/src/dtls_connection.erl3
-rw-r--r--lib/ssl/src/ssl.app.src1
-rw-r--r--lib/ssl/src/ssl.appup.src24
-rw-r--r--lib/ssl/src/ssl.erl321
-rw-r--r--lib/ssl/src/ssl_alert.erl29
-rw-r--r--lib/ssl/src/ssl_alert.hrl2
-rw-r--r--lib/ssl/src/ssl_cipher.erl8
-rw-r--r--lib/ssl/src/ssl_connection.erl216
-rw-r--r--lib/ssl/src/ssl_connection.hrl3
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl16
-rw-r--r--lib/ssl/src/ssl_handshake.erl79
-rw-r--r--lib/ssl/src/ssl_internal.hrl17
-rw-r--r--lib/ssl/src/ssl_listen_tracker_sup.erl71
-rw-r--r--lib/ssl/src/ssl_record.erl2
-rw-r--r--lib/ssl/src/ssl_record.hrl2
-rw-r--r--lib/ssl/src/ssl_socket.erl209
-rw-r--r--lib/ssl/src/ssl_sup.erl19
-rw-r--r--lib/ssl/src/tls_connection.erl144
-rw-r--r--lib/ssl/src/tls_connection_sup.erl4
-rw-r--r--lib/ssl/src/tls_record.erl33
-rw-r--r--lib/ssl/test/make_certs.erl5
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl287
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl30
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl14
-rw-r--r--lib/ssl/test/ssl_test_lib.erl40
-rw-r--r--lib/ssl/vsn.mk2
28 files changed, 1155 insertions, 481 deletions
diff --git a/lib/ssl/internal_doc/ssl-implementation.txt b/lib/ssl/internal_doc/ssl-implementation.txt
deleted file mode 100644
index e5d6ac8cd0..0000000000
--- a/lib/ssl/internal_doc/ssl-implementation.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-
-Important modules:
-
- module behaviour children
- ------ ---------
- ssl_app application ssl_sup
- ssl_sup supervisor ssl_server, ssl_broker_sup
- ssl_server gen_server -
- ssl_broker_sup supervisor ssl_broker
- ssl_broker gen_server -
-
-The ssl_server controls a port program that implements the SSL functionality.
-That port program uses the OpenSSL package.
-
-Each socket has a corresponding broker (listen, accept or connect). A broker
-is created and supervised by the ssl_broker_sup.
-
-All communication is between a user and a broker. The broker communicates
-with the ssl_server, that sends its commands to the port program and handles
-the port program responses, that are distributed to users through the
-brokers.
-
-There is a distinction between commands and data flow between the ssl_server
-and the port program. Each established connection between the user and the
-outside world consists of a local erlang socket (owned by the broker) that
-is read from and written to by the broker. At the other end of the local
-connection is a local socket in the port program.
-
-The "real" socket that connects to the outside world is in the port program
-(including listen sockets). The main purpose of the port program is to
-shuffle data between local sockets and outside world sockets, and detect and
-propagate read and write errors (including detection of closed sockets) to
-the ssl_server.
-
-There is documentation in the ssl_broker.erl module.
-
-There is also documentation in the esock.c and esock_openssl.c files.
-
-The ssl_pem.erl, ssl_pkix.erl and ssl_base64.erl modules are support
-modules for reading SSL certificates. Modules for parsing certificates
-are generated from ASN.1 modules in the `pkix' directory.
-
-The `examples' directory contains functions for generating certificates.
-Those certificates are used in the test suites.
-
-
-
-
-
-
-
-
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 131b615277..7c4c8ec2cc 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2013. All Rights Reserved.
+# Copyright Ericsson AB 1999-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -66,6 +66,7 @@ MODULES= \
ssl_session \
ssl_session_cache \
ssl_socket \
+ ssl_listen_tracker_sup \
tls_record \
dtls_record \
ssl_record \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 57f8dd86d3..508983ddac 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -202,13 +202,14 @@ hello(Hello = #client_hello{client_version = ClientVersion,
session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
{Version, {Type, Session},
ConnectionStates,
#hello_extensions{ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves} = ServerHelloExt} ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert,
+ dtls_v1:corresponding_tls_version(Version)),
ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 99839f6149..36681e2897 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -28,6 +28,7 @@
ssl_srp_primes,
ssl_alert,
ssl_socket,
+ ssl_listen_tracker_sup,
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
ssl_tls_dist_proxy,
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index b0ef292c4e..b713f86c1e 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,33 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 743753bf7d..189bbd7edd 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -97,17 +97,17 @@ connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
{Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
{gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = emulated_options(),
+ EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
- try handle_options(SslOptions0 ++ SocketValues, client) of
+ try handle_options(SslOptions0 ++ SocketValues) of
{ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts,
connection_cb = ConnectionCb}} ->
- ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
case ssl_socket:peername(Transport, Socket) of
{ok, {Address, Port}} ->
ssl_connection:connect(ConnectionCb, Address, Port, Socket,
- {SslOptions, EmOpts},
+ {SslOptions, emulated_socket_options(EmOpts, #socket_options{}), undefined},
self(), CbInfo, Timeout);
{error, Error} ->
{error, Error}
@@ -121,7 +121,7 @@ connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) ->
- try handle_options(Options, client) of
+ try handle_options(Options) of
{ok, Config} ->
do_connect(Host,Port,Config,Timeout)
catch
@@ -139,12 +139,15 @@ listen(_Port, []) ->
{error, nooptions};
listen(Port, Options0) ->
try
- {ok, Config} = handle_options(Options0, server),
+ {ok, Config} = handle_options(Options0),
ConnectionCb = connection_cb(Options0),
- #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb} = Config,
+ #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb,
+ ssl = SslOpts, emulated = EmOpts} = Config,
case Transport:listen(Port, Options) of
{ok, ListenSocket} ->
- {ok, #sslsocket{pid = {ListenSocket, Config}}};
+ ok = ssl_socket:setopts(Transport, ListenSocket, ssl_socket:internal_inet_values()),
+ {ok, Tracker} = ssl_socket:inherit_tracker(ListenSocket, EmOpts, SslOpts),
+ {ok, #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}};
Err = {error, _} ->
Err
end
@@ -164,25 +167,20 @@ transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
transport_accept(#sslsocket{pid = {ListenSocket,
- #config{transport_info = CbInfo,
+ #config{transport_info = {Transport,_,_, _} =CbInfo,
connection_cb = ConnectionCb,
- ssl = SslOpts}}}, Timeout) ->
- %% The setopt could have been invoked on the listen socket
- %% and options should be inherited.
- EmOptions = emulated_options(),
- {Transport,_,_, _} = CbInfo,
- {ok, SocketValues} = ssl_socket:getopts(Transport, ListenSocket, EmOptions),
- ok = ssl_socket:setopts(Transport, ListenSocket, internal_inet_values()),
+ ssl = SslOpts,
+ emulated = Tracker}}}, Timeout) ->
case Transport:accept(ListenSocket, Timeout) of
{ok, Socket} ->
- ok = ssl_socket:setopts(Transport, ListenSocket, SocketValues),
+ {ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker),
{ok, Port} = ssl_socket:port(Transport, Socket),
ConnArgs = [server, "localhost", Port, Socket,
- {SslOpts, socket_options(SocketValues)}, self(), CbInfo],
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Tracker}, self(), CbInfo],
ConnectionSup = connection_sup(ConnectionCb),
case ConnectionSup:start_child(ConnArgs) of
{ok, Pid} ->
- ssl_connection:socket_control(ConnectionCb, Socket, Pid, Transport);
+ ssl_connection:socket_control(ConnectionCb, Socket, Pid, Transport, Tracker);
{error, Reason} ->
{error, Reason}
end;
@@ -213,26 +211,27 @@ ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(#sslsocket{} = Socket, [], Timeout) ->
ssl_accept(#sslsocket{} = Socket, Timeout);
-ssl_accept(#sslsocket{} = Socket, SslOptions, Timeout) ->
+ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) ->
try
- {ok, #config{ssl = SSL}} = handle_options(SslOptions, server),
- ssl_connection:handshake(Socket, SSL, Timeout)
+ {ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker),
+ SslOpts = handle_options(SslOpts0, InheritedSslOpts),
+ ssl_connection:handshake(Socket, {SslOpts, emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
catch
Error = {error, _Reason} -> Error
end;
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
{Transport,_,_,_} =
proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = emulated_options(),
+ EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
ConnetionCb = connection_cb(SslOptions),
- try handle_options(SslOptions ++ SocketValues, server) of
+ try handle_options(SslOptions ++ SocketValues) of
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
- ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
{ok, Port} = ssl_socket:port(Transport, Socket),
ssl_connection:ssl_accept(ConnetionCb, Port, Socket,
- {SslOpts, EmOpts},
- self(), CbInfo, Timeout)
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout)
catch
Error = {error, _Reason} -> Error
end.
@@ -301,7 +300,7 @@ connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
-peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid)->
+peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)->
ssl_socket:peername(Transport, Socket);
peername(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}) ->
ssl_socket:peername(Transport, ListenSocket). %% Will return {error, enotconn}
@@ -357,11 +356,7 @@ cipher_suites(openssl) ->
[ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)];
cipher_suites(all) ->
Version = tls_record:highest_protocol_version([]),
- Supported = ssl_cipher:suites(Version)
- ++ ssl_cipher:anonymous_suites()
- ++ ssl_cipher:psk_suites(Version)
- ++ ssl_cipher:srp_suites(),
- [suite_definition(S) || S <- Supported].
+ [suite_definition(S) || S <- ssl_cipher:all_suites(Version)].
%%--------------------------------------------------------------------
-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) ->
@@ -371,7 +366,7 @@ cipher_suites(all) ->
%%--------------------------------------------------------------------
getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) ->
ssl_connection:get_opts(Pid, OptionTags);
-getopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}},
+getopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket,
OptionTags) when is_list(OptionTags) ->
try ssl_socket:getopts(Transport, ListenSocket, OptionTags) of
{ok, _} = Result ->
@@ -379,8 +374,8 @@ getopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_
{error, InetError} ->
{error, {options, {socket_options, OptionTags, InetError}}}
catch
- _:_ ->
- {error, {options, {socket_options, OptionTags}}}
+ _:Error ->
+ {error, {options, {socket_options, OptionTags, Error}}}
end;
getopts(#sslsocket{}, OptionTags) ->
{error, {options, {socket_options, OptionTags}}}.
@@ -400,7 +395,7 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
{error, {options, {not_a_proplist, Options0}}}
end;
-setopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}, Options) when is_list(Options) ->
+setopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) ->
try ssl_socket:setopts(Transport, ListenSocket, Options) of
ok ->
ok;
@@ -429,10 +424,10 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
-sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport,_, _, _}}}}) when is_port(Listen) ->
+sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}) when is_port(Listen) ->
ssl_socket:sockname(Transport, Listen);
-sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid) ->
+sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid) ->
ssl_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
@@ -551,7 +546,8 @@ do_connect(Address, Port,
{Transport, _, _, _} = CbInfo,
try Transport:connect(Address, Port, SocketOpts, Timeout) of
{ok, Socket} ->
- ssl_connection:connect(ConnetionCb, Address, Port, Socket, {SslOpts,EmOpts},
+ ssl_connection:connect(ConnetionCb, Address, Port, Socket,
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
self(), CbInfo, Timeout);
{error, Reason} ->
{error, Reason}
@@ -564,53 +560,47 @@ do_connect(Address, Port,
{error, {options, {socket_options, UserOpts}}}
end.
-handle_options(Opts0, _Role) ->
+%% Handle extra ssl options given to ssl_accept
+handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
+ cacertfile = CaCertFile0} = InheritedSslOpts) ->
+ RecordCB = record_cb(Protocol),
+ CaCerts = handle_option(cacerts, Opts0, CaCerts0),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts0, CaCerts),
+ CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of
+ undefined ->
+ CaCertDefault;
+ CAFile ->
+ CAFile
+ end,
+ NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts,
+ cacertfile = CaCertFile,
+ verify = Verify,
+ verify_fun = VerifyFun,
+ fail_if_no_peer_cert = FailIfNoPeerCert},
+ SslOpts1 = lists:foldl(fun(Key, PropList) ->
+ proplists:delete(Key, PropList)
+ end, Opts0, [cacerts, cacertfile, verify, verify_fun, fail_if_no_peer_cert]),
+ case handle_option(versions, SslOpts1, []) of
+ [] ->
+ new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB);
+ Value ->
+ Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
+ new_ssl_options(proplists:delete(versions, SslOpts1),
+ NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol))
+ end.
+
+%% Handle all options in listen and connect
+handle_options(Opts0) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
assert_proplist(Opts),
RecordCb = record_cb(Opts),
ReuseSessionFun = fun(_, _, _, _) -> true end,
-
- DefaultVerifyNoneFun =
- {fun(_,{bad_cert, _}, UserState) ->
- {valid, UserState};
- (_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, UserState) ->
- {valid, UserState};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, []},
-
- VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun),
-
- UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
- UserVerifyFun = handle_option(verify_fun, Opts, undefined),
CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} =
- %% Handle 0, 1, 2 for backwards compatibility
- case proplists:get_value(verify, Opts, verify_none) of
- 0 ->
- {verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
- 1 ->
- {verify_peer, false,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- 2 ->
- {verify_peer, true,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- verify_none ->
- {verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
- verify_peer ->
- {verify_peer, UserFailIfNoPeerCert,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- Value ->
- throw({error, {options, {verify, Value}}})
- end,
-
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts, CaCerts),
+
CertFile = handle_option(certfile, Opts, <<>>),
RecordCb = record_cb(Opts),
@@ -641,7 +631,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),
@@ -656,7 +647,8 @@ handle_options(Opts0, _Role) ->
handle_option(client_preferred_next_protocols, Opts, undefined)),
log_alert = handle_option(log_alert, Opts, true),
server_name_indication = handle_option(server_name_indication, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts, false)
+ honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
+ protocol = proplists:get_value(protocol, Opts, tls)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -675,10 +667,10 @@ handle_options(Opts0, _Role) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
- {SSLsock, Emulated} = emulated_options(SockOpts),
+ {Sock, Emulated} = emulated_options(SockOpts),
ConnetionCb = connection_cb(Opts),
- {ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = SSLsock,
+ {ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock,
inet_user = SockOpts, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
@@ -904,40 +896,24 @@ ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) ->
%% some trusted certs.
ca_cert_default(verify_peer, undefined, _) ->
"".
-
-emulated_options() ->
- [mode, packet, active, header, packet_size].
-
-internal_inet_values() ->
- [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}].
-
-socket_options(InetValues) ->
- #socket_options{
- mode = proplists:get_value(mode, InetValues, lists),
- header = proplists:get_value(header, InetValues, 0),
- active = proplists:get_value(active, InetValues, active),
- packet = proplists:get_value(packet, InetValues, 0),
- packet_size = proplists:get_value(packet_size, InetValues)
- }.
-
emulated_options(Opts) ->
- emulated_options(Opts, internal_inet_values(), #socket_options{}).
-
-emulated_options([{mode,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(mode,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt});
-emulated_options([{header,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(header,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{header=Opt});
-emulated_options([{active,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(active,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{active=Opt});
-emulated_options([{packet,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(packet,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt});
-emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(packet_size,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt});
+ emulated_options(Opts, ssl_socket:internal_inet_values(), ssl_socket:default_inet_values()).
+
+emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(mode, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
+emulated_options([{header, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(header, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(header, Emulated)]);
+emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(active, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
+emulated_options([{packet, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(packet, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet, Emulated)]);
+emulated_options([{packet_size, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(packet_size, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet_size, Emulated)]);
emulated_options([Opt|Opts], Inet, Emulated) ->
emulated_options(Opts, [Opt|Inet], Emulated);
emulated_options([], Inet,Emulated) ->
@@ -953,7 +929,7 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
error:_->
throw({error, {options, {ciphers, Value}}})
end.
-binary_cipher_suites(Version, []) -> %% Defaults to all supported suits
+binary_cipher_suites(Version, []) -> % Defaults to all supported suites
ssl_cipher:suites(Version);
binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
@@ -1054,7 +1030,7 @@ record_cb(tls) ->
record_cb(dtls) ->
dtls_record;
record_cb(Opts) ->
- record_cb(proplists:get_value(protocol, Opts, tls)).
+ record_cb(proplists:get_value(protocol, Opts, tls)).
connection_sup(tls_connection) ->
tls_connection_sup;
@@ -1076,3 +1052,112 @@ assert_proplist([inet6 | Rest]) ->
assert_proplist(Rest);
assert_proplist([Value | _]) ->
throw({option_not_a_key_value_tuple, Value}).
+
+emulated_socket_options(InetValues, #socket_options{
+ mode = Mode,
+ header = Header,
+ active = Active,
+ packet = Packet,
+ packet_size = Size}) ->
+ #socket_options{
+ mode = proplists:get_value(mode, InetValues, Mode),
+ header = proplists:get_value(header, InetValues, Header),
+ active = proplists:get_value(active, InetValues, Active),
+ packet = proplists:get_value(packet, InetValues, Packet),
+ packet_size = proplists:get_value(packet_size, InetValues, Size)
+ }.
+
+new_ssl_options([], #ssl_options{} = Opts, _) ->
+ Opts;
+new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{verify_client_once = validate_option(verify_client_once, Value)}, RecordCB);
+new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
+new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{cert = validate_option(cert, Value)}, RecordCB);
+new_ssl_options([{certfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{certfile = validate_option(certfile, Value)}, RecordCB);
+new_ssl_options([{key, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{key = validate_option(key, Value)}, RecordCB);
+new_ssl_options([{keyfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{keyfile = validate_option(keyfile, Value)}, RecordCB);
+new_ssl_options([{password, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{password = validate_option(password, Value)}, RecordCB);
+new_ssl_options([{dh, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{dh = validate_option(dh, Value)}, RecordCB);
+new_ssl_options([{dhfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{dhfile = validate_option(dhfile, Value)}, RecordCB);
+new_ssl_options([{user_lookup_fun, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{user_lookup_fun = validate_option(user_lookup_fun, Value)}, RecordCB);
+new_ssl_options([{psk_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{psk_identity = validate_option(psk_identity, Value)}, RecordCB);
+new_ssl_options([{srp_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{srp_identity = validate_option(srp_identity, Value)}, RecordCB);
+new_ssl_options([{ciphers, Value} | Rest], #ssl_options{versions = Versions} = Opts, RecordCB) ->
+ Ciphers = handle_cipher_option(Value, RecordCB:highest_protocol_version(Versions)),
+ new_ssl_options(Rest,
+ Opts#ssl_options{ciphers = Ciphers}, RecordCB);
+new_ssl_options([{reuse_session, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{reuse_session = validate_option(reuse_session, Value)}, RecordCB);
+new_ssl_options([{reuse_sessions, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{reuse_sessions = validate_option(reuse_sessions, Value)}, RecordCB);
+new_ssl_options([{ssl_imp, _Value} | Rest], #ssl_options{} = Opts, RecordCB) -> %% Not used backwards compatibility
+ new_ssl_options(Rest, Opts, RecordCB);
+new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB);
+new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);
+new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);
+new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB);
+new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector =
+ make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB);
+new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{log_alert = validate_option(log_alert, Value)}, RecordCB);
+new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB);
+new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB);
+new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) ->
+ throw({error, {options, {Key, Value}}}).
+
+
+handle_verify_options(Opts, CaCerts) ->
+ DefaultVerifyNoneFun =
+ {fun(_,{bad_cert, _}, UserState) ->
+ {valid, UserState};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun),
+
+ UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
+ UserVerifyFun = handle_option(verify_fun, Opts, undefined),
+
+
+ %% Handle 0, 1, 2 for backwards compatibility
+ case proplists:get_value(verify, Opts, verify_none) of
+ 0 ->
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ 1 ->
+ {verify_peer, false,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ 2 ->
+ {verify_peer, true,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ verify_none ->
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ verify_peer ->
+ {verify_peer, UserFailIfNoPeerCert,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ Value ->
+ throw({error, {options, {verify, Value}}})
+ end.
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/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 78a328ace8..a3ec419c2a 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -34,7 +34,8 @@
-export([security_parameters/2, security_parameters/3, suite_definition/1,
decipher/5, cipher/5,
- suite/1, suites/1, ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,
+ suite/1, suites/1, all_suites/1,
+ ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,
openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2]).
@@ -224,6 +225,11 @@ suites({3, 0}) ->
suites({3, N}) ->
tls_v1:suites(N).
+all_suites(Version) ->
+ suites(Version)
+ ++ ssl_cipher:anonymous_suites()
+ ++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:srp_suites().
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
%%
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index c2810a199f..34006612a2 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -37,7 +37,7 @@
%% Setup
-export([connect/8, ssl_accept/7, handshake/2, handshake/3,
- socket_control/4]).
+ socket_control/4, socket_control/5]).
%% User Events
-export([send/2, recv/3, close/1, shutdown/2,
@@ -50,7 +50,7 @@
%% SSL FSM state functions
-export([hello/3, abbreviated/3, certify/3, cipher/3, connection/3]).
%% SSL all state functions
--export([handle_sync_event/4, handle_info/3, terminate/3]).
+-export([handle_sync_event/4, handle_info/3, terminate/3, format_status/2]).
%%====================================================================
@@ -121,9 +121,16 @@ handshake(#sslsocket{pid = Pid}, SslOptions, Timeout) ->
%% Description: Set the ssl process to own the accept socket
%%--------------------------------------------------------------------
socket_control(Connection, Socket, Pid, Transport) ->
+ socket_control(Connection, Socket, Pid, Transport, undefined).
+
+%--------------------------------------------------------------------
+-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom(), pid()| undefined) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+%%--------------------------------------------------------------------
+socket_control(Connection, Socket, Pid, Transport, ListenTracker) ->
case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, ssl_socket:socket(Pid, Transport, Socket, Connection)};
+ {ok, ssl_socket:socket(Pid, Transport, Socket, Connection, ListenTracker)};
{error, Reason} ->
{error, Reason}
end.
@@ -290,12 +297,11 @@ hello(#hello_request{}, #state{role = client} = State0, Connection) ->
{Record, State} = Connection:next_record(State0),
Connection:next_state(hello, hello, Record, State);
-hello({common_client_hello, Type, ServerHelloExt, HashSign},
- #state{session = #session{cipher_suite = CipherSuite},
- negotiated_version = Version} = State, Connection) ->
- {KeyAlg, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- NegotiatedHashSign = negotiated_hashsign(HashSign, KeyAlg, Version),
+hello({common_client_hello, Type, ServerHelloExt, NegotiatedHashSign},
+ State, Connection) ->
do_server_hello(Type, ServerHelloExt,
+ %% Note NegotiatedHashSign is only negotiated for real if
+ %% if TLS version is at least TLS-1.2
State#state{hashsign_algorithm = NegotiatedHashSign}, Connection);
hello(timeout, State, _) ->
@@ -432,7 +438,8 @@ certify(#server_key_exchange{exchange_keys = Keys},
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
- ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
+ Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
+ Version, certify, State)
end
end;
@@ -441,8 +448,9 @@ certify(#server_key_exchange{} = Msg,
Connection:handle_unexpected_message(Msg, certify_server_keyexchange, State);
certify(#certificate_request{hashsign_algorithms = HashSigns},
- #state{session = #session{own_certificate = Cert}} = State0, Connection) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
+ #state{session = #session{own_certificate = Cert},
+ negotiated_version = Version} = State0, Connection) ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
Connection:next_state(certify, certify, Record,
State#state{cert_hashsign_algorithm = HashSign});
@@ -559,7 +567,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
tls_handshake_history = Handshake
} = State0, Connection) ->
- HashSign = ssl_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
+ HashSign = ssl_handshake:select_hashsign_algs(CertHashSign, Algo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
@@ -601,7 +609,7 @@ cipher(#finished{verify_data = Data} = Finished,
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0, Connection) ->
{Record, State} = Connection:next_record(State0#state{next_protocol = SelectedProtocol}),
- Connection:next_state(cipher, cipher, Record, State);
+ Connection:next_state(cipher, cipher, Record, State#state{expecting_next_protocol_negotiation = false});
cipher(timeout, State, _) ->
{next_state, cipher, State, hibernate};
@@ -641,12 +649,27 @@ handle_sync_event({application_data, Data}, From, StateName,
State#state{send_queue = queue:in({From, Data}, Queue)},
get_timeout(State)};
-handle_sync_event({start, Timeout}, StartFrom, hello, #state{protocol_cb = Connection} = State) ->
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- Connection:hello(start, State#state{start_or_recv_from = StartFrom,
- timer = Timer});
+handle_sync_event({start, Timeout}, StartFrom, hello, #state{role = Role,
+ protocol_cb = Connection,
+ ssl_options = SSLOpts} = State0) ->
+ try
+ State = ssl_config(SSLOpts, Role, State0),
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ Connection:hello(start, State#state{start_or_recv_from = StartFrom,
+ timer = Timer})
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State0}
+ end;
+
+handle_sync_event({start, {Opts, EmOpts}, Timeout}, From, StateName, State) ->
+ try
+ handle_sync_event({start, Timeout}, From, StateName, State#state{socket_options = EmOpts,
+ ssl_options = Opts})
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State}
+ end;
-%% The two clauses below could happen if a server upgrades a socket in
+%% These two clauses below could happen if a server upgrades a socket in
%% active mode. Note that in this case we are lucky that
%% controlling_process has been evalueated before receiving handshake
%% messages from client. The server should put the socket in passive
@@ -656,17 +679,16 @@ handle_sync_event({start, Timeout}, StartFrom, hello, #state{protocol_cb = Conne
%% they upgrade an active socket.
handle_sync_event({start,_}, _, connection, State) ->
{reply, connected, connection, State, get_timeout(State)};
-handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) ->
- {stop, {shutdown, Error}, {error, Error}, State};
-handle_sync_event({start, Timeout}, StartFrom, StateName, State) ->
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- {next_state, StateName, State#state{start_or_recv_from = StartFrom,
- timer = Timer}, get_timeout(State)};
-
-handle_sync_event({start, Opts, Timeout}, From, StateName, #state{ssl_options = SslOpts} = State) ->
- NewOpts = new_ssl_options(Opts, SslOpts),
- handle_sync_event({start, Timeout}, From, StateName, State#state{ssl_options = NewOpts});
+handle_sync_event({start, Timeout}, StartFrom, StateName, #state{role = Role, ssl_options = SslOpts} = State0) ->
+ try
+ State = ssl_config(SslOpts, Role, State0),
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ {next_state, StateName, State#state{start_or_recv_from = StartFrom,
+ timer = Timer}, get_timeout(State)}
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State0}
+ end;
handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State) ->
%% Run terminate before returning
@@ -674,7 +696,6 @@ handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State)
%% as intended.
(catch Connection:terminate(user_close, StateName, State)),
{stop, normal, ok, State#state{terminated = true}};
-
handle_sync_event({shutdown, How0}, _, StateName,
#state{transport_cb = Transport,
negotiated_version = Version,
@@ -696,13 +717,14 @@ handle_sync_event({shutdown, How0}, _, StateName,
Error ->
{stop, normal, Error, State}
end;
-
+handle_sync_event({recv, _N, _Timeout}, _RecvFrom, StateName,
+ #state{socket_options = #socket_options{active = Active}} = State) when Active =/= false ->
+ {reply, {error, einval}, StateName, State, get_timeout(State)};
handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName,
#state{protocol_cb = Connection} = State0) ->
Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
Connection:passive_receive(State0#state{bytes_to_read = N,
start_or_recv_from = RecvFrom, timer = Timer}, StateName);
-
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_is_connection/2.
handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
@@ -710,26 +732,22 @@ handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
{next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
timer = Timer},
get_timeout(State)};
-
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
{reply, ok, StateName, State#state{user_application = {NewMon,User}},
get_timeout(State)};
-
handle_sync_event({get_opts, OptTags}, _From, StateName,
#state{socket = Socket,
transport_cb = Transport,
socket_options = SockOpts} = State) ->
OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []),
{reply, OptsReply, StateName, State, get_timeout(State)};
-
handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) ->
{reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)};
handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
{reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
-
handle_sync_event({set_opts, Opts0}, _From, StateName0,
#state{socket_options = Opts1,
protocol_cb = Connection,
@@ -768,13 +786,10 @@ handle_sync_event({set_opts, Opts0}, _From, StateName0,
end
end
end;
-
handle_sync_event(renegotiate, From, connection, #state{protocol_cb = Connection} = State) ->
Connection:renegotiate(State#state{renegotiation = {true, From}});
-
handle_sync_event(renegotiate, _, StateName, State) ->
{reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
-
handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
#state{connection_states = ConnectionStates,
negotiated_version = Version} = State) ->
@@ -800,7 +815,6 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
error:Reason -> {error, Reason}
end,
{reply, Reply, StateName, State, get_timeout(State)};
-
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
session = #session{cipher_suite = Suite}} = State) ->
@@ -808,14 +822,12 @@ handle_sync_event(info, _, StateName,
AtomVersion = tls_record:protocol_version(Version),
{reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
StateName, State, get_timeout(State)};
-
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
{cipher_suite, ssl:suite_definition(Suite)}],
StateName, State, get_timeout(State)};
-
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
@@ -825,8 +837,9 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
start_or_recv_from = StartFrom, role = Role,
protocol_cb = Connection,
- error_tag = ErrorTag} = State) when StateName =/= connection ->
- Connection:alert_user(Transport, Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ error_tag = ErrorTag,
+ tracker = Tracker} = State) when StateName =/= connection ->
+ Connection:alert_user(Transport, Tracker,Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -876,7 +889,6 @@ terminate(_, _, #state{terminated = true}) ->
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns.
ok;
-
terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
handle_unrecv_data(StateName, State),
@@ -889,7 +901,6 @@ terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue,
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate);
-
terminate(Reason, connection, #state{negotiated_version = Version,
protocol_cb = Connection,
connection_states = ConnectionStates,
@@ -906,7 +917,6 @@ terminate(Reason, connection, #state{negotiated_version = Version,
_ ->
ok
end;
-
terminate(_Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
@@ -915,9 +925,50 @@ terminate(_Reason, _StateName, #state{transport_cb = Transport,
notify_renegotiater(Renegotiate),
Transport:close(Socket).
+format_status(normal, [_, State]) ->
+ [{data, [{"StateData", State}]}];
+format_status(terminate, [_, State]) ->
+ SslOptions = (State#state.ssl_options),
+ NewOptions = SslOptions#ssl_options{password = "***",
+ cert = "***",
+ cacerts = "***",
+ key = "***",
+ dh = "***",
+ psk_identity = "***",
+ srp_identity = "***"},
+ [{data, [{"StateData", State#state{connection_states = "***",
+ protocol_buffers = "***",
+ user_data_buffer = "***",
+ tls_handshake_history = "***",
+ session = "***",
+ private_key = "***",
+ diffie_hellman_params = "***",
+ diffie_hellman_keys = "***",
+ srp_params = "***",
+ srp_keys = "***",
+ premaster_secret = "***",
+ ssl_options = NewOptions
+ }}]}].
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+ssl_config(Opts, Role, State) ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} =
+ ssl_config:init(Opts, Role),
+ Handshake = ssl_handshake:init_handshake_history(),
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ Session = State#state.session,
+ State#state{tls_handshake_history = Handshake,
+ session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = Opts}.
+
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
#state{negotiated_version = Version,
@@ -1559,60 +1610,6 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
session = Session}, cipher, Connection),
Connection:next_state_connection(cipher, ack_connection(State#state{session = Session})).
-negotiated_hashsign(undefined, Algo, Version) ->
- default_hashsign(Version, Algo);
-negotiated_hashsign(HashSign = {_, _}, _, _) ->
- HashSign.
-
-%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
-%% If the client does not send the signature_algorithms extension, the
-%% server MUST do the following:
-%%
-%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
-%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
-%% sent the value {sha1,rsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
-%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
-%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-
-default_hashsign(_Version = {Major, Minor}, KeyExchange)
- when Major >= 3 andalso Minor >= 3 andalso
- (KeyExchange == rsa orelse
- KeyExchange == dhe_rsa orelse
- KeyExchange == dh_rsa orelse
- KeyExchange == ecdhe_rsa orelse
- KeyExchange == ecdh_rsa orelse
- KeyExchange == srp_rsa) ->
- {sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == dh_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == srp_rsa ->
- {md5sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_ecdsa ->
- {sha, ecdsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dhe_dss;
- KeyExchange == dh_dss;
- KeyExchange == srp_dss ->
- {sha, dsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dh_anon;
- KeyExchange == ecdh_anon;
- KeyExchange == psk;
- KeyExchange == dhe_psk;
- KeyExchange == rsa_psk;
- KeyExchange == srp_anon ->
- {null, anon}.
-
select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
select_curve(_) ->
@@ -1874,13 +1871,14 @@ make_premaster_secret({MajVer, MinVer}, rsa) ->
make_premaster_secret(_, _) ->
undefined.
-%% One day this can be maps instead, but we have to be backwards compatible for now
-new_ssl_options(New, Old) ->
- new_ssl_options(tuple_to_list(New), tuple_to_list(Old), []).
+negotiated_hashsign(undefined, Alg, Version) ->
+ %% Not negotiated choose default
+ case is_anonymous(Alg) of
+ true ->
+ {null, anon};
+ false ->
+ ssl_handshake:select_hashsign_algs(Alg, Version)
+ end;
+negotiated_hashsign(HashSign = {_, _}, _, _) ->
+ HashSign.
-new_ssl_options([], [], Acc) ->
- list_to_tuple(lists:reverse(Acc));
-new_ssl_options([undefined | Rest0], [Head1| Rest1], Acc) ->
- new_ssl_options(Rest0, Rest1, [Head1 | Acc]);
-new_ssl_options([Head0 | Rest0], [_| Rest1], Acc) ->
- new_ssl_options(Rest0, Rest1, [Head0 | Acc]).
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index b01c6cb1b3..592889b177 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -78,7 +78,8 @@
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
next_protocol = undefined :: undefined | binary(),
- client_ecc % {Curves, PointFmt}
+ client_ecc, % {Curves, PointFmt}
+ tracker :: pid() %% Tracker process for listen socket
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index 22614a2d34..58efeaf892 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,9 +45,11 @@ start_link() ->
init([]) ->
SessionCertManager = session_and_cert_manager_child_spec(),
ConnetionManager = connection_manager_child_spec(),
+ ListenOptionsTracker = listen_options_tracker_child_spec(),
ProxyServer = proxy_server_child_spec(),
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager,
+ {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager,
+ ListenOptionsTracker,
ProxyServer]}}.
%%--------------------------------------------------------------------
@@ -68,7 +70,7 @@ connection_manager_child_spec() ->
StartFunc = {tls_connection_sup, start_link_dist, []},
Restart = permanent,
Shutdown = 4000,
- Modules = [ssl_connection],
+ Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -81,3 +83,11 @@ proxy_server_child_spec() ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+listen_options_tracker_child_spec() ->
+ Name = ssl_socket_dist,
+ StartFunc = {ssl_listen_tracker_sup, start_link_dist, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_socket],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 1108edcf48..fc67d2c28d 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -73,7 +73,8 @@
]).
%% MISC
--export([select_version/3, prf/5, select_hashsign/2, select_cert_hashsign/3,
+-export([select_version/3, prf/5, select_hashsign/3,
+ select_hashsign_algs/2, select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
%%====================================================================
@@ -590,23 +591,25 @@ prf({3,1}, Secret, Label, Seed, WantedLength) ->
{ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
prf({3,_N}, Secret, Label, Seed, WantedLength) ->
{ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
+
+
%%--------------------------------------------------------------------
--spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary()) ->
- [{atom(), atom()}] | undefined.
+-spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary(), ssl_record:ssl_version()) ->
+ {atom(), atom()} | undefined.
%%
%% Description:
%%--------------------------------------------------------------------
-select_hashsign(_, undefined) ->
+select_hashsign(_, undefined, _Version) ->
{null, anon};
-select_hashsign(undefined, Cert) ->
+select_hashsign(undefined, Cert, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_cert_hashsign(undefined, Algo, {undefined, undefined});
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
+ select_hashsign_algs(undefined, Algo, Version);
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- DefaultHashSign = {_, Sign} = select_cert_hashsign(undefined, Algo, {undefined, undefined}),
+ DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version),
case lists:filter(fun({sha, dsa}) ->
true;
({_, dsa}) ->
@@ -622,26 +625,59 @@ select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
[HashSign| _] ->
HashSign
end.
+
%%--------------------------------------------------------------------
--spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version() | {undefined, undefined}) ->
+-spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) ->
{atom(), atom()}.
+%% Description: For TLS 1.2 hash function and signature algorithm pairs can be
+%% negotiated with the signature_algorithms extension,
+%% for previous versions always use appropriate defaults.
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following: (e.i defaults for TLS 1.2)
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
%%
-%% Description: For TLS 1.2 selected cert_hash_sign will be recived
-%% in the handshake message, for previous versions use appropriate defaults.
-%% This function is also used by select_hashsign to extract
-%% the alogrithm of the server cert key.
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+
%%--------------------------------------------------------------------
-select_cert_hashsign(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
+select_hashsign_algs(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
Major >= 3 andalso Minor >= 3 ->
HashSign;
-select_cert_hashsign(undefined,?'id-ecPublicKey', _) ->
+select_hashsign_algs(undefined, ?rsaEncryption, {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ {sha, rsa};
+select_hashsign_algs(undefined,?'id-ecPublicKey', _) ->
{sha, ecdsa};
-select_cert_hashsign(undefined, ?rsaEncryption, _) ->
+select_hashsign_algs(undefined, ?rsaEncryption, _) ->
{md5sha, rsa};
-select_cert_hashsign(undefined, ?'id-dsa', _) ->
+select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
+-spec select_hashsign_algs(atom(), ssl_record:ssl_version()) -> {atom(), atom()}.
+%% Wrap function to keep the knowledge of the default values in
+%% one place only
+select_hashsign_algs(Alg, Version) when (Alg == rsa orelse
+ Alg == dhe_rsa orelse
+ Alg == dh_rsa orelse
+ Alg == ecdhe_rsa orelse
+ Alg == ecdh_rsa orelse
+ Alg == srp_rsa) ->
+ select_hashsign_algs(undefined, ?rsaEncryption, Version);
+select_hashsign_algs(Alg, Version) when (Alg == dhe_dss orelse
+ Alg == dh_dss orelse
+ Alg == srp_dss) ->
+ select_hashsign_algs(undefined, ?'id-dsa', Version);
+select_hashsign_algs(Alg, Version) when (Alg == ecdhe_ecdsa orelse
+ Alg == ecdh_ecdsa) ->
+ select_hashsign_algs(undefined, ?'id-ecPublicKey', Version).
+
%%--------------------------------------------------------------------
-spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
client | server) -> {binary(), #connection_states{}} | #alert{}.
@@ -1017,12 +1053,9 @@ decode_suites('3_bytes', Dec) ->
%%-------------Cipeher suite handling --------------------------------
available_suites(UserSuites, Version) ->
- case UserSuites of
- [] ->
- ssl_cipher:suites(Version);
- _ ->
- UserSuites
- end.
+ lists:filtermap(fun(Suite) ->
+ lists:member(Suite, ssl_cipher:all_suites(Version))
+ end, UserSuites).
available_suites(ServerCert, UserSuites, Version, Curve) ->
ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version))
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 8bf5b30a83..fd0d87bd5f 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -116,14 +116,6 @@
honor_cipher_order = false
}).
--record(config, {ssl, %% SSL parameters
- inet_user, %% User set inet options
- emulated, %% #socket_option{} emulated
- inet_ssl, %% inet options for internal ssl socket
- transport_info, %% Callback info
- connection_cb
- }).
-
-record(socket_options,
{
mode = list,
@@ -133,6 +125,15 @@
active = true
}).
+-record(config, {ssl, %% SSL parameters
+ inet_user, %% User set inet options
+ emulated, %% Emulated option list or "inherit_tracker" pid
+ inet_ssl, %% inet options for internal ssl socket
+ transport_info, %% Callback info
+ connection_cb
+ }).
+
+
-type state_name() :: hello | abbreviated | certify | cipher | connection.
-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
{next_state, state_name(), term(), timeout()} |
diff --git a/lib/ssl/src/ssl_listen_tracker_sup.erl b/lib/ssl/src/ssl_listen_tracker_sup.erl
new file mode 100644
index 0000000000..29f40e846d
--- /dev/null
+++ b/lib/ssl/src/ssl_listen_tracker_sup.erl
@@ -0,0 +1,71 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Supervisor for a listen options tracker
+%%----------------------------------------------------------------------
+-module(ssl_listen_tracker_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0, start_link_dist/0]).
+-export([start_child/1, start_child_dist/1]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, tracker_name(normal)}, ?MODULE, []).
+
+start_link_dist() ->
+ supervisor:start_link({local, tracker_name(dist)}, ?MODULE, []).
+
+start_child(Args) ->
+ supervisor:start_child(tracker_name(normal), Args).
+
+start_child_dist(Args) ->
+ supervisor:start_child(tracker_name(dist), Args).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init(_O) ->
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {ssl_socket, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
+ Modules = [ssl_socket],
+ Type = worker,
+
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
+
+tracker_name(normal) ->
+ ?MODULE;
+tracker_name(dist) ->
+ list_to_atom(atom_to_list(?MODULE) ++ "dist").
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index b0e9943e6d..7337225bc4 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -377,7 +377,7 @@ cipher(Version, Fragment,
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
{CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
%%--------------------------------------------------------------------
--spec decipher(ssl_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}.
+-spec decipher(ssl_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 87ed233c0a..6aab35d6da 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -70,7 +70,7 @@
-define(INITIAL_BYTES, 5).
--define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
+-define(MAX_SEQENCE_NUMBER, 18446744073709551615). %% (1 bsl 64) - 1 = 18446744073709551615
%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
%% We will renegotiate a little before so that there will be sequence numbers left
%% for the rehandshake and a little data. Currently we decided to renegotiate a little more
diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/ssl_socket.erl
index 1b6e637cd3..55eb569b20 100644
--- a/lib/ssl/src/ssl_socket.erl
+++ b/lib/ssl/src/ssl_socket.erl
@@ -1,20 +1,73 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
-module(ssl_socket).
+-behaviour(gen_server).
+
-include("ssl_internal.hrl").
-include("ssl_api.hrl").
--export([socket/4, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+-export([socket/5, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+-export([emulated_options/0, internal_inet_values/0, default_inet_values/0,
+ init/1, start_link/3, terminate/2, inherit_tracker/3, get_emulated_opts/1,
+ set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
+ handle_info/2, code_change/3]).
+
+-record(state, {
+ emulated_opts,
+ port,
+ ssl_opts
+ }).
-socket(Pid, Transport, Socket, ConnectionCb) ->
+%%--------------------------------------------------------------------
+%%% Internal API
+%%--------------------------------------------------------------------
+socket(Pid, Transport, Socket, ConnectionCb, Tracker) ->
#sslsocket{pid = Pid,
%% "The name "fd" is keept for backwards compatibility
- fd = {Transport, Socket, ConnectionCb}}.
-
+ fd = {Transport, Socket, ConnectionCb, Tracker}}.
+setopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOpts, EmulatedOpts} = split_options(Options),
+ ok = set_emulated_opts(Tracker, EmulatedOpts),
+ inet:setopts(ListenSocket, SockOpts);
+setopts(_, #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_},
+ emulated = Tracker}}}, Options) ->
+ {SockOpts, EmulatedOpts} = split_options(Options),
+ ok = set_emulated_opts(Tracker, EmulatedOpts),
+ Transport:setopts(ListenSocket, SockOpts);
+%%% Following clauses will not be called for emulated options, they are handled in the connection process
setopts(gen_tcp, Socket, Options) ->
inet:setopts(Socket, Options);
setopts(Transport, Socket, Options) ->
Transport:setopts(Socket, Options).
+getopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = split_options(Options),
+ EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
+ SocketOpts = get_socket_opts(ListenSocket, SockOptNames, inet),
+ {ok, EmulatedOpts ++ SocketOpts};
+getopts(Transport, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = split_options(Options),
+ EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
+ SocketOpts = get_socket_opts(ListenSocket, SockOptNames, Transport),
+ {ok, EmulatedOpts ++ SocketOpts};
+%%% Following clauses will not be called for emulated options, they are handled in the connection process
getopts(gen_tcp, Socket, Options) ->
inet:getopts(Socket, Options);
getopts(Transport, Socket, Options) ->
@@ -34,3 +87,151 @@ port(gen_tcp, Socket) ->
inet:port(Socket);
port(Transport, Socket) ->
Transport:port(Socket).
+
+emulated_options() ->
+ [mode, packet, active, header, packet_size].
+
+internal_inet_values() ->
+ [{packet_size,0}, {packet, 0}, {header, 0}, {active, false}, {mode,binary}].
+
+default_inet_values() ->
+ [{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}].
+
+inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = false} = SslOpts) ->
+ ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]);
+inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = true} = SslOpts) ->
+ ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]).
+
+get_emulated_opts(TrackerPid) ->
+ call(TrackerPid, get_emulated_opts).
+set_emulated_opts(TrackerPid, InetValues) ->
+ call(TrackerPid, {set_emulated_opts, InetValues}).
+get_all_opts(TrackerPid) ->
+ call(TrackerPid, get_all_opts).
+
+%%====================================================================
+%% ssl_listen_tracker_sup API
+%%====================================================================
+
+start_link(Port, SockOpts, SslOpts) ->
+ gen_server:start_link(?MODULE, [Port, SockOpts, SslOpts], []).
+
+%%--------------------------------------------------------------------
+-spec init(list()) -> {ok, #state{}}.
+%% Possible return values not used now.
+%% | {ok, #state{}, timeout()} | ignore | {stop, term()}.
+%%
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Port, Opts, SslOpts]) ->
+ process_flag(trap_exit, true),
+ true = link(Port),
+ {ok, #state{emulated_opts = Opts, port = Port, ssl_opts = SslOpts}}.
+
+%%--------------------------------------------------------------------
+-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}.
+%% Possible return values not used now.
+%% {reply, reply(), #state{}, timeout()} |
+%% {noreply, #state{}} |
+%% {noreply, #state{}, timeout()} |
+%% {stop, reason(), reply(), #state{}} |
+%% {stop, reason(), #state{}}.
+%%
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({set_emulated_opts, Opts0}, _From,
+ #state{emulated_opts = Opts1} = State) ->
+ Opts = do_set_emulated_opts(Opts0, Opts1),
+ {reply, ok, State#state{emulated_opts = Opts}};
+handle_call(get_emulated_opts, _From,
+ #state{emulated_opts = Opts} = State) ->
+ {reply, {ok, Opts}, State};
+handle_call(get_all_opts, _From,
+ #state{emulated_opts = EmOpts,
+ ssl_opts = SslOpts} = State) ->
+ {reply, {ok, EmOpts, SslOpts}, State}.
+
+%%--------------------------------------------------------------------
+-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
+%% Possible return values not used now.
+%% | {noreply, #state{}, timeout()} |
+%% {stop, reason(), #state{}}.
+%%
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_, State)->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+-spec handle_info(msg(), #state{}) -> {stop, reason(), #state{}}.
+%% Possible return values not used now.
+%% {noreply, #state{}}.
+%% |{noreply, #state{}, timeout()} |
+%%
+%%
+%% Description: Handling all non call/cast messages
+%%-------------------------------------------------------------------
+handle_info({'EXIT', Port, _}, #state{port = Port} = State) ->
+ {stop, normal, State}.
+
+
+%%--------------------------------------------------------------------
+-spec terminate(reason(), #state{}) -> ok.
+%%
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+-spec code_change(term(), #state{}, list()) -> {ok, #state{}}.
+%%
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+call(Pid, Msg) ->
+ gen_server:call(Pid, Msg, infinity).
+
+split_options(Opts) ->
+ split_options(Opts, emulated_options(), [], []).
+split_options([], _, SocketOpts, EmuOpts) ->
+ {SocketOpts, EmuOpts};
+split_options([{Name, _} = Opt | Opts], Emu, SocketOpts, EmuOpts) ->
+ case lists:member(Name, Emu) of
+ true ->
+ split_options(Opts, Emu, SocketOpts, [Opt | EmuOpts]);
+ false ->
+ split_options(Opts, Emu, [Opt | SocketOpts], EmuOpts)
+ end;
+split_options([Name | Opts], Emu, SocketOptNames, EmuOptNames) ->
+ case lists:member(Name, Emu) of
+ true ->
+ split_options(Opts, Emu, SocketOptNames, [Name | EmuOptNames]);
+ false ->
+ split_options(Opts, Emu, [Name | SocketOptNames], EmuOptNames)
+ end.
+
+do_set_emulated_opts([], Opts) ->
+ Opts;
+do_set_emulated_opts([{Name,_} = Opt | Rest], Opts) ->
+ do_set_emulated_opts(Rest, [Opt | proplists:delete(Name, Opts)]).
+
+get_socket_opts(_, [], _) ->
+ [];
+get_socket_opts(ListenSocket, SockOptNames, Cb) ->
+ {ok, Opts} = Cb:getopts(ListenSocket, SockOptNames),
+ Opts.
+
+get_emulated_opts(TrackerPid, EmOptNames) ->
+ {ok, EmOpts} = get_emulated_opts(TrackerPid),
+ lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts),
+ Value end,
+ EmOptNames).
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index e1aeb11ca4..7cccf8d5a5 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -47,8 +47,10 @@ init([]) ->
TLSConnetionManager = tls_connection_manager_child_spec(),
%% Not supported yet
%%DTLSConnetionManager = tls_connection_manager_child_spec(),
-
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager]}}.
+ %% Handles emulated options so that they inherited by the accept socket, even when setopts is performed on
+ %% the listen socket
+ ListenOptionsTracker = listen_options_tracker_child_spec(),
+ {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, ListenOptionsTracker]}}.
manager_opts() ->
@@ -85,7 +87,7 @@ tls_connection_manager_child_spec() ->
StartFunc = {tls_connection_sup, start_link, []},
Restart = permanent,
Shutdown = 4000,
- Modules = [tls_connection, ssl_connection],
+ Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -98,6 +100,17 @@ tls_connection_manager_child_spec() ->
%% Type = supervisor,
%% {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+listen_options_tracker_child_spec() ->
+ Name = ssl_socket,
+ StartFunc = {ssl_listen_tracker_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_socket],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+
session_cb_init_args() ->
case application:get_env(ssl, session_cb_init_args) of
{ok, Args} when is_list(Args) ->
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index ffa04ee8ba..3b89cf7b71 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,7 +53,7 @@
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
handle_normal_shutdown/3, handle_unexpected_message/3,
- workaround_transport_delivery_problems/2, alert_user/5, alert_user/8
+ workaround_transport_delivery_problems/2, alert_user/6, alert_user/9
]).
%% Data handling
@@ -66,18 +66,18 @@
%% gen_fsm callbacks
-export([init/1, hello/2, certify/2, cipher/2,
abbreviated/2, connection/2, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4, format_status/2]).
%%====================================================================
%% Internal application API
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = tls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -85,13 +85,13 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
Error
end;
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = tls_connection_sup:start_child_dist([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -144,29 +144,10 @@ send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
-init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
+init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
process_flag(trap_exit, true),
- State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- Handshake = ssl_handshake:init_handshake_history(),
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
- try ssl_config:init(SSLOpts0, Role) of
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
- Session = State0#state.session,
- State = State0#state{
- tls_handshake_history = Handshake,
- session = Session#session{own_certificate = OwnCert,
- time_stamp = TimeStamp},
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams},
- gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
- catch
- throw:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
- end.
+ State = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)).
%%--------------------------------------------------------------------
%% Description:There should be one instance of this function for each
@@ -208,11 +189,11 @@ hello(Hello = #client_hello{client_version = ClientVersion,
session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
{Version, {Type, Session},
ConnectionStates, ServerHelloExt} ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
@@ -344,7 +325,6 @@ handle_info(Msg, StateName, State) ->
terminate(Reason, StateName, State) ->
ssl_connection:terminate(Reason, StateName, State).
-
%%--------------------------------------------------------------------
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
%% Description: Convert process state when code is changed
@@ -352,6 +332,9 @@ terminate(Reason, StateName, State) ->
code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}.
+format_status(Type, Data) ->
+ ssl_connection:format_status(Type, Data).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -362,22 +345,13 @@ 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,
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
ConnectionStates = ssl_record:init_connection_states(Role),
@@ -391,9 +365,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
Monitor = erlang:monitor(process, User),
#state{socket_options = SocketOptions,
- %% We do not want to save the password in the state so that
- %% could be written in the clear into error logs.
- ssl_options = SSLOptions#ssl_options{password = undefined},
+ ssl_options = SSLOptions,
session = #session{is_resumable = new},
transport_cb = CbModule,
data_tag = DataTag,
@@ -411,7 +383,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
renegotiation = {false, first},
start_or_recv_from = undefined,
send_queue = queue:new(),
- protocol_cb = ?MODULE
+ protocol_cb = ?MODULE,
+ tracker = Tracker
}.
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
@@ -420,10 +393,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,
@@ -513,7 +489,7 @@ next_record(State) ->
next_record_if_active(State =
#state{socket_options =
- #socket_options{active = false}}) ->
+ #socket_options{active = false}}) ->
{no_record ,State};
next_record_if_active(State) ->
@@ -577,7 +553,8 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
bytes_to_read = BytesToRead,
start_or_recv_from = RecvFrom,
timer = Timer,
- user_data_buffer = Buffer0} = State0) ->
+ user_data_buffer = Buffer0,
+ tracker = Tracker} = State0) ->
Buffer1 = if
Buffer0 =:= <<>> -> Data;
Data =:= <<>> -> Buffer0;
@@ -585,7 +562,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end,
case get_data(SOpts, BytesToRead, Buffer1) of
{ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom),
+ SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom, Tracker),
cancel_timer(Timer),
State = State0#state{user_data_buffer = Buffer,
start_or_recv_from = undefined,
@@ -606,7 +583,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
{passive, Buffer} ->
next_record_if_active(State0#state{user_data_buffer = Buffer});
{error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom),
+ deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker),
{stop, normal, State0}
end.
@@ -661,8 +638,8 @@ decode_packet(Type, Buffer, PacketOpts) ->
%% HTTP headers using the {packet, httph} option, we don't do any automatic
%% switching of states.
deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
- Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data)),
+ Data, Pid, From, Tracker) ->
+ send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker)),
SO = case Data of
{P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
((Type =:= http) or (Type =:= http_bin)) ->
@@ -682,20 +659,20 @@ deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packe
end.
format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
- header = Header}, Data) ->
+ header = Header}, Data, _) ->
{ok, do_format_reply(Mode, Packet, Header, Data)};
format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
- header = Header}, Data) ->
- {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE),
+ header = Header}, Data, Tracker) ->
+ {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
do_format_reply(Mode, Packet, Header, Data)}.
-deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data)).
+deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker)).
-format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data) ->
+format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _) ->
{error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
-format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data) ->
- {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE),
+format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker) ->
+ {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
{invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
@@ -751,7 +728,11 @@ handle_tls_handshake(Handle, StateName,
handle_tls_handshake(Handle, NextStateName, State);
{stop, _,_} = Stop ->
Stop
- end.
+ end;
+
+handle_tls_handshake(_Handle, _StateName, #state{}) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
write_application_data(Data0, From,
#state{socket = Socket,
negotiated_version = Version,
@@ -835,10 +816,10 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,
#state{socket = Socket, transport_cb = Transport,
ssl_options = SslOpts, start_or_recv_from = From, host = Host,
port = Port, session = Session, user_application = {_Mon, Pid},
- role = Role, socket_options = Opts} = State) ->
+ role = Role, socket_options = Opts, tracker = Tracker} = State) ->
invalidate_session(Role, Host, Port, Session),
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role),
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
@@ -859,36 +840,37 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
{Record, State} = next_record(State0),
next_state(StateName, connection, Record, State);
-handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
+%% Gracefully log and ignore all other warning alerts
+handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts} = State0) ->
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
{Record, State} = next_record(State0),
next_state(StateName, StateName, Record, State).
-alert_user(Transport, Socket, connection, Opts, Pid, From, Alert, Role) ->
- alert_user(Transport,Socket, Opts#socket_options.active, Pid, From, Alert, Role);
-alert_user(Transport, Socket,_, _, _, From, Alert, Role) ->
- alert_user(Transport, Socket, From, Alert, Role).
+alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role) ->
+ alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role);
+alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role) ->
+ alert_user(Transport, Tracker, Socket, From, Alert, Role).
-alert_user(Transport, Socket, From, Alert, Role) ->
- alert_user(Transport, Socket, false, no_pid, From, Alert, Role).
+alert_user(Transport, Tracker, Socket, From, Alert, Role) ->
+ alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role).
-alert_user(_,_, false = Active, Pid, From, Alert, Role) ->
+alert_user(_, _, _, false = Active, Pid, From, Alert, Role) ->
%% If there is an outstanding ssl_accept | recv
%% From will be defined and send_or_reply will
%% send the appropriate error message.
ReasonCode = ssl_alert:reason_code(Alert, Role),
send_or_reply(Active, Pid, From, {error, ReasonCode});
-alert_user(Transport, Socket, Active, Pid, From, Alert, Role) ->
+alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role) ->
case ssl_alert:reason_code(Alert, Role) of
closed ->
send_or_reply(Active, Pid, From,
{ssl_closed, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE)});
+ Transport, Socket, ?MODULE, Tracker)});
ReasonCode ->
send_or_reply(Active, Pid, From,
{ssl_error, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE), ReasonCode})
+ Transport, Socket, ?MODULE, Tracker), ReasonCode})
end.
log_alert(true, Info, Alert) ->
@@ -921,15 +903,17 @@ handle_own_alert(Alert, Version, StateName,
handle_normal_shutdown(Alert, _, #state{socket = Socket,
transport_cb = Transport,
start_or_recv_from = StartFrom,
+ tracker = Tracker,
role = Role, renegotiation = {false, first}}) ->
- alert_user(Transport, Socket, StartFrom, Alert, Role);
+ alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role);
handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
socket_options = Opts,
transport_cb = Transport,
user_application = {_Mon, Pid},
+ tracker = Tracker,
start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(Transport, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
diff --git a/lib/ssl/src/tls_connection_sup.erl b/lib/ssl/src/tls_connection_sup.erl
index 6f0d8a7262..7a637c212a 100644
--- a/lib/ssl/src/tls_connection_sup.erl
+++ b/lib/ssl/src/tls_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -59,7 +59,7 @@ init(_O) ->
StartFunc = {tls_connection, start_link, []},
Restart = temporary, % E.g. should not be restarted
Shutdown = 4000,
- Modules = [tls_connection],
+ Modules = [tls_connection, ssl_connection],
Type = worker,
ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 4da08e9c51..f50ea22f39 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -154,21 +154,24 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
sequence_number = Seq,
security_parameters = SecParams} = ReadState0,
CompressAlg = SecParams#security_parameters.compression_algorithm,
- {PlainFragment, Mac, ReadState1} = ssl_record:decipher(Version, CipherFragment, ReadState0),
- MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
- case ssl_record:is_correct_mac(Mac, MacHash) of
- true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- sequence_number = Seq + 1,
- compression_state = CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
- end.
-
+ case ssl_record:decipher(Version, CipherFragment, ReadState0) of
+ {PlainFragment, Mac, ReadState1} ->
+ MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ sequence_number = Seq + 1,
+ compression_state = CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
%%--------------------------------------------------------------------
-spec protocol_version(tls_atom_version() | tls_version()) ->
tls_version() | tls_atom_version().
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 8e3d2e4b80..59756b956f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -115,12 +115,14 @@ options_tests() ->
reuseaddr,
tcp_reuseaddr,
honor_server_cipher_order,
- honor_client_cipher_order
+ honor_client_cipher_order,
+ ciphersuite_vs_version,
+ unordered_protocol_versions_server,
+ unordered_protocol_versions_client
].
api_tests() ->
- [new_options_in_accept,
- connection_info,
+ [connection_info,
peername,
peercert,
peercert_with_client_cert,
@@ -138,7 +140,9 @@ api_tests() ->
ssl_accept_timeout,
ssl_recv_timeout,
versions_option,
- server_name_indication_option
+ server_name_indication_option,
+ accept_pool,
+ new_options_in_accept
].
session_tests() ->
@@ -187,7 +191,11 @@ error_handling_tests()->
tcp_error_propagation_in_active_mode,
tcp_connect,
tcp_connect_big,
- close_transport_accept
+ close_transport_accept,
+ recv_active,
+ recv_active_once,
+ recv_error_handling,
+ dont_crash_on_handshake_garbage
].
rizzo_tests() ->
@@ -240,6 +248,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.
@@ -330,14 +346,15 @@ new_options_in_accept() ->
[{doc,"Test that you can set ssl options in ssl_accept/3 and not tcp upgrade"}].
new_options_in_accept(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts0 = ?config(server_dsa_opts, Config),
+ [_ , _ | ServerSslOpts] = ?config(server_opts, Config), %% Remove non ssl opts
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {ssl_opts, [{versions, [sslv3]},
- {ciphers,[{rsa,rc4_128,sha}]}]}, %% To be set in ssl_accept/3
+ {ssl_extra_opts, [{versions, [sslv3]},
+ {ciphers,[{rsa,rc4_128,sha}]} | ServerSslOpts]}, %% To be set in ssl_accept/3
{mfa, {?MODULE, connection_info_result, []}},
- {options, ServerOpts}]),
+ {options, proplists:delete(cacertfile, ServerOpts0)}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
@@ -396,6 +413,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."}].
@@ -1154,6 +1172,57 @@ close_transport_accept(Config) when is_list(Config) ->
Other ->
exit({?LINE, Other})
end.
+%%--------------------------------------------------------------------
+recv_active() ->
+ [{doc,"Test recv on active socket"}].
+
+recv_active(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, try_recv_active, []}},
+ {options, [{active, true} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+recv_active_once() ->
+ [{doc,"Test recv on active socket"}].
+
+recv_active_once(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, try_recv_active_once, []}},
+ {options, [{active, once} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
dh_params() ->
@@ -1177,7 +1246,7 @@ dh_params(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options,
- [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} |
+ [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} |
ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1276,7 +1345,7 @@ tcp_connect() ->
tcp_connect(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
+ TcpOpts = [binary, {reuseaddr, true}, {active, false}],
Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -2559,6 +2628,81 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ciphersuite_vs_version(Config) when is_list(Config) ->
+
+ {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+ ok = gen_tcp:send(Socket,
+ <<22, 3,0, 49:16, % handshake, SSL 3.0, length
+ 1, 45:24, % client_hello, length
+ 3,0, % SSL 3.0
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, % no session ID
+ %% three cipher suites -- null, one with sha256 hash and one with sha hash
+ 6:16, 0,255, 0,61, 0,57,
+ 1, 0 % no compression
+ >>),
+ {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
+ {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000),
+ ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
+ case ServerHello of
+ #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
+ ok;
+ _ ->
+ ct:fail({unexpected_server_hello, ServerHello})
+ end.
+
+%%--------------------------------------------------------------------
+
+dont_crash_on_handshake_garbage() ->
+ [{doc, "Ensure SSL server worker thows an alert on garbage during handshake "
+ "instead of crashing and exposing state to user code"}].
+
+dont_crash_on_handshake_garbage(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, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ unlink(Server), monitor(process, Server),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+
+ % Send hello and garbage record
+ ok = gen_tcp:send(Socket,
+ [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values
+
+ <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage
+ ]),
+ % Send unexpected change_cipher_spec
+ ok = gen_tcp:send(Socket, <<20, 0,0,12, 111,40,244,7,137,224,16,109,197,110,249,152>>),
+
+ % Ensure we receive an alert, not sudden disconnect
+ {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000).
+
+drop_handshakes(Socket, Timeout) ->
+ {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout),
+ {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout),
+ case RecType of
+ 22 -> drop_handshakes(Socket, Timeout);
+ _ -> {ok, <<Header/binary, Frag/binary>>}
+ end.
+
+
+%%--------------------------------------------------------------------
hibernate() ->
[{doc,"Check that an SSL connection that is started with option "
@@ -2957,6 +3101,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."}].
@@ -2994,6 +3189,53 @@ server_name_indication_option(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
ssl_test_lib:close(Client1).
+%%--------------------------------------------------------------------
+
+accept_pool() ->
+ [{doc,"Test having an accept pool."}].
+accept_pool(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {accepters, 3},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server0),
+ [Server1, Server2] = ssl_test_lib:accepters(2),
+
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client2 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ ssl_test_lib:check_ok([Server0, Server1, Server2, Client0, Client1, Client2]),
+
+ ssl_test_lib:close(Server0),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Server2),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -3505,6 +3747,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).
@@ -3582,3 +3828,22 @@ version_option_test(Config, Version) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
+try_recv_active(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ {error, einval} = ssl:recv(Socket, 11),
+ ok.
+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
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 6d020c472b..5f36842f9e 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@
-include_lib("common_test/include/ct.hrl").
-include("ssl_internal.hrl").
-include("tls_handshake.hrl").
+-include_lib("public_key/include/public_key.hrl").
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -36,7 +37,8 @@ all() -> [decode_hello_handshake,
decode_single_hello_extension_correctly,
decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
- encode_single_hello_sni_extension_correctly].
+ encode_single_hello_sni_extension_correctly,
+ select_proper_tls_1_2_rsa_default_hashsign].
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
@@ -95,3 +97,11 @@ encode_single_hello_sni_extension_correctly(_Config) ->
HelloExt = <<ExtSize:16/unsigned-big-integer, SNI/binary>>,
Encoded = ssl_handshake:encode_hello_extensions(Exts),
HelloExt = Encoded.
+
+select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
+ % RFC 5246 section 7.4.1.4.1 tells to use {sha1,rsa} as default signature_algorithm for RSA key exchanges
+ {sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,3}),
+ % Older versions use MD5/SHA1 combination
+ {md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,2}),
+ {md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,0}).
+
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 59f10d53a6..804fb1c9c0 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -67,7 +67,16 @@ run_server(Opts) ->
run_server(ListenSocket, Opts).
run_server(ListenSocket, Opts) ->
- do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts).
+ Accepters = proplists:get_value(accepters, Opts, 1),
+ run_server(ListenSocket, Opts, Accepters).
+
+run_server(ListenSocket, Opts, 1) ->
+ do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts);
+run_server(ListenSocket, Opts, N) ->
+ Pid = proplists:get_value(from, Opts),
+ Server = spawn(?MODULE, run_server, [ListenSocket, Opts, 1]),
+ Pid ! {accepter, N, Server},
+ run_server(ListenSocket, Opts, N-1).
do_run_server(_, {error, timeout} = Result, Opts) ->
Pid = proplists:get_value(from, Opts),
@@ -106,7 +115,7 @@ connect(#sslsocket{} = ListenSocket, Opts) ->
Node = proplists:get_value(node, Opts),
ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0),
Timeout = proplists:get_value(timeout, Opts, infinity),
- SslOpts = proplists:get_value(ssl_opts, Opts, []),
+ SslOpts = proplists:get_value(ssl_extra_opts, Opts, []),
AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout, SslOpts),
case ReconnectTimes of
0 ->
@@ -177,10 +186,7 @@ run_client(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]),
-ct:log("~p:~p~nnet_adm:ping(~p)=~p",[?MODULE,?LINE, Node,net_adm:ping(Node)]),
-%%ct:log("~p:~p~n~p:connect(~p, ~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Options, Node]),
-ct:log("~p:~p~n~p:connect(~p, ~p, ...)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
+ ct:log("~p:~p~n~p:connect(~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
Pid ! {connected, Socket},
@@ -290,7 +296,16 @@ wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
%% Unexpected
end.
-
+check_ok([]) ->
+ ok;
+check_ok(Pids) ->
+ receive
+ {Pid, ok} ->
+ check_ok(lists:delete(Pid, Pids));
+ Other ->
+ ct:fail({expected, {"pid()", ok}, got, Other})
+ end.
+
wait_for_result(Pid, Msg) ->
receive
{Pid, Msg} ->
@@ -679,6 +694,17 @@ run_client_error(Opts) ->
Error = rpc:call(Node, Transport, connect, [Host, Port, Options]),
Pid ! {self(), Error}.
+accepters(N) ->
+ accepters([], N).
+
+accepters(Acc, 0) ->
+ Acc;
+accepters(Acc, N) ->
+ receive
+ {accepter, _, Server} ->
+ accepters([Server| Acc], N-1)
+ end.
+
inet_port(Pid) when is_pid(Pid)->
receive
{Pid, {port, Port}} ->
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index e08f5dff78..004cacf7fc 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.4
+SSL_VSN = 5.3.5