aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/Makefile24
-rw-r--r--lib/ssl/src/ssl.appup.src19
-rw-r--r--lib/ssl/src/ssl.erl112
-rw-r--r--lib/ssl/src/ssl_connection.erl514
-rw-r--r--lib/ssl/src/ssl_handshake.erl189
-rw-r--r--lib/ssl/src/ssl_handshake.hrl9
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/ssl_manager.erl45
-rw-r--r--lib/ssl/src/ssl_record.erl5
-rw-r--r--lib/ssl/src/ssl_session.erl18
10 files changed, 516 insertions, 421 deletions
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index c5c5bf593a..043645be41 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -108,10 +108,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -130,3 +130,23 @@ release_spec: opt
release_docs_spec:
+# ----------------------------------------------------
+# Dependencies
+# ----------------------------------------------------
+$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl
+$(EBIN)/ssl.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl
+$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl
+$(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_connection.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_handshake.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl
+$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl
+$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
+$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
+
+
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 76550fa04b..9b1227fa7f 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,25 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"5.0.1", [{restart_application, ssl}]},
- {"5.0", [{restart_application, ssl}]},
+ {"5.1.1", [{restart_application, ssl}]
+ },
+ {"5.1", [
+ {load_module, ssl_connection, soft_purge, soft_purge, []}
+ ]
+ },
+ {<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
- {"5.0.1", [{restart_application, ssl}]},
- {"5.0", [{restart_application, ssl}]},
+ {"5.1.1", [{restart_application, ssl}]
+ },
+ {"5.1", [
+ {load_module, ssl_connection, soft_purge, soft_purge, []}
+ ]
+ },
+ {"5.1", [{restart_application, ssl}]},
+ {<<"5.0\\*">>, [{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 9a562aa5a8..09f2819ca8 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -28,14 +28,11 @@
cipher_suites/0, cipher_suites/1, suite_definition/1,
close/1, shutdown/2,
connect/3, connect/2, connect/4, connection_info/1,
- controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
+ controlling_process/2, listen/2, peername/1, peercert/1,
recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]).
-
--deprecated({pid, 1, next_major_release}).
-
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
@@ -47,7 +44,7 @@
-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
erl_cipher_suite/0, %% From ssl_cipher.hrl
tls_atom_version/0, %% From ssl_internal.hrl
- prf_random/0]).
+ prf_random/0, sslsocket/0]).
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -55,6 +52,8 @@
inet_ssl, %% inet options for internal ssl socket
cb %% Callback info
}).
+
+-type sslsocket() :: #sslsocket{}.
-type connect_option() :: socket_connect_option() | ssl_option() | transport_option().
-type socket_connect_option() :: gen_tcp:connect_option().
-type listen_option() :: socket_listen_option() | ssl_option() | transport_option().
@@ -165,7 +164,7 @@ listen(Port, Options0) ->
#config{cb={CbModule, _, _, _},inet_user=Options} = Config,
case CbModule:listen(Port, Options) of
{ok, ListenSocket} ->
- {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
+ {ok, #sslsocket{pid = {ListenSocket, Config}}};
Err = {error, _} ->
Err
end
@@ -245,18 +244,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
+close(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:close(Pid);
close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) ->
- CbMod:close(ListenSocket);
-close(#sslsocket{pid = Pid}) ->
- ssl_connection:close(Pid).
+ CbMod:close(ListenSocket).
%%--------------------------------------------------------------------
-spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
-send(#sslsocket{pid = Pid}, Data) ->
- ssl_connection:send(Pid, Data).
+send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) ->
+ ssl_connection:send(Pid, Data);
+send(#sslsocket{pid = {ListenSocket, #config{cb={CbModule, _, _, _}}}}, Data) ->
+ CbModule:send(ListenSocket, Data). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
@@ -266,8 +267,10 @@ send(#sslsocket{pid = Pid}, Data) ->
%%--------------------------------------------------------------------
recv(Socket, Length) ->
recv(Socket, Length, infinity).
-recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
- ssl_connection:recv(Pid, Length, Timeout).
+recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) ->
+ ssl_connection:recv(Pid, Length, Timeout);
+recv(#sslsocket{pid = {Listen, #config{cb={CbModule, _, _, _}}}}, _,_) when is_port(Listen)->
+ CbModule:recv(Listen, 0). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}.
@@ -275,8 +278,12 @@ recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
%% Description: Changes process that receives the messages when active = true
%% or once.
%%--------------------------------------------------------------------
-controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) ->
- ssl_connection:new_user(Pid, NewOwner).
+controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) ->
+ ssl_connection:new_user(Pid, NewOwner);
+controlling_process(#sslsocket{pid = {Listen,
+ #config{cb={CbModule, _, _, _}}}}, NewOwner) when is_port(Listen),
+ is_pid(NewOwner) ->
+ CbModule:controlling_process(Listen, NewOwner).
%%--------------------------------------------------------------------
-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
@@ -284,29 +291,35 @@ controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) ->
%%
%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
-connection_info(#sslsocket{pid = Pid}) ->
- ssl_connection:info(Pid).
+connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:info(Pid);
+connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
-peername(#sslsocket{pid = Pid}) ->
- ssl_connection:peername(Pid).
+peername(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid)->
+ inet:peername(Socket);
+peername(#sslsocket{pid = {ListenSocket, _}}) ->
+ inet:peername(ListenSocket). %% Will return {error, enotconn}
%%--------------------------------------------------------------------
-spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}.
%%
%% Description: Returns the peercert.
%%--------------------------------------------------------------------
-peercert(#sslsocket{pid = Pid}) ->
+peercert(#sslsocket{pid = Pid}) when is_pid(Pid) ->
case ssl_connection:peer_certificate(Pid) of
{ok, undefined} ->
{error, no_peercert};
Result ->
Result
- end.
+ end;
+peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
@@ -323,7 +336,7 @@ suite_definition(S) ->
%% Description: Returns the next protocol that has been negotiated. If no
%% protocol has been negotiated will return {error, next_protocol_not_negotiated}
%%--------------------------------------------------------------------
-negotiated_next_protocol(#sslsocket{fd = new_ssl, pid = Pid}) ->
+negotiated_next_protocol(#sslsocket{pid = Pid}) ->
ssl_connection:negotiated_next_protocol(Pid).
-spec cipher_suites() -> [erl_cipher_suite()].
@@ -396,8 +409,9 @@ setopts(#sslsocket{}, Options) ->
%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
-shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}, How) ->
- CbMod:shutdown(ListenSocket, How);
+shutdown(#sslsocket{pid = {Listen, #config{cb={CbMod,_, _, _}}}},
+ How) when is_port(Listen) ->
+ CbMod:shutdown(Listen, How);
shutdown(#sslsocket{pid = Pid}, How) ->
ssl_connection:shutdown(Pid, How).
@@ -406,11 +420,11 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
-sockname(#sslsocket{pid = {ListenSocket, _}}) ->
- inet:sockname(ListenSocket);
+sockname(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ inet:sockname(Listen);
-sockname(#sslsocket{pid = Pid}) ->
- ssl_connection:sockname(Pid).
+sockname(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid) ->
+ inet:sockname(Socket).
%%---------------------------------------------------------------
-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
@@ -418,12 +432,14 @@ sockname(#sslsocket{pid = Pid}) ->
%% Description: Returns list of session info currently [{session_id, session_id(),
%% {cipher_suite, cipher_suite()}]
%%--------------------------------------------------------------------
-session_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
- ssl_connection:session_info(Pid).
+session_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:session_info(Pid);
+session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
+ {error, enotconn}.
%%---------------------------------------------------------------
-spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} |
- {available, [tls_atom_version()]}].
+ {available, [tls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
@@ -439,8 +455,10 @@ versions() ->
%%
%% Description: Initiates a renegotiation.
%%--------------------------------------------------------------------
-renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
- ssl_connection:renegotiation(Pid).
+renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:renegotiation(Pid);
+renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec prf(#sslsocket{}, binary() | 'master_secret', binary(),
@@ -449,10 +467,11 @@ renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
%%
%% Description: use a ssl sessions TLS PRF to generate key material
%%--------------------------------------------------------------------
-prf(#sslsocket{pid = Pid, fd = new_ssl},
- Secret, Label, Seed, WantedLength) ->
- ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength).
-
+prf(#sslsocket{pid = Pid},
+ Secret, Label, Seed, WantedLength) when is_pid(Pid) ->
+ ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength);
+prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec clear_pem_cache() -> ok.
@@ -849,10 +868,10 @@ internal_inet_values() ->
socket_options(InetValues) ->
#socket_options{
- mode = proplists:get_value(mode, InetValues),
- header = proplists:get_value(header, InetValues),
- active = proplists:get_value(active, InetValues),
- packet = proplists:get_value(packet, InetValues),
+ 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)
}.
@@ -934,14 +953,3 @@ make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) ->
PreferredProtocol -> PreferredProtocol
end
end.
-
-%% Only used to remove exit messages from old ssl
-%% First is a nonsense clause to provide some
-%% backward compatibility for orber that uses this
-%% function in a none recommended way, but will
-%% work correctly if a valid pid is returned.
-%% Deprcated to be removed in r16
-pid(#sslsocket{fd = new_ssl}) ->
- whereis(ssl_connection_sup);
-pid(#sslsocket{pid = Pid}) ->
- Pid.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 23f22987df..94f76e0606 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -40,8 +40,7 @@
-export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2,
socket_control/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1, sockname/1, peername/1, renegotiation/1,
- negotiated_next_protocol/1, prf/5]).
+ peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]).
%% Called by ssl_connection_sup
-export([start_link/7]).
@@ -90,6 +89,7 @@
log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
start_or_recv_from, % "gen_fsm From"
+ timer, % start_or_recv_timer
send_queue, % queue()
terminated = false, %
allow_renegotiate = true,
@@ -120,7 +120,7 @@ send(Pid, Data) ->
sync_send_all_state_event(Pid, {application_data,
%% iolist_to_binary should really
%% be called iodata_to_binary()
- erlang:iolist_to_binary(Data)}, infinity).
+ erlang:iolist_to_binary(Data)}).
%%--------------------------------------------------------------------
-spec recv(pid(), integer(), timeout()) ->
@@ -129,7 +129,7 @@ send(Pid, Data) ->
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
recv(Pid, Length, Timeout) ->
- sync_send_all_state_event(Pid, {recv, Length}, Timeout).
+ sync_send_all_state_event(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
-spec connect(host(), inet:port_number(), port(), {#ssl_options{}, #socket_options{}},
pid(), tuple(), timeout()) ->
@@ -166,7 +166,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
handshake(#sslsocket{pid = Pid}, Timeout) ->
- case sync_send_all_state_event(Pid, start, Timeout) of
+ case sync_send_all_state_event(Pid, {start, Timeout}) of
connected ->
ok;
Error ->
@@ -181,7 +181,7 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
socket_control(Socket, Pid, CbModule) ->
case CbModule:controlling_process(Socket, Pid) of
ok ->
- {ok, sslsocket(Pid)};
+ {ok, sslsocket(Pid, Socket)};
{error, Reason} ->
{error, Reason}
end.
@@ -215,13 +215,7 @@ shutdown(ConnectionPid, How) ->
%%--------------------------------------------------------------------
new_user(ConnectionPid, User) ->
sync_send_all_state_event(ConnectionPid, {new_user, User}).
-%%--------------------------------------------------------------------
--spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
-%%
-%% Description: Same as inet:sockname/1
-%%--------------------------------------------------------------------
-sockname(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, sockname).
+
%%--------------------------------------------------------------------
-spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}.
%%
@@ -229,13 +223,7 @@ sockname(ConnectionPid) ->
%%--------------------------------------------------------------------
negotiated_next_protocol(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, negotiated_next_protocol).
-%%--------------------------------------------------------------------
--spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
-%%
-%% Description: Same as inet:peername/1
-%%--------------------------------------------------------------------
-peername(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, peername).
+
%%--------------------------------------------------------------------
-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
%%
@@ -344,15 +332,15 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
#state{}) -> gen_fsm_state_return().
%%--------------------------------------------------------------------
hello(start, #state{host = Host, port = Port, role = client,
- ssl_options = SslOpts,
- session = #session{own_certificate = Cert} = Session0,
- session_cache = Cache, session_cache_cb = CacheCb,
- transport_cb = Transport, socket = Socket,
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}} = State0) ->
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
-
+
Version = Hello#client_hello.client_version,
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
@@ -383,23 +371,22 @@ hello(#server_hello{cipher_suite = CipherSuite,
renegotiation = {Renegotiation, _},
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- #alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State0),
- {stop, normal, State0};
-
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State0),
+ {stop, {shutdown, own_alert}, State0};
{Version, NewId, ConnectionStates, NextProtocol} ->
{KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
-
- NewNextProtocol = case NextProtocol of
- undefined ->
- State0#state.next_protocol;
- _ ->
- NextProtocol
- end,
-
+
+ NewNextProtocol = case NextProtocol of
+ undefined ->
+ State0#state.next_protocol;
+ _ ->
+ NextProtocol
+ end,
+
State = State0#state{key_algorithm = KeyAlgorithm,
hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
negotiated_version = Version,
@@ -407,13 +394,13 @@ hello(#server_hello{cipher_suite = CipherSuite,
premaster_secret = PremasterSecret,
expecting_next_protocol_negotiation = NextProtocol =/= undefined,
next_protocol = NewNextProtocol},
-
+
case ssl_session:is_new(OldId, NewId) of
true ->
handle_new_session(NewId, CipherSuite, Compression,
State#state{connection_states = ConnectionStates});
false ->
- handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
+ handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
end
end;
@@ -432,8 +419,7 @@ hello(Hello = #client_hello{client_version = ClientVersion},
negotiated_version = Version,
session = Session});
#alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State),
- {stop, normal, State}
+ handle_own_alert(Alert, ClientVersion, hello, State)
end;
hello(timeout, State) ->
@@ -464,8 +450,7 @@ abbreviated(#finished{verify_data = Data} = Finished,
next_state_connection(abbreviated,
ack_connection(State#state{connection_states = ConnectionStates}));
#alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, abbreviated, State)
end;
abbreviated(#finished{verify_data = Data} = Finished,
@@ -485,8 +470,7 @@ abbreviated(#finished{verify_data = Data} = Finished,
connection_states =
ConnectionStates}));
#alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, abbreviated, State)
end;
abbreviated(timeout, State) ->
@@ -510,8 +494,7 @@ certify(#certificate{asn1_certificates = []},
fail_if_no_peer_cert = true}} =
State) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State};
+ handle_own_alert(Alert, Version, certify, State);
certify(#certificate{asn1_certificates = []},
#state{role = server,
@@ -534,8 +517,7 @@ certify(#certificate{} = Cert,
handle_peer_cert(PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false});
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(#server_key_exchange{} = KeyExchangeMsg,
@@ -547,8 +529,7 @@ certify(#server_key_exchange{} = KeyExchangeMsg,
{Record, State} = next_record(State1),
next_state(certify, certify, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(#server_key_exchange{} = Msg,
@@ -572,8 +553,7 @@ certify(#server_hello_done{},
State = State0#state{connection_states = ConnectionStates},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
%% Master secret is calculated from premaster_secret
@@ -591,8 +571,7 @@ certify(#server_hello_done{},
session = Session},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(#client_key_exchange{} = Msg,
@@ -608,8 +587,7 @@ certify(#client_key_exchange{exchange_keys = Keys},
certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, certify, State)
end;
@@ -634,8 +612,7 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
{Record, State} = next_record(State1),
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
@@ -648,8 +625,7 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl
{Record, State} = next_record(State1),
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end.
%%--------------------------------------------------------------------
@@ -678,8 +654,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
{Record, State} = next_record(State0),
next_state(cipher, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, cipher, State0)
end;
% client must send a next protocol message if we are expecting it
@@ -705,8 +680,7 @@ cipher(#finished{verify_data = Data} = Finished,
Session = register_session(Role, Host, Port, Session0),
cipher_role(Role, Data, Session, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, cipher, State)
end;
% only allowed to send next_protocol message after change cipher spec
@@ -801,8 +775,10 @@ handle_sync_event({application_data, Data}, From, StateName,
State#state{send_queue = queue:in({From, Data}, Queue)},
get_timeout(State)};
-handle_sync_event(start, StartFrom, hello, State) ->
- hello(start, State#state{start_or_recv_from = StartFrom});
+handle_sync_event({start, Timeout}, StartFrom, hello, State) ->
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ hello(start, State#state{start_or_recv_from = StartFrom,
+ timer = Timer});
%% The two clauses below could happen if a server upgrades a socket in
%% active mode. Note that in this case we are lucky that
@@ -811,13 +787,16 @@ handle_sync_event(start, StartFrom, hello, State) ->
%% mode before telling the client that it is willing to upgrade
%% and before calling ssl:ssl_accept/2. These clauses are
%% here to make sure it is the users problem and not owers if
-%% they upgrade a active socket.
-handle_sync_event(start, _, connection, State) ->
+%% 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{}}) ->
+handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) ->
{stop, {shutdown, Error}, {error, Error}, State};
-handle_sync_event(start, StartFrom, StateName, State) ->
- {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(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(close, _, StateName, State) ->
%% Run terminate before returning
@@ -848,13 +827,17 @@ handle_sync_event({shutdown, How0}, _, StateName,
{stop, normal, Error, State}
end;
-handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) ->
- passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName);
+handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName, State0) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ 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}, RecvFrom, StateName, State) ->
- {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom},
+handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ {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,
@@ -870,19 +853,10 @@ handle_sync_event({get_opts, OptTags}, _From, StateName,
OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
{reply, OptsReply, StateName, State, get_timeout(State)};
-handle_sync_event(sockname, _From, StateName,
- #state{socket = Socket} = State) ->
- SockNameReply = inet:sockname(Socket),
- {reply, SockNameReply, 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(peername, _From, StateName,
- #state{socket = Socket} = State) ->
- PeerNameReply = inet:peername(Socket),
- {reply, PeerNameReply, StateName, State, get_timeout(State)};
handle_sync_event({set_opts, Opts0}, _From, StateName,
#state{socket_options = Opts1,
@@ -985,7 +959,7 @@ handle_info({Protocol, _, Data}, StateName,
next_state(StateName, StateName, Record, State);
#alert{} = Alert ->
handle_normal_shutdown(Alert, StateName, State0),
- {stop, normal, State0}
+ {stop, {shutdown, own_alert}, State0}
end;
handle_info({CloseTag, Socket}, StateName,
@@ -1006,12 +980,12 @@ handle_info({CloseTag, Socket}, StateName,
ok
end,
handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, transport_closed}, State};
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, start_or_recv_from = StartFrom, role = Role,
error_tag = ErrorTag} = State) when StateName =/= connection ->
- alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
+ alert_user(Socket, StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -1027,7 +1001,21 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
handle_info(allow_renegotiate, StateName, State) ->
{next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)};
-
+
+handle_info({cancel_start_or_recv, StartFrom}, StateName,
+ #state{renegotiation = {false, first}} = State) when StateName =/= connection ->
+ gen_fsm:reply(StartFrom, {error, timeout}),
+ {stop, {shutdown, user_timeout}, State#state{timer = undefined}};
+
+handle_info({cancel_start_or_recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom} = State) ->
+ gen_fsm:reply(RecvFrom, {error, timeout}),
+ {next_state, StateName, State#state{start_or_recv_from = undefined,
+ bytes_to_read = undefined,
+ timer = undefined}, get_timeout(State)};
+
+handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
+ {next_state, StateName, State#state{timer = undefined}, get_timeout(State)};
+
handle_info(Msg, StateName, State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
error_logger:info_report(Report),
@@ -1044,6 +1032,20 @@ 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),
+ handle_trusted_certs_db(State),
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate);
+
+terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue,
+ renegotiation = Renegotiate} = State) ->
+ handle_trusted_certs_db(State),
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate);
+
terminate(Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
@@ -1054,16 +1056,14 @@ terminate(Reason, connection, #state{negotiated_version = Version,
notify_renegotiater(Renegotiate),
BinAlert = terminate_alert(Reason, Version, ConnectionStates),
Transport:send(Socket, BinAlert),
- workaround_transport_delivery_problems(Socket, Transport, Reason),
- Transport:close(Socket);
+ workaround_transport_delivery_problems(Socket, Transport);
-terminate(Reason, _StateName, #state{transport_cb = Transport,
+terminate(_Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket).
%%--------------------------------------------------------------------
@@ -1238,18 +1238,13 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
end.
sync_send_all_state_event(FsmPid, Event) ->
- sync_send_all_state_event(FsmPid, Event, infinity).
-
-sync_send_all_state_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
catch
exit:{noproc, _} ->
{error, closed};
- exit:{timeout, _} ->
- {error, timeout};
exit:{normal, _} ->
{error, closed};
- exit:{shutdown, _} ->
+ exit:{{shutdown, _},_} ->
{error, closed}
end.
@@ -1346,8 +1341,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
next_state(hello, certify, Record, State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
resumed_server_hello(#state{session = Session,
@@ -1367,8 +1361,7 @@ resumed_server_hello(#state{session = Session,
{Record, State} = next_record(State2),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) ->
@@ -1393,8 +1386,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session = Session}),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
@@ -1411,8 +1403,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
next_state(certify, cipher, Record, State)
catch
throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end.
do_client_certify_and_key_exchange(State0) ->
@@ -1637,78 +1628,49 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
ssl_record:set_server_verify_data(current_write, Data, ConnectionStates).
-handle_server_key(#server_key_exchange{params =
- #server_dh_params{dh_p = P,
- dh_g = G,
- dh_y = ServerPublicDhKey},
- signed_params = <<>>},
- #state{key_algorithm = dh_anon} = State) ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
-
-handle_server_key(
- #server_key_exchange{params =
- #server_dh_params{dh_p = P,
- dh_g = G,
- dh_y = ServerPublicDhKey},
- signed_params = Signed,
- hashsign = HashSign},
- #state{negotiated_version = Version,
- public_key_info = PubKeyInfo,
- connection_states = ConnectionStates} = State) ->
-
- PLen = size(P),
- GLen = size(G),
- YLen = size(ServerPublicDhKey),
- HashAlgo = connection_hash_algo(HashSign, State),
+handle_server_key(#server_key_exchange{exchange_keys = Keys},
+ #state{key_algorithm = KeyAlg,
+ negotiated_version = Version} = State) ->
+ Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version),
+ HashSign = connection_hashsign(Params#server_key_params.hashsign, State),
+ case HashSign of
+ {_, anon} ->
+ server_master_secret(Params#server_key_params.params, State);
+ _ ->
+ verify_server_key(Params, HashSign, State)
+ end.
- ConnectionState =
+verify_server_key(#server_key_params{params = Params,
+ params_bin = EncParams,
+ signature = Signature},
+ HashSign = {HashAlgo, _},
+ #state{negotiated_version = Version,
+ public_key_info = PubKeyInfo,
+ connection_states = ConnectionStates} = State) ->
+ ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
- <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen),
- ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ case ssl_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of
true ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
+ server_master_secret(Params, State);
false ->
?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams})
- when Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey);
-verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) ->
- case public_key:decrypt_public(Signed, PubKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
- true;
- _ ->
- false
- end;
-verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}).
+server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey},
+ State) ->
+ dh_master_secret(P, G, ServerPublicDhKey, undefined, State).
-dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
- PMpint = mpint_binary(Prime),
- GMpint = mpint_binary(Base),
- Keys = {_, PrivateDhKey} =
- crypto:dh_generate_key([PMpint,GMpint]),
- dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
-
-dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
- #state{session = Session,
- negotiated_version = Version, role = Role,
- connection_states = ConnectionStates0} = State) ->
- PremasterSecret =
- crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
- [PMpint, GMpint]),
+master_from_premaster_secret(PremasterSecret,
+ #state{session = Session,
+ negotiated_version = Version, role = Role,
+ connection_states = ConnectionStates0} = State) ->
case ssl_handshake:master_secret(Version, PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
@@ -1720,6 +1682,19 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
Alert
end.
+dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
+ PMpint = mpint_binary(Prime),
+ GMpint = mpint_binary(Base),
+ Keys = {_, PrivateDhKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
+
+dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) ->
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
+ [PMpint, GMpint]),
+ master_from_premaster_secret(PremasterSecret, State).
+
cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(cipher, ack_connection(State#state{session = Session,
@@ -1787,10 +1762,12 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
end.
read_application_data(Data, #state{user_application = {_Mon, Pid},
- socket_options = SOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- user_data_buffer = Buffer0} = State0) ->
+ socket = Socket,
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ timer = Timer,
+ user_data_buffer = Buffer0} = State0) ->
Buffer1 = if
Buffer0 =:= <<>> -> Data;
Data =:= <<>> -> Buffer0;
@@ -1798,10 +1775,12 @@ 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(SOpts, ClientData, Pid, RecvFrom),
+ SocketOpt = deliver_app_data(Socket, SOpts, ClientData, Pid, RecvFrom),
+ cancel_timer(Timer),
State = State0#state{user_data_buffer = Buffer,
start_or_recv_from = undefined,
- bytes_to_read = 0,
+ timer = undefined,
+ bytes_to_read = undefined,
socket_options = SocketOpt
},
if
@@ -1814,8 +1793,10 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end;
{more, Buffer} -> % no reply, we need more data
next_record(State0#state{user_data_buffer = Buffer});
+ {passive, Buffer} ->
+ next_record_if_active(State0#state{user_data_buffer = Buffer});
{error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom),
+ deliver_packet_error(Socket, SOpts, Buffer1, Pid, RecvFrom),
{stop, normal, State0}
end.
@@ -1855,6 +1836,9 @@ is_time_to_renegotiate(_,_) ->
%% Picks ClientData
get_data(_, _, <<>>) ->
{more, <<>>};
+%% Recv timed out save buffer data until next recv
+get_data(#socket_options{active=false}, undefined, Buffer) ->
+ {passive, Buffer};
get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
if
@@ -1894,9 +1878,9 @@ decode_packet(Type, Buffer, PacketOpts) ->
%% Note that if the user has explicitly configured the socket to expect
%% HTTP headers using the {packet, httph} option, we don't do any automatic
%% switching of states.
-deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
- Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_reply(SOpts, Data)),
+deliver_app_data(Socket, SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(Socket, SOpts, Data)),
SO = case Data of
{P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
((Type =:= http) or (Type =:= http_bin)) ->
@@ -1915,31 +1899,31 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
SO
end.
-format_reply(#socket_options{active = false, mode = Mode, packet = Packet,
+format_reply(_,#socket_options{active = false, mode = Mode, packet = Packet,
header = Header}, Data) ->
- {ok, format_reply(Mode, Packet, Header, Data)};
-format_reply(#socket_options{active = _, mode = Mode, packet = Packet,
+ {ok, do_format_reply(Mode, Packet, Header, Data)};
+format_reply(Socket, #socket_options{active = _, mode = Mode, packet = Packet,
header = Header}, Data) ->
- {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}.
+ {ssl, sslsocket(self(), Socket), do_format_reply(Mode, Packet, Header, Data)}.
-deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_packet_error(SO, Data)).
+deliver_packet_error(Socket, SO= #socket_options{active = Active}, Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Socket, SO, Data)).
-format_packet_error(#socket_options{active = false, mode = Mode}, Data) ->
- {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}};
-format_packet_error(#socket_options{active = _, mode = Mode}, Data) ->
- {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}.
+format_packet_error(_,#socket_options{active = false, mode = Mode}, Data) ->
+ {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
+format_packet_error(Socket, #socket_options{active = _, mode = Mode}, Data) ->
+ {ssl_error, sslsocket(self(), Socket), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
-format_reply(binary, _, N, Data) when N > 0 -> % Header mode
+do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
header(N, Data);
-format_reply(binary, _, _, Data) ->
+do_format_reply(binary, _, _, Data) ->
Data;
-format_reply(list, Packet, _, Data)
+do_format_reply(list, Packet, _, Data)
when Packet == http; Packet == {http, headers};
Packet == http_bin; Packet == {http_bin, headers};
Packet == httph; Packet == httph_bin ->
Data;
-format_reply(list, _,_, Data) ->
+do_format_reply(list, _,_, Data) ->
binary_to_list(Data).
header(0, <<>>) ->
@@ -1983,8 +1967,7 @@ handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]}
end.
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
- handle_own_alert(Alert, Version, Current, State),
- {stop, normal, State};
+ handle_own_alert(Alert, Version, Current, State);
next_state(_,Next, no_record, State) ->
{next_state, Next, State, get_timeout(State)};
@@ -2022,8 +2005,7 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
handle_tls_handshake(Handle, Next, State)
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, Current, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, Current, State0)
end;
next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) ->
@@ -2162,7 +2144,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
tls_record_buffer = <<>>,
tls_cipher_texts = [],
user_application = {Monitor, User},
- bytes_to_read = 0,
user_data_buffer = <<>>,
log_alert = true,
session_cache_cb = SessionCacheCb,
@@ -2171,11 +2152,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
send_queue = queue:new()
}.
-sslsocket(Pid) ->
- #sslsocket{pid = Pid, fd = new_ssl}.
-
-sslsocket() ->
- sslsocket(self()).
+sslsocket(Pid, Socket) ->
+ #sslsocket{pid = Pid, fd = Socket}.
get_socket_opts(_,[], _, Acc) ->
{ok, Acc};
@@ -2271,24 +2249,24 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{start_or_recv_from = From, host = Host, port = Port, session = Session,
- user_application = {_Mon, Pid},
+ #state{socket = Socket, start_or_recv_from = From, host = Host,
+ port = Port, session = Session, user_application = {_Mon, Pid},
log_alert = Log, role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
log_alert(Log, StateName, Alert),
- alert_user(StateName, Opts, Pid, From, Alert, Role),
+ alert_user(Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
StateName, State) ->
handle_normal_shutdown(Alert, StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{log_alert = Log, renegotiation = {true, internal}} = State) ->
log_alert(Log, StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{log_alert = Log, renegotiation = {true, From}} = State0) ->
@@ -2303,28 +2281,28 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta
{Record, State} = next_record(State0),
next_state(StateName, StateName, Record, State).
-alert_user(connection, Opts, Pid, From, Alert, Role) ->
- alert_user(Opts#socket_options.active, Pid, From, Alert, Role);
-alert_user(_, _, _, From, Alert, Role) ->
- alert_user(From, Alert, Role).
+alert_user(Socket, connection, Opts, Pid, From, Alert, Role) ->
+ alert_user(Socket, Opts#socket_options.active, Pid, From, Alert, Role);
+alert_user(Socket,_, _, _, From, Alert, Role) ->
+ alert_user(Socket, From, Alert, Role).
-alert_user(From, Alert, Role) ->
- alert_user(false, no_pid, From, Alert, Role).
+alert_user(Socket, From, Alert, Role) ->
+ alert_user(Socket, false, no_pid, From, Alert, Role).
-alert_user(false = Active, Pid, From, Alert, Role) ->
+alert_user(_Socket, 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(Active, Pid, From, Alert, Role) ->
+alert_user(Socket, Active, Pid, From, Alert, Role) ->
case ssl_alert:reason_code(Alert, Role) of
closed ->
send_or_reply(Active, Pid, From,
- {ssl_closed, sslsocket()});
+ {ssl_closed, sslsocket(self(), Socket)});
ReasonCode ->
send_or_reply(Active, Pid, From,
- {ssl_error, sslsocket(), ReasonCode})
+ {ssl_error, sslsocket(self(), Socket), ReasonCode})
end.
log_alert(true, Info, Alert) ->
@@ -2341,8 +2319,8 @@ handle_own_alert(Alert, Version, StateName,
try %% Try to tell the other side
{BinMsg, _} =
encode_alert(Alert, Version, ConnectionStates),
- linux_workaround_transport_delivery_problems(Alert, Socket),
- Transport:send(Socket, BinMsg)
+ Transport:send(Socket, BinMsg),
+ workaround_transport_delivery_problems(Socket, Transport)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
end,
@@ -2351,20 +2329,23 @@ handle_own_alert(Alert, Version, StateName,
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
- end.
+ end,
+ {stop, {shutdown, own_alert}, State}.
-handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) ->
- alert_user(StartFrom, Alert, Role);
+handle_normal_shutdown(Alert, _, #state{socket = Socket,
+ start_or_recv_from = StartFrom,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Socket, StartFrom, Alert, Role);
-handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts,
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
user_application = {_Mon, Pid},
start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role).
+ alert_user(Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, {Info, Msg}, State),
- {stop, normal, State}.
+ handle_own_alert(Alert, Version, {Info, Msg}, State).
make_premaster_secret({MajVer, MinVer}, rsa) ->
Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
@@ -2385,9 +2366,11 @@ ack_connection(#state{renegotiation = {true, From}} = State) ->
gen_fsm:reply(From, ok),
State#state{renegotiation = undefined};
ack_connection(#state{renegotiation = {false, first},
- start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined ->
+ start_or_recv_from = StartFrom,
+ timer = Timer} = State) when StartFrom =/= undefined ->
gen_fsm:reply(StartFrom, connected),
- State#state{renegotiation = undefined, start_or_recv_from = undefined};
+ cancel_timer(Timer),
+ State#state{renegotiation = undefined, start_or_recv_from = undefined, timer = undefined};
ack_connection(State) ->
State.
@@ -2422,36 +2405,35 @@ notify_renegotiater({true, From}) when not is_atom(From) ->
notify_renegotiater(_) ->
ok.
-terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown;
+terminate_alert(Reason, Version, ConnectionStates) when Reason == normal;
Reason == user_close ->
{BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates),
BinAlert;
+terminate_alert({shutdown, _}, Version, ConnectionStates) ->
+ {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ Version, ConnectionStates),
+ BinAlert;
+
terminate_alert(_, Version, ConnectionStates) ->
{BinAlert, _} = encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
Version, ConnectionStates),
BinAlert.
-workaround_transport_delivery_problems(_,_, user_close) ->
- ok;
-workaround_transport_delivery_problems(Socket, Transport, _) ->
+workaround_transport_delivery_problems(Socket, gen_tcp = Transport) ->
%% Standard trick to try to make sure all
- %% data sent to to tcp port is really sent
- %% before tcp port is closed so that the peer will
- %% get a correct error message.
+ %% data sent to the tcp port is really delivered to the
+ %% peer application before tcp port is closed so that the peer will
+ %% get the correct TLS alert message and not only a transport close.
inet:setopts(Socket, [{active, false}]),
Transport:shutdown(Socket, write),
- Transport:recv(Socket, 0).
-
-linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) ->
- case os:type() of
- {unix, linux} ->
- inet:setopts(Socket, [{nodelay, true}]);
- _ ->
- ok
- end;
-linux_workaround_transport_delivery_problems(_, _) ->
- ok.
+ %% Will return when other side has closed or after 30 s
+ %% e.g. we do not want to hang if something goes wrong
+ %% with the network but we want to maximise the odds that
+ %% peer application gets all data sent on the tcp connection.
+ Transport:recv(Socket, 0, 30000);
+workaround_transport_delivery_problems(Socket, Transport) ->
+ Transport:close(Socket).
get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
infinity;
@@ -2487,10 +2469,10 @@ get_pending_connection_state_prf(CStates, Direction) ->
CS = ssl_record:pending_connection_state(CStates, Direction),
CS#connection_state.security_parameters#security_parameters.prf_algorithm.
-connection_hash_algo({HashAlgo, _}, _State) ->
- HashAlgo;
-connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) ->
- HashAlgo.
+connection_hashsign(HashSign = {_, _}, _State) ->
+ HashSign;
+connection_hashsign(_, #state{hashsign_algorithm = HashSign}) ->
+ HashSign.
%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
%% If the client does not send the signature_algorithms extension, the
@@ -2524,3 +2506,31 @@ default_hashsign(_Version, KeyExchange)
default_hashsign(_Version, KeyExchange)
when KeyExchange == dh_anon ->
{null, anon}.
+
+start_or_recv_cancel_timer(infinity, _RecvFrom) ->
+ undefined;
+start_or_recv_cancel_timer(Timeout, RecvFrom) ->
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
+
+cancel_timer(undefined) ->
+ ok;
+cancel_timer(Timer) ->
+ erlang:cancel_timer(Timer).
+
+handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
+ inet:setopts(Socket, [{active, false}]),
+ case Transport:recv(Socket, 0, 0) of
+ {error, closed} ->
+ ok;
+ {ok, Data} ->
+ handle_close_alert(Data, StateName, State)
+ end.
+
+handle_close_alert(Data, StateName, State0) ->
+ case next_tls_record(Data, State0) of
+ {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
+ [Alert|_] = decode_alerts(EncAlerts),
+ handle_normal_shutdown(Alert, StateName, State);
+ _ ->
+ ok
+ end.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index fa1784714f..1929370991 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -32,10 +32,10 @@
-export([master_secret/4, client_hello/8, server_hello/5, hello/4,
hello_request/0, certify/7, certificate/4,
- client_certificate_verify/6, certificate_verify/6,
+ client_certificate_verify/6, certificate_verify/6, verify_signature/5,
certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
finished/5, verify_connection/6, get_tls_handshake/3,
- decode_client_key/3, server_hello_done/0,
+ decode_client_key/3, decode_server_key/3, server_hello_done/0,
encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
decrypt_premaster_secret/2, prf/5, next_protocol/1]).
@@ -320,25 +320,36 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of
+certificate_verify(Signature, PublicKeyInfo, Version,
+ HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
+ Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of
true ->
valid;
_ ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
- end;
-certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of
- true ->
- valid;
- false ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
+%%--------------------------------------------------------------------
+-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+ public_key_info()) -> true | false.
+%%
+%% Description: Checks that a public_key signature is valid.
+%%--------------------------------------------------------------------
+verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) ->
+ true;
+verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey);
+verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+ case public_key:decrypt_public(Signature, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash -> true;
+ _ -> false
+ end;
+verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
+
%%--------------------------------------------------------------------
-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) ->
@@ -382,31 +393,33 @@ key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) ->
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
<<?UINT32(_), GBin/binary>> = crypto:mpint(G),
- PLen = byte_size(PBin),
- GLen = byte_size(GBin),
- YLen = byte_size(PublicKey),
ServerDHParams = #server_dh_params{dh_p = PBin,
dh_g = GBin, dh_y = PublicKey},
+ enc_server_key_exchange(Version, ServerDHParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey).
+enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
+ ClientRandom, ServerRandom, PrivateKey) ->
+ EncParams = enc_server_key(Params),
case HashAlgo of
null ->
- #server_key_exchange{params = ServerDHParams,
- signed_params = <<>>,
- hashsign = {null, anon}};
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
_ ->
Hash =
server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), PBin/binary,
- ?UINT16(GLen), GBin/binary,
- ?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
- #server_key_exchange{params = ServerDHParams,
- signed_params = Signed,
- hashsign = {HashAlgo, SignAlgo}}
+ ServerRandom/binary,
+ EncParams/binary>>),
+ Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {HashAlgo, SignAlgo},
+ signature = Signature}
end.
%%--------------------------------------------------------------------
@@ -523,6 +536,15 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
+-spec decode_server_key(binary(), key_algo(), tls_version()) ->
+ #server_key_params{}.
+%%
+%% Description: Decode server_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_server_key(ServerKey, Type, Version) ->
+ dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
-spec init_handshake_history() -> tls_handshake_history().
%%
@@ -975,31 +997,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
next_protocol_negotiation = NextProtocolNegotiation};
dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-
-dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = <<>>, hashsign = {null, anon}};
-dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?BYTE(HashAlgo), ?BYTE(SignAlgo),
- ?UINT16(Len), Sig:Len/binary>>)
- when Major == 3, Minor >= 3 ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = Sig,
- hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}};
-dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?UINT16(Len), Sig:Len/binary>>) ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = Sig, hashsign = undefined};
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
+ #server_key_exchange{exchange_keys = Keys};
dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
@@ -1039,6 +1038,42 @@ dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
#client_diffie_hellman_public{dh_public = DH_Y}.
+dec_ske_params(Len, Keys, Version) ->
+ <<Params:Len/bytes, Signature/binary>> = Keys,
+ dec_ske_signature(Params, Signature, Version).
+
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(0)>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, <<>>};
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Signature:Len/binary>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, Signature};
+dec_ske_signature(Params, <<>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(0)>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) ->
+ {Params, undefined, Signature};
+dec_ske_signature(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+dec_server_key(<<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
+ Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
dec_hello_extensions(<<>>) ->
[];
dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
@@ -1156,18 +1191,12 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
ASN1Certs = certs_from_list(ASN1CertList),
ACLen = erlang:iolist_size(ASN1Certs),
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
-enc_hs(#server_key_exchange{params = #server_dh_params{
- dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams, hashsign = HashSign}, Version) ->
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- Signature = enc_sign(HashSign, SignedParams, Version),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen), Y/binary,
- Signature/binary>>
- };
+enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) ->
+ {?SERVER_KEY_EXCHANGE, Keys};
+enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign,
+ signature = Signature}, Version) ->
+ EncSign = enc_sign(HashSign, Signature, Version),
+ {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>};
enc_hs(#certificate_request{certificate_types = CertTypes,
hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
certificate_authorities = CertAuths},
@@ -1211,6 +1240,14 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/binary>>.
+enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>.
+
+enc_sign({_, anon}, _Sign, _Version) ->
+ <<>>;
enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
when Major == 3, Minor >= 3->
SignLen = byte_size(Signature),
@@ -1328,8 +1365,8 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
public_key:sign({digest, Hash}, HashAlgo, Key);
-digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) ->
- public_key:sign({digest, Hash}, sha, Key);
+digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
[{rsa_pad, rsa_pkcs1_padding}]).
@@ -1378,19 +1415,6 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, sha, Signature, PublicKey);
-certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey);
-certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) ->
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes -> true;
- _ -> false
- end.
-
-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
@@ -1401,6 +1425,7 @@ default_hash_signs() ->
[?TLSEXT_SIGALG(sha512),
?TLSEXT_SIGALG(sha384),
?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha224),
?TLSEXT_SIGALG(sha),
?TLSEXT_SIGALG_DSA(sha),
?TLSEXT_SIGALG_RSA(md5)]}.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 9af6511d68..2414d5b666 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -141,9 +141,14 @@
}).
-record(server_key_exchange, {
+ exchange_keys
+ }).
+
+-record(server_key_params, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params, %% #signature{}
- hashsign %% term(atom(), atom())
+ params_bin,
+ hashsign, %% term(atom(), atom())
+ signature %% #signature{}
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index a5db2dcee7..ed0dc34adf 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -69,8 +69,8 @@
-define(TRUE, 0).
-define(FALSE, 1).
--define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16
-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
-record(ssl_options, {
versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 0cf4f2ce33..14fba72d86 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -24,8 +24,6 @@
-module(ssl_manager).
-behaviour(gen_server).
--include("ssl_internal.hrl").
-
%% Internal application API
-export([start_link/1, start_link_dist/1,
connection_init/2, cache_pem_file/2,
@@ -144,8 +142,14 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
new_session_id(Port) ->
call({new_session_id, Port}).
+%%--------------------------------------------------------------------
+-spec clean_cert_db(reference(), binary()) -> term().
+%%
+%% Description: Send clean request of cert db to ssl_manager process should
+%% be called by ssl-connection processes.
+%%--------------------------------------------------------------------
clean_cert_db(Ref, File) ->
- erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}).
+ erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}).
%%--------------------------------------------------------------------
-spec register_session(inet:port_number(), #session{}) -> ok.
@@ -322,19 +326,12 @@ handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) ->
handle_info({clean_cert_db, Ref, File},
#state{certificate_db = [CertDb,RefDb, PemCache]} = State) ->
- case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
- 0 ->
- MD5 = crypto:md5(File),
- case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of
- [{Content, Ref}] ->
- ssl_certificate_db:insert(MD5, Content, PemCache);
- undefined ->
- ok
- end,
- ssl_certificate_db:remove(Ref, RefDb),
- ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+
+ case ssl_certificate_db:lookup(Ref, RefDb) of
+ undefined -> %% Alredy cleaned
+ ok;
_ ->
- ok
+ clean_cert_db(Ref, CertDb, RefDb, PemCache, File)
end,
{noreply, State};
@@ -466,3 +463,19 @@ new_id(Port, Tries, Cache, CacheCb) ->
_ ->
new_id(Port, Tries - 1, Cache, CacheCb)
end.
+
+clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
+ case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
+ 0 ->
+ MD5 = crypto:md5(File),
+ case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of
+ [{Content, Ref}] ->
+ ssl_certificate_db:insert(MD5, Content, PemCache);
+ _ ->
+ ok
+ end,
+ ssl_certificate_db:remove(Ref, RefDb),
+ ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+ _ ->
+ ok
+ end.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 8e93ce4634..173b9611c6 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -463,10 +463,9 @@ supported_protocol_versions() ->
supported_protocol_versions([]) ->
Vsns = case sufficient_tlsv1_2_crypto_support() of
true ->
- %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16
- ?DEFAULT_SUPPORTED_VERSIONS;
+ ?ALL_SUPPORTED_VERSIONS;
false ->
- ?DEFAULT_SUPPORTED_VERSIONS
+ ?MIN_SUPPORTED_VERSIONS
end,
application:set_env(ssl, protocol_version, Vsns),
Vsns;
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 2ad422fc03..a24b2d9444 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -72,15 +72,12 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
server_id(Port, <<>>, _SslOpts, _Cert, _, _) ->
{ssl_manager:new_session_id(Port), undefined};
-server_id(Port, SuggestedId,
- #ssl_options{reuse_sessions = ReuseEnabled,
- reuse_session = ReuseFun},
- Cert, Cache, CacheCb) ->
+server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) ->
LifeTime = case application:get_env(ssl, session_lifetime) of
{ok, Time} when is_integer(Time) -> Time;
_ -> ?'24H_in_sec'
end,
- case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun,
+ case is_resumable(SuggestedId, Port, Options,
Cache, CacheCb, LifeTime, Cert)
of
{true, Resumed} ->
@@ -112,9 +109,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
[[Id, _]|_] -> Id
end.
-is_resumable(_, _, false, _, _, _, _, _) ->
+is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) ->
{false, undefined};
-is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache,
+is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache,
CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
@@ -125,6 +122,7 @@ is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache,
case resumable(IsResumable)
andalso (OwnCert == SessionOwnCert)
andalso valid_session(Session, SecondLifeTime)
+ andalso reusable_options(Options, Session)
andalso ReuseFun(SuggestedSessionId, PeerCert,
Compression, CipherSuite)
of
@@ -139,3 +137,9 @@ resumable(new) ->
false;
resumable(IsResumable) ->
IsResumable.
+
+reusable_options(#ssl_options{fail_if_no_peer_cert = true,
+ verify = verify_peer}, Session) ->
+ (Session#session.peer_certificate =/= undefined);
+reusable_options(_,_) ->
+ true.