aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_connection.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_connection.erl')
-rw-r--r--lib/ssl/src/ssl_connection.erl133
1 files changed, 63 insertions, 70 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index b7c1b9e8d0..f681204de6 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,7 +36,7 @@
-include_lib("public_key/include/public_key.hrl").
%% Setup
--export([connect/8, ssl_accept/7, handshake/2,
+-export([connect/8, ssl_accept/7, handshake/2, handshake/3,
socket_control/4]).
%% User Events
@@ -52,6 +52,7 @@
%% SSL all state functions
-export([handle_sync_event/4, handle_info/3, terminate/3]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -99,6 +100,20 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
Error ->
Error
end.
+
+%%--------------------------------------------------------------------
+-spec handshake(#sslsocket{}, #ssl_options{}, timeout()) -> ok | {error, reason()}.
+%%
+%% Description: Starts ssl handshake with some new options
+%%--------------------------------------------------------------------
+handshake(#sslsocket{pid = Pid}, SslOptions, Timeout) ->
+ case sync_send_all_state_event(Pid, {start, SslOptions, Timeout}) of
+ connected ->
+ ok;
+ Error ->
+ Error
+ end.
+
%--------------------------------------------------------------------
-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom()) ->
{ok, #sslsocket{}} | {error, reason()}.
@@ -275,12 +290,11 @@ hello(#hello_request{}, #state{role = client} = State0, Connection) ->
{Record, State} = Connection:next_record(State0),
Connection:next_state(hello, hello, Record, State);
-hello({common_client_hello, Type, ServerHelloExt, HashSign},
- #state{session = #session{cipher_suite = CipherSuite},
- negotiated_version = Version} = State, Connection) ->
- {KeyAlg, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- NegotiatedHashSign = negotiated_hashsign(HashSign, KeyAlg, Version),
+hello({common_client_hello, Type, ServerHelloExt, NegotiatedHashSign},
+ State, Connection) ->
do_server_hello(Type, ServerHelloExt,
+ %% Note NegotiatedHashSign is only negotiated for real if
+ %% if TLS version is at least TLS-1.2
State#state{hashsign_algorithm = NegotiatedHashSign}, Connection);
hello(timeout, State, _) ->
@@ -417,7 +431,8 @@ certify(#server_key_exchange{exchange_keys = Keys},
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
- ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
+ Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
+ Version, certify, State)
end
end;
@@ -426,8 +441,9 @@ certify(#server_key_exchange{} = Msg,
Connection:handle_unexpected_message(Msg, certify_server_keyexchange, State);
certify(#certificate_request{hashsign_algorithms = HashSigns},
- #state{session = #session{own_certificate = Cert}} = State0, Connection) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
+ #state{session = #session{own_certificate = Cert},
+ negotiated_version = Version} = State0, Connection) ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
Connection:next_state(certify, certify, Record,
State#state{cert_hashsign_algorithm = HashSign});
@@ -544,7 +560,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
tls_handshake_history = Handshake
} = State0, Connection) ->
- HashSign = ssl_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
+ HashSign = ssl_handshake:select_hashsign_algs(CertHashSign, Algo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
@@ -586,7 +602,7 @@ cipher(#finished{verify_data = Data} = Finished,
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0, Connection) ->
{Record, State} = Connection:next_record(State0#state{next_protocol = SelectedProtocol}),
- Connection:next_state(cipher, cipher, Record, State);
+ Connection:next_state(cipher, cipher, Record, State#state{expecting_next_protocol_negotiation = false});
cipher(timeout, State, _) ->
{next_state, cipher, State, hibernate};
@@ -649,6 +665,10 @@ handle_sync_event({start, Timeout}, StartFrom, StateName, State) ->
{next_state, StateName, State#state{start_or_recv_from = StartFrom,
timer = Timer}, get_timeout(State)};
+handle_sync_event({start, Opts, Timeout}, From, StateName, #state{ssl_options = SslOpts} = State) ->
+ NewOpts = new_ssl_options(Opts, SslOpts),
+ handle_sync_event({start, Timeout}, From, StateName, State#state{ssl_options = NewOpts});
+
handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State) ->
%% Run terminate before returning
%% so that the reuseaddr inet-option will work
@@ -677,7 +697,11 @@ handle_sync_event({shutdown, How0}, _, StateName,
Error ->
{stop, normal, Error, State}
end;
-
+
+handle_sync_event({recv, _N, _Timeout}, _RecvFrom, StateName,
+ #state{socket_options = #socket_options{active = Active}} = State) when Active =/= false ->
+ {reply, {error, einval}, StateName, State, get_timeout(State)};
+
handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName,
#state{protocol_cb = Connection} = State0) ->
Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
@@ -1540,64 +1564,10 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
session = Session}, cipher, Connection),
Connection:next_state_connection(cipher, ack_connection(State#state{session = Session})).
-negotiated_hashsign(undefined, Algo, Version) ->
- default_hashsign(Version, Algo);
-negotiated_hashsign(HashSign = {_, _}, _, _) ->
- HashSign.
-
-%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
-%% If the client does not send the signature_algorithms extension, the
-%% server MUST do the following:
-%%
-%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
-%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
-%% sent the value {sha1,rsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
-%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
-%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-
-default_hashsign(_Version = {Major, Minor}, KeyExchange)
- when Major >= 3 andalso Minor >= 3 andalso
- (KeyExchange == rsa orelse
- KeyExchange == dhe_rsa orelse
- KeyExchange == dh_rsa orelse
- KeyExchange == ecdhe_rsa orelse
- KeyExchange == ecdh_rsa orelse
- KeyExchange == srp_rsa) ->
- {sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == dh_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == srp_rsa ->
- {md5sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_ecdsa ->
- {sha, ecdsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dhe_dss;
- KeyExchange == dh_dss;
- KeyExchange == srp_dss ->
- {sha, dsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dh_anon;
- KeyExchange == ecdh_anon;
- KeyExchange == psk;
- KeyExchange == dhe_psk;
- KeyExchange == rsa_psk;
- KeyExchange == srp_anon ->
- {null, anon}.
-
select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
select_curve(_) ->
- {namedCurve, ?secp256k1}.
+ {namedCurve, ?secp256r1}.
is_anonymous(Algo) when Algo == dh_anon;
Algo == ecdh_anon;
@@ -1757,12 +1727,12 @@ handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport,
Connection:handle_close_alert(Data, StateName, State)
end.
-handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) ->
+handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
%% No trusted certs specified
ok;
handle_trusted_certs_db(#state{cert_db_ref = Ref,
cert_db = CertDb,
- ssl_options = #ssl_options{cacertfile = undefined}}) ->
+ ssl_options = #ssl_options{cacertfile = <<>>}}) ->
%% Certs provided as DER directly can not be shared
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
@@ -1854,3 +1824,26 @@ make_premaster_secret({MajVer, MinVer}, rsa) ->
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
+
+%% One day this can be maps instead, but we have to be backwards compatible for now
+new_ssl_options(New, Old) ->
+ new_ssl_options(tuple_to_list(New), tuple_to_list(Old), []).
+
+new_ssl_options([], [], Acc) ->
+ list_to_tuple(lists:reverse(Acc));
+new_ssl_options([undefined | Rest0], [Head1| Rest1], Acc) ->
+ new_ssl_options(Rest0, Rest1, [Head1 | Acc]);
+new_ssl_options([Head0 | Rest0], [_| Rest1], Acc) ->
+ new_ssl_options(Rest0, Rest1, [Head0 | Acc]).
+
+negotiated_hashsign(undefined, Alg, Version) ->
+ %% Not negotiated choose default
+ case is_anonymous(Alg) of
+ true ->
+ {null, anon};
+ false ->
+ ssl_handshake:select_hashsign_algs(Alg, Version)
+ end;
+negotiated_hashsign(HashSign = {_, _}, _, _) ->
+ HashSign.
+