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.src15
-rw-r--r--lib/ssl/src/ssl.erl203
-rw-r--r--lib/ssl/src/ssl_alert.erl11
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl51
-rw-r--r--lib/ssl/src/ssl_connection.erl342
-rw-r--r--lib/ssl/src/ssl_handshake.erl400
-rw-r--r--lib/ssl/src/ssl_handshake.hrl29
-rw-r--r--lib/ssl/src/ssl_internal.hrl6
-rw-r--r--lib/ssl/src/ssl_manager.erl48
-rw-r--r--lib/ssl/src/ssl_record.erl5
-rw-r--r--lib/ssl/src/ssl_session.erl18
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl50
13 files changed, 762 insertions, 440 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 9b1227fa7f..76e14860ec 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,24 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"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}]}
],
[
- {"5.1.1", [{restart_application, ssl}]
- },
- {"5.1", [
- {load_module, ssl_connection, soft_purge, soft_purge, []}
- ]
- },
- {"5.1", [{restart_application, ssl}]},
+ {<<"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 66ceb2a591..09f2819ca8 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -28,16 +28,15 @@
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]).
-
--deprecated({pid, 1, next_major_release}).
+ renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]).
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -67,7 +66,9 @@
{keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
{cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
{ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()} | {hibernate_after, integer()|undefined}.
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {next_protocols_advertised, list(binary())} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
@@ -163,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
@@ -243,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()}.
@@ -264,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()}.
@@ -273,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()}} |
@@ -282,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().
@@ -316,6 +331,14 @@ suite_definition(S) ->
{KeyExchange, Cipher, Hash}.
%%--------------------------------------------------------------------
+-spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
+%%
+%% 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{pid = Pid}) ->
+ ssl_connection:negotiated_next_protocol(Pid).
+
-spec cipher_suites() -> [erl_cipher_suite()].
-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
@@ -386,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).
@@ -396,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()}.
@@ -408,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.
%%--------------------------------------------------------------------
@@ -429,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(),
@@ -439,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.
@@ -596,7 +625,9 @@ handle_options(Opts0, _Role) ->
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, []),
hibernate_after = handle_option(hibernate_after, Opts, undefined),
- erl_dist = handle_option(erl_dist, Opts, false)
+ erl_dist = handle_option(erl_dist, Opts, false),
+ next_protocols_advertised = handle_option(next_protocols_advertised, Opts, undefined),
+ next_protocol_selector = make_next_protocol_selector(handle_option(client_preferred_next_protocols, Opts, undefined))
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -605,7 +636,8 @@ handle_options(Opts0, _Role) ->
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist],
+ cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, next_protocols_advertised,
+ client_preferred_next_protocols],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -730,12 +762,64 @@ validate_option(hibernate_after, undefined) ->
undefined;
validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
-validate_option(erl_dist,Value) when Value == true;
+validate_option(erl_dist,Value) when Value == true;
Value == false ->
Value;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols} = Value)
+ when is_list(PreferredProtocols) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ {Precedence, PreferredProtocols, ?NO_PROTOCOL}
+ end;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols, Default} = Value)
+ when is_list(PreferredProtocols), is_binary(Default),
+ byte_size(Default) > 0, byte_size(Default) < 256 ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ Value
+ end;
+
+validate_option(client_preferred_next_protocols, undefined) ->
+ undefined;
+validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(next_protocols_advertised, Value),
+ Value
+ end;
+
+validate_option(next_protocols_advertised, undefined) ->
+ undefined;
validate_option(Opt, Value) ->
throw({error, {eoptions, {Opt, Value}}}).
-
+
+validate_npn_ordering(client) ->
+ ok;
+validate_npn_ordering(server) ->
+ ok;
+validate_npn_ordering(Value) ->
+ throw({error, {eoptions, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
+
+validate_binary_list(Opt, List) ->
+ lists:foreach(
+ fun(Bin) when is_binary(Bin),
+ byte_size(Bin) > 0,
+ byte_size(Bin) < 256 ->
+ ok;
+ (Bin) ->
+ throw({error, {eoptions, {Opt, {invalid_protocol, Bin}}}})
+ end, List).
+
validate_versions([], Versions) ->
Versions;
validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
@@ -841,14 +925,31 @@ cipher_suites(Version, Ciphers0) ->
no_format(Error) ->
lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
-
-%% 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.
+
+detect(_Pred, []) ->
+ undefined;
+detect(Pred, [H|T]) ->
+ case Pred(H) of
+ true ->
+ H;
+ _ ->
+ detect(Pred, T)
+ end.
+
+make_next_protocol_selector(undefined) ->
+ undefined;
+make_next_protocol_selector({client, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AdvertisedProtocols) end, AllProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end;
+
+make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AllProtocols) end, AdvertisedProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 222b3f1ad7..f94a1136a0 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.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
@@ -36,8 +36,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec reason_code(#alert{}, client | server) -> closed | esslconnect |
- esslaccept | string().
+-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
%% user.
@@ -45,12 +44,8 @@
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
-reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) ->
- esslconnect;
-reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) ->
- esslaccept;
reason_code(#alert{description = Description}, _) ->
- description_txt(Description).
+ {essl, description_txt(Description)}.
%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 67d00f0da7..ff36b5ee26 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.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
@@ -18,10 +18,11 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Storage for trused certificats
+%% Purpose: Storage for trusted certificates
%%----------------------------------------------------------------------
-module(ssl_certificate_db).
+
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
@@ -37,7 +38,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec create() -> [db_handle()].
+-spec create() -> [db_handle(),...].
%%
%% Description: Creates a new certificate db.
%% Note: lookup_trusted_cert/4 may be called from any process but only
@@ -54,7 +55,7 @@ create() ->
].
%%--------------------------------------------------------------------
--spec remove([db_handle()]) -> term().
+-spec remove([db_handle()]) -> ok.
%%
%% Description: Removes database db
%%--------------------------------------------------------------------
@@ -114,8 +115,8 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
new_trusted_cert_entry({MD5, File}, Db)
end.
%%--------------------------------------------------------------------
--spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term().
--spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term().
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}.
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}.
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
@@ -131,19 +132,25 @@ cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
insert(MD5, {Content, Ref}, PemChache),
{ok, Content}.
+%%--------------------------------------------------------------------
+-spec remove_trusted_certs(reference(), db_handle()) -> ok.
+%%
+%% Description: Removes all trusted certificates refernced by <Ref>.
+%%--------------------------------------------------------------------
remove_trusted_certs(Ref, CertsDb) ->
remove_certs(Ref, CertsDb).
%%--------------------------------------------------------------------
--spec remove(term(), db_handle()) -> term().
+-spec remove(term(), db_handle()) -> ok.
%%
%% Description: Removes an element in a <Db>.
%%--------------------------------------------------------------------
remove(Key, Db) ->
- _ = ets:delete(Db, Key).
+ ets:delete(Db, Key),
+ ok.
%%--------------------------------------------------------------------
--spec lookup(term(), db_handle()) -> term() | undefined.
+-spec lookup(term(), db_handle()) -> [term()] | undefined.
%%
%% Description: Looks up an element in a <Db>.
%%--------------------------------------------------------------------
@@ -158,7 +165,7 @@ lookup(Key, Db) ->
[Pick(Data) || Data <- Contents]
end.
%%--------------------------------------------------------------------
--spec foldl(fun(), term(), db_handle()) -> term().
+-spec foldl(fun((_,_) -> term()), term(), db_handle()) -> term().
%%
%% Description: Calls Fun(Elem, AccIn) on successive elements of the
%% cache, starting with AccIn == Acc0. Fun/2 must return a new
@@ -178,12 +185,13 @@ ref_count(Key, Db, N) ->
ets:update_counter(Db,Key,N).
%%--------------------------------------------------------------------
--spec clear(db_handle()) -> term().
+-spec clear(db_handle()) -> ok.
%%
%% Description: Clears the cache
%%--------------------------------------------------------------------
clear(Db) ->
- ets:delete_all_objects(Db).
+ true = ets:delete_all_objects(Db),
+ ok.
%%--------------------------------------------------------------------
-spec db_size(db_handle()) -> integer().
@@ -194,30 +202,35 @@ db_size(Db) ->
ets:info(Db, size).
%%--------------------------------------------------------------------
-%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return().
+-spec insert(Key::term(), Data::term(), Db::db_handle()) -> ok.
%%
%% Description: Inserts data into <Db>
%%--------------------------------------------------------------------
insert(Key, Data, Db) ->
- true = ets:insert(Db, {Key, Data}).
+ true = ets:insert(Db, {Key, Data}),
+ ok.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
update_counter(Key, Count, Db) ->
- true = ets:insert(Db, {Key, Count}).
+ true = ets:insert(Db, {Key, Count}),
+ ok.
remove_certs(Ref, CertsDb) ->
- ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
+ true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}),
+ ok.
add_certs_from_der(DerList, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
- [Add(Cert) || Cert <- DerList].
+ [Add(Cert) || Cert <- DerList],
+ ok.
add_certs_from_pem(PemEntries, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
- [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries].
-
+ [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
+ ok.
+
add_certs(Cert, Ref, CertsDb) ->
try ErlCert = public_key:pkix_decode_cert(Cert, otp),
TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index d4784604fd..e5a6181a88 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.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
@@ -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,
- prf/5]).
+ peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]).
%% Called by ssl_connection_sup
-export([start_link/7]).
@@ -93,7 +92,9 @@
timer, % start_or_recv_timer
send_queue, % queue()
terminated = false, %
- allow_renegotiate = true
+ allow_renegotiate = true,
+ expecting_next_protocol_negotiation = false :: boolean(),
+ next_protocol = undefined :: undefined | binary()
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
@@ -180,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.
@@ -214,20 +215,15 @@ 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 peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
+-spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}.
%%
-%% Description: Same as inet:peername/1
+%% Description: Returns the negotiated protocol
%%--------------------------------------------------------------------
-peername(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, peername).
+negotiated_next_protocol(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, negotiated_next_protocol).
+
%%--------------------------------------------------------------------
-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
%%
@@ -375,16 +371,28 @@ hello(#server_hello{cipher_suite = CipherSuite,
renegotiation = {Renegotiation, _},
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- {Version, NewId, ConnectionStates} ->
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, 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,
+
State = State0#state{key_algorithm = KeyAlgorithm,
hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
negotiated_version = Version,
connection_states = ConnectionStates,
- premaster_secret = PremasterSecret},
+ premaster_secret = PremasterSecret,
+ expecting_next_protocol_negotiation = NextProtocol =/= undefined,
+ next_protocol = NewNextProtocol},
case ssl_session:is_new(OldId, NewId) of
true ->
@@ -392,13 +400,10 @@ hello(#server_hello{cipher_suite = CipherSuite,
State#state{connection_states = ConnectionStates});
false ->
handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
- end;
- #alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State0),
- {stop, {shutdown, own_alert}, State0}
+ end
end;
-hello(Hello = #client_hello{client_version = ClientVersion},
+hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -407,8 +412,8 @@ hello(Hello = #client_hello{client_version = ClientVersion},
ssl_options = SslOpts}) ->
case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, Session}, ConnectionStates} ->
- do_server_hello(Type, State#state{connection_states =
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise} ->
+ do_server_hello(Type, ProtocolsToAdvertise, State#state{connection_states =
ConnectionStates,
negotiated_version = Version,
session = Session});
@@ -584,6 +589,7 @@ certify(#client_key_exchange{exchange_keys = Keys},
handle_own_alert(Alert, Version, certify, State)
end;
+
certify(timeout, State) ->
{ next_state, certify, State, hibernate };
@@ -650,6 +656,12 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
handle_own_alert(Alert, Version, cipher, State0)
end;
+% client must send a next protocol message if we are expecting it
+cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
+ next_protocol = undefined, negotiated_version = Version} = State0) ->
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0),
+ {stop, normal, State0};
+
cipher(#finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
host = Host,
@@ -670,6 +682,13 @@ cipher(#finished{verify_data = Data} = Finished,
handle_own_alert(Alert, Version, cipher, State)
end;
+% only allowed to send next_protocol message after change cipher spec
+% & before finished message and it is not allowed during renegotiation
+cipher(#next_protocol{selected_protocol = SelectedProtocol},
+ #state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
+ {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
+ next_state(cipher, cipher, Record, State);
+
cipher(timeout, State) ->
{ next_state, cipher, State, hibernate };
@@ -833,15 +852,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(peername, _From, StateName,
- #state{socket = Socket} = State) ->
- PeerNameReply = inet:peername(Socket),
- {reply, PeerNameReply, 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, StateName,
#state{socket_options = Opts1,
@@ -970,7 +984,7 @@ handle_info({CloseTag, Socket}, StateName,
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,
@@ -1120,7 +1134,7 @@ init_certificates(#ssl_options{cacerts = CaCerts,
{ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
+ handle_file_error(?LINE, Error, Reason, CACertFile, {ecacertfile, Reason},
erlang:get_stacktrace())
end,
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role).
@@ -1142,7 +1156,7 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
+ handle_file_error(?LINE, Error, Reason, CertFile, {ecertfile, Reason},
erlang:get_stacktrace())
end;
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
@@ -1161,7 +1175,7 @@ init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
private_key(public_key:pem_entry_decode(PemEntry, Password))
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, KeyFile, ekeyfile,
+ handle_file_error(?LINE, Error, Reason, KeyFile, {ekeyfile, Reason},
erlang:get_stacktrace())
end;
@@ -1219,7 +1233,7 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason,
- DHParamFile, edhfile, erlang:get_stacktrace())
+ DHParamFile, {edhfile, Reason}, erlang:get_stacktrace())
end.
sync_send_all_state_event(FsmPid, Event) ->
@@ -1291,17 +1305,18 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
-do_server_hello(Type, #state{negotiated_version = Version,
- session = #session{session_id = SessId},
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}}
- = State0) when is_atom(Type) ->
+do_server_hello(Type, NextProtocolsToSend, #state{negotiated_version = Version,
+ session = #session{session_id = SessId},
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}}
+ = State0) when is_atom(Type) ->
ServerHello =
ssl_handshake:server_hello(SessId, Version,
- ConnectionStates0, Renegotiation),
- State = server_hello(ServerHello, State0),
-
+ ConnectionStates0, Renegotiation, NextProtocolsToSend),
+ State = server_hello(ServerHello,
+ State0#state{expecting_next_protocol_negotiation =
+ NextProtocolsToSend =/= undefined}),
case Type of
new ->
new_server_hello(ServerHello, State);
@@ -1551,12 +1566,33 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State.
finalize_handshake(State, StateName) ->
- ConnectionStates0 = cipher_protocol(State),
+ ConnectionStates0 = cipher_protocol(State),
+
ConnectionStates =
ssl_record:activate_pending_connection_state(ConnectionStates0,
write),
- finished(State#state{connection_states = ConnectionStates}, StateName).
-
+
+ State1 = State#state{connection_states = ConnectionStates},
+ State2 = next_protocol(State1),
+ finished(State2, StateName).
+
+next_protocol(#state{role = server} = State) ->
+ State;
+next_protocol(#state{next_protocol = undefined} = State) ->
+ State;
+next_protocol(#state{expecting_next_protocol_negotiation = false} = State) ->
+ State;
+next_protocol(#state{transport_cb = Transport, socket = Socket,
+ negotiated_version = Version,
+ next_protocol = NextProtocol,
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
+ NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
+ {BinMsg, ConnectionStates, Handshake} = encode_handshake(NextProtocolMessage, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake}.
+
cipher_protocol(#state{connection_states = ConnectionStates0,
socket = Socket,
negotiated_version = Version,
@@ -1591,78 +1627,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}).
-
-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});
+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(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} ->
@@ -1674,6 +1681,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,
@@ -1741,6 +1761,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
end.
read_application_data(Data, #state{user_application = {_Mon, Pid},
+ socket = Socket,
socket_options = SOpts,
bytes_to_read = BytesToRead,
start_or_recv_from = RecvFrom,
@@ -1753,7 +1774,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(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,
@@ -1774,7 +1795,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(SOpts, Buffer1, Pid, RecvFrom),
+ deliver_packet_error(Socket, SOpts, Buffer1, Pid, RecvFrom),
{stop, normal, State0}
end.
@@ -1856,9 +1877,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)) ->
@@ -1877,31 +1898,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, <<>>) ->
@@ -2072,8 +2093,8 @@ next_state_is_connection(_, State =
next_state_is_connection(StateName, State0) ->
{Record, State} = next_record_if_active(State0),
next_state(StateName, connection, Record, State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history()}).
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history()}).
register_session(client, Host, Port, #session{is_resumable = new} = Session0) ->
Session = Session0#session{is_resumable = true},
@@ -2130,11 +2151,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};
@@ -2230,12 +2248,12 @@ 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,
@@ -2262,28 +2280,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) ->
@@ -2313,13 +2331,16 @@ handle_own_alert(Alert, Version, StateName,
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),
@@ -2447,10 +2468,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
@@ -2488,12 +2509,13 @@ default_hashsign(_Version, KeyExchange)
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}).
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
cancel_timer(undefined) ->
ok;
cancel_timer(Timer) ->
- erlang:cancel_timer(Timer).
+ erlang:cancel_timer(Timer),
+ ok.
handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
inet:setopts(Socket, [{active, false}]),
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index bb26302fff..1929370991 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -30,21 +30,21 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/8, server_hello/4, hello/4,
+-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]).
+ decrypt_premaster_secret/2, prf/5, next_protocol/1]).
-export([dec_hello_extensions/2]).
-type tls_handshake() :: #client_hello{} | #server_hello{} |
#server_hello_done{} | #certificate{} | #certificate_request{} |
#client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{}.
+ #hello_request{} | #next_protocol{}.
%%====================================================================
%% Internal application API
@@ -77,18 +77,31 @@ client_hello(Host, Port, ConnectionStates,
cipher_suites = cipher_suites(Ciphers, Renegotiation),
compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
+
renegotiation_info =
renegotiation_info(client, ConnectionStates, Renegotiation),
- hash_signs = default_hash_signs()
+ hash_signs = default_hash_signs(),
+ next_protocol_negotiation =
+ encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation)
}.
+encode_protocol(Protocol, Acc) ->
+ Len = byte_size(Protocol),
+ <<Acc/binary, ?BYTE(Len), Protocol/binary>>.
+
+encode_protocols_advertised_on_server(undefined) ->
+ undefined;
+
+encode_protocols_advertised_on_server(Protocols) ->
+ #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
%%--------------------------------------------------------------------
-spec server_hello(session_id(), tls_version(), #connection_states{},
- boolean()) -> #server_hello{}.
+ boolean(), [binary()] | undefined) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
+server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdvertisedOnServer) ->
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
#server_hello{server_version = Version,
@@ -98,7 +111,8 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
random = SecParams#security_parameters.server_random,
session_id = SessionId,
renegotiation_info =
- renegotiation_info(server, ConnectionStates, Renegotiation)
+ renegotiation_info(server, ConnectionStates, Renegotiation),
+ next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer)
}.
%%--------------------------------------------------------------------
@@ -113,20 +127,21 @@ hello_request() ->
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{}, binary()},
- boolean()) -> {tls_version(), session_id(), #connection_states{}}|
- {tls_version(), {resumed | new, #session{}},
- #connection_states{}} | #alert{}.
+ atom(), #connection_states{}, binary()},
+ boolean()) ->
+ {tls_version(), session_id(), #connection_states{}, binary() | undefined}|
+ {tls_version(), {resumed | new, #session{}}, #connection_states{}, list(binary()) | undefined} |
+ #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
session_id = SessionId, renegotiation_info = Info,
- hash_signs = _HashSigns},
- #ssl_options{secure_renegotiate = SecureRenegotation},
+ hash_signs = _HashSigns} = Hello,
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
ConnectionStates0, Renegotiation) ->
-%%TODO: select hash and signature algorigthm
+ %%TODO: select hash and signature algorigthm
case ssl_record:is_acceptable_version(Version) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
@@ -135,7 +150,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
ConnectionStates =
hello_pending_connection_states(client, Version, CipherSuite, Random,
Compression, ConnectionStates1),
- {Version, SessionId, ConnectionStates};
+ case handle_next_protocol(Hello, NextProtocolSelector, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {Version, SessionId, ConnectionStates, Protocol}
+ end;
#alert{} = Alert ->
Alert
end;
@@ -145,9 +165,8 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
hello(#client_hello{client_version = ClientVersion, random = Random,
cipher_suites = CipherSuites,
- renegotiation_info = Info,
- hash_signs = _HashSigns} = Hello,
- #ssl_options{versions = Versions,
+ renegotiation_info = Info} = Hello,
+ #ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
%% TODO: select hash and signature algorithm
@@ -173,7 +192,12 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
Random,
Compression,
ConnectionStates1),
- {Version, {Type, Session}, ConnectionStates};
+ case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
+ #alert{} = Alert ->
+ Alert;
+ ProtocolsToAdvertise ->
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise}
+ end;
#alert{} = Alert ->
Alert
end
@@ -296,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()) ->
@@ -358,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.
%%--------------------------------------------------------------------
@@ -427,6 +464,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
end.
+-spec next_protocol(binary()) -> #next_protocol{}.
+
+next_protocol(SelectedProtocol) ->
+ #next_protocol{selected_protocol = SelectedProtocol}.
+
%%--------------------------------------------------------------------
-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
#finished{}.
@@ -494,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().
%%
@@ -660,6 +711,57 @@ renegotiation_info(server, ConnectionStates, true) ->
#renegotiation_info{renegotiated_connection = undefined}
end.
+decode_next_protocols({next_protocol_negotiation, Protocols}) ->
+ decode_next_protocols(Protocols, []).
+decode_next_protocols(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
+ case Len of
+ 0 ->
+ {error, invalid_next_protocols};
+ _ ->
+ decode_next_protocols(Rest, [Protocol|Acc])
+ end;
+decode_next_protocols(_Bytes, _Acc) ->
+ {error, invalid_next_protocols}.
+
+next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) ->
+ NextProtocolSelector =/= undefined andalso not Renegotiating.
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = undefined}, _Renegotiation, _SslOpts) ->
+ undefined;
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = {next_protocol_negotiation, <<>>}},
+ false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ Protocols;
+
+handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = undefined},
+ _NextProtocolSelector, _Renegotiating) ->
+ undefined;
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = Protocols},
+ NextProtocolSelector, Renegotiating) ->
+
+ case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of
+ true ->
+ select_next_protocol(decode_next_protocols(Protocols), NextProtocolSelector);
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension
+ end.
+
+select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+select_next_protocol(Protocols, NextProtocolSelector) ->
+ case NextProtocolSelector(Protocols) of
+ ?NO_PROTOCOL ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ Protocol when is_binary(Protocol) ->
+ Protocol
+ end.
+
handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -816,17 +918,21 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(_Version, ?HELLO_REQUEST, <<>>) ->
+dec_hs(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), SelectedProtocol:SelectedProtocolLength/binary,
+ ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) ->
+ #next_protocol{selected_protocol = SelectedProtocol};
+
+dec_hs(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
#client_hello{client_version = {Major, Minor},
random = ssl_ssl2:client_random(ChallengeData, CDLength),
session_id = 0,
@@ -839,20 +945,22 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
- HelloExtensions = dec_hello_extensions(Extensions),
- RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
- undefined),
- HashSigns = proplists:get_value(hash_signs, HelloExtensions,
- undefined),
+
+ DecodedExtensions = dec_hello_extensions(Extensions),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined),
+ HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined),
+
#client_hello{
- client_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suites = from_2bytes(CipherSuites),
- compression_methods = Comp_methods,
- renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns
- };
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suites = from_2bytes(CipherSuites),
+ compression_methods = Comp_methods,
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation
+ };
dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
@@ -868,7 +976,7 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method),
+ Cipher_suite:2/binary, ?BYTE(Comp_method),
?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
HelloExtensions = dec_hello_extensions(Extensions, []),
@@ -876,6 +984,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
undefined),
HashSigns = proplists:get_value(hash_signs, HelloExtensions,
undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined),
+
#server_hello{
server_version = {Major,Minor},
random = Random,
@@ -883,34 +993,12 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns};
+ hash_signs = HashSigns,
+ 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,
@@ -950,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>>) ->
@@ -959,6 +1083,9 @@ dec_hello_extensions(_) ->
dec_hello_extensions(<<>>, Acc) ->
Acc;
+dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
+ Prop = {next_protocol_negotiation, #next_protocol_negotiation{extension_data = ExtensionData}},
+ dec_hello_extensions(Rest, [Prop | Acc]);
dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
RenegotiateInfo = case Len of
1 -> % Initial handshake
@@ -982,6 +1109,7 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
+
dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
dec_hello_extensions(Rest, Acc);
%% This theoretically should not happen if the protocol is followed, but if it does it is ignored.
@@ -1014,6 +1142,11 @@ certs_from_list(ACList) ->
<<?UINT24(CertLen), Cert/binary>>
end || Cert <- ACList]).
+enc_hs(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
+ PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
+
+ {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
+ ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
enc_hs(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_hs(#client_hello{client_version = {Major, Minor},
@@ -1022,19 +1155,21 @@ enc_hs(#client_hello{client_version = {Major, Minor},
cipher_suites = CipherSuites,
compression_methods = CompMethods,
renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns}, _Version) ->
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions0 = hello_extensions(RenegotiationInfo),
+ Extensions0 = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
Extensions1 = if
Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
true -> Extensions0
end,
ExtensionsBin = enc_hello_extensions(Extensions1),
- {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
@@ -1044,9 +1179,10 @@ enc_hs(#server_hello{server_version = {Major, Minor},
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo}, _Version) ->
+ renegotiation_info = RenegotiationInfo,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SID_length = byte_size(Session_ID),
- Extensions = hello_extensions(RenegotiationInfo),
+ Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
@@ -1055,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},
@@ -1110,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),
@@ -1119,8 +1257,9 @@ enc_sign(_HashSign, Sign, _Version) ->
SignLen = byte_size(Sign),
<<?UINT16(SignLen), Sign/binary>>.
-hello_extensions(undefined) ->
- [];
+hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
+ hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
+
%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
@@ -1129,6 +1268,11 @@ hello_extensions(#renegotiation_info{} = Info) ->
hello_extensions(#hash_sign_algos{} = Info) ->
[Info].
+next_protocol_extension(undefined) ->
+ [];
+next_protocol_extension(#next_protocol_negotiation{} = Info) ->
+ [Info].
+
enc_hello_extensions(Extensions) ->
enc_hello_extensions(Extensions, <<>>).
enc_hello_extensions([], <<>>) ->
@@ -1137,6 +1281,9 @@ enc_hello_extensions([], Acc) ->
Size = byte_size(Acc),
<<?UINT16(Size), Acc/binary>>;
+enc_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
+ Len = byte_size(ExtensionData),
+ enc_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>);
enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
Len = byte_size(Info),
enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
@@ -1151,8 +1298,15 @@ enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest],
{Hash, Sign} <- HashSignAlgos >>,
ListLen = byte_size(SignAlgoList),
Len = ListLen + 2,
- enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
+ enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
+encode_client_protocol_negotiation(undefined, _) ->
+ undefined;
+encode_client_protocol_negotiation(_, false) ->
+ #next_protocol_negotiation{extension_data = <<>>};
+encode_client_protocol_negotiation(_, _) ->
+ undefined.
from_3bytes(Bin3) ->
from_3bytes(Bin3, []).
@@ -1211,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}]).
@@ -1261,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}).
@@ -1284,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 cc17dc2975..2414d5b666 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -33,6 +33,8 @@
-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
-type tls_handshake_history() :: {[binary()], [binary()]}.
+-define(NO_PROTOCOL, <<>>).
+
%% Signature algorithms
-define(ANON, 0).
-define(RSA, 1).
@@ -97,7 +99,8 @@
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
renegotiation_info,
- hash_signs % supported combinations of hashes/signature algos
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
-record(server_hello, {
@@ -107,7 +110,8 @@
cipher_suite, % cipher_suites
compression_method, % compression_method
renegotiation_info,
- hash_signs % supported combinations of hashes/signature algos
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -137,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;
@@ -234,6 +243,18 @@
hash_sign_algos
}).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Next Protocol Negotiation
+%% (http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-02)
+%% (http://technotes.googlecode.com/git/nextprotoneg.html)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(NEXTPROTONEG_EXT, 13172).
+-define(NEXT_PROTOCOL, 67).
+-record(next_protocol_negotiation, {extension_data}).
+
+-record(next_protocol, {selected_protocol}).
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index b8f2ae3b51..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
@@ -106,7 +106,9 @@
% after which ssl_connection will
% go into hibernation
%% This option should only be set to true by inet_tls_dist
- erl_dist = false
+ erl_dist = false,
+ next_protocols_advertised = undefined, %% [binary()],
+ next_protocol_selector = undefined %% fun([binary()]) -> binary())
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 0cf4f2ce33..aa9da65bb8 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,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
new_session_id(Port) ->
call({new_session_id, Port}).
+%%--------------------------------------------------------------------
+-spec clean_cert_db(reference(), binary()) -> ok.
+%%
+%% 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}),
+ ok.
%%--------------------------------------------------------------------
-spec register_session(inet:port_number(), #session{}) -> ok.
@@ -322,19 +327,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};
@@ -347,7 +345,7 @@ handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
--spec terminate(reason(), #state{}) -> term().
+-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
@@ -466,3 +464,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.
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a8476b104f..a22af6b960 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -89,7 +89,7 @@ handle_call({connect, Ip, Port}, {From, _}, State) ->
ok ->
flush_old_controller(From, Socket),
{reply, Res, State}
- end;
+ end;
{Pid, Error} ->
{reply, Error, State}
end;
@@ -115,13 +115,13 @@ code_change(_OldVsn, St, _Extra) ->
get_tcp_address(Socket) ->
case inet:sockname(Socket) of
{ok, Address} ->
- {ok, Host} = inet:gethostname(),
+ {ok, Host} = inet:gethostname(),
NetAddress = #net_address{
- address = Address,
- host = Host,
- protocol = proxy,
- family = inet
- },
+ address = Address,
+ host = Host,
+ protocol = proxy,
+ family = inet
+ },
{ok, NetAddress};
{error, _} = Error -> Error
end.
@@ -129,17 +129,17 @@ get_tcp_address(Socket) ->
accept_loop(Proxy, erts = Type, Listen, Extra) ->
process_flag(priority, max),
case gen_tcp:accept(Listen) of
- {ok, Socket} ->
- Extra ! {accept,self(),Socket,inet,proxy},
- receive
- {_Kernel, controller, Pid} ->
- ok = gen_tcp:controlling_process(Socket, Pid),
- flush_old_controller(Pid, Socket),
- Pid ! {self(), controller};
- {_Kernel, unsupported_protocol} ->
- exit(unsupported_protocol)
- end;
- Error ->
+ {ok, Socket} ->
+ Extra ! {accept,self(),Socket,inet,proxy},
+ receive
+ {_Kernel, controller, Pid} ->
+ ok = gen_tcp:controlling_process(Socket, Pid),
+ flush_old_controller(Pid, Socket),
+ Pid ! {self(), controller};
+ {_Kernel, unsupported_protocol} ->
+ exit(unsupported_protocol)
+ end;
+ Error ->
exit(Error)
end,
accept_loop(Proxy, Type, Listen, Extra);
@@ -242,7 +242,7 @@ loop_conn(World, Erts) ->
ssl:close(World);
{ssl_closed, World} ->
gen_tcp:close(Erts)
- end.
+ end.
get_ssl_options(Type) ->
case init:get_argument(ssl_dist_opt) of
@@ -255,7 +255,7 @@ get_ssl_options(Type) ->
ssl_options(_,[]) ->
[];
ssl_options(server, ["client_" ++ _, _Value |T]) ->
- ssl_options(server,T);
+ ssl_options(server,T);
ssl_options(client, ["server_" ++ _, _Value|T]) ->
ssl_options(client,T);
ssl_options(server, ["server_certfile", Value|T]) ->
@@ -265,7 +265,7 @@ ssl_options(client, ["client_certfile", Value | T]) ->
ssl_options(server, ["server_cacertfile", Value|T]) ->
[{cacertfile, Value} | ssl_options(server,T)];
ssl_options(client, ["client_cacertfile", Value|T]) ->
- [{cacertfile, Value} | ssl_options(client,T)];
+ [{cacertfile, Value} | ssl_options(client,T)];
ssl_options(server, ["server_keyfile", Value|T]) ->
[{keyfile, Value} | ssl_options(server,T)];
ssl_options(client, ["client_keyfile", Value|T]) ->
@@ -277,7 +277,7 @@ ssl_options(client, ["client_password", Value|T]) ->
ssl_options(server, ["server_verify", Value|T]) ->
[{verify, atomize(Value)} | ssl_options(server,T)];
ssl_options(client, ["client_verify", Value|T]) ->
- [{verify, atomize(Value)} | ssl_options(client,T)];
+ [{verify, atomize(Value)} | ssl_options(client,T)];
ssl_options(server, ["server_reuse_sessions", Value|T]) ->
[{reuse_sessions, atomize(Value)} | ssl_options(server,T)];
ssl_options(client, ["client_reuse_sessions", Value|T]) ->
@@ -295,11 +295,11 @@ ssl_options(server, ["server_hibernate_after", Value|T]) ->
ssl_options(client, ["client_hibernate_after", Value|T]) ->
[{hibernate_after, list_to_integer(Value)} | ssl_options(client,T)];
ssl_options(server, ["server_ciphers", Value|T]) ->
- [{ciphers, Value} | ssl_options(server,T)];
+ [{ciphers, Value} | ssl_options(server,T)];
ssl_options(client, ["client_ciphers", Value|T]) ->
[{ciphers, Value} | ssl_options(client,T)];
ssl_options(server, ["server_dhfile", Value|T]) ->
- [{dhfile, Value} | ssl_options(server,T)];
+ [{dhfile, Value} | ssl_options(server,T)];
ssl_options(server, ["server_fail_if_no_peer_cert", Value|T]) ->
[{fail_if_no_peer_cert, atomize(Value)} | ssl_options(server,T)];
ssl_options(_,_) ->