diff options
Diffstat (limited to 'lib/ssl/src')
| -rw-r--r-- | lib/ssl/src/Makefile | 20 | ||||
| -rw-r--r-- | lib/ssl/src/ssl.appup.src | 15 | ||||
| -rw-r--r-- | lib/ssl/src/ssl.erl | 98 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_connection.erl | 360 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 1 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_manager.erl | 2 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_session.erl | 18 |
7 files changed, 281 insertions, 233 deletions
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index c5c5bf593a..6be8a1456e 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -130,3 +130,23 @@ release_spec: opt release_docs_spec: +# ---------------------------------------------------- +# Dependencies +# ---------------------------------------------------- +$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl +$(EBIN)/ssl.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl +$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_connection.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_handshake.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl +$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl +$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl + + diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 76550fa04b..c118c129e8 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,14 +1,21 @@ %% -*- erlang -*- {"%VSN%", [ - {"5.0.1", [{restart_application, ssl}]}, - {"5.0", [{restart_application, ssl}]}, + {"5.1", [ + {load_module, ssl_connection, soft_purge, soft_purge, []} + ] + }, + {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"5.0.1", [{restart_application, ssl}]}, - {"5.0", [{restart_application, ssl}]}, + {"5.1", [ + {load_module, ssl_connection, soft_purge, soft_purge, []} + ] + }, + {"5.1", [{restart_application, ssl}]}, + {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 9a562aa5a8..771bfa5739 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -165,7 +165,7 @@ listen(Port, Options0) -> #config{cb={CbModule, _, _, _},inet_user=Options} = Config, case CbModule:listen(Port, Options) of {ok, ListenSocket} -> - {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}}; + {ok, #sslsocket{pid = {ListenSocket, Config}}}; Err = {error, _} -> Err end @@ -245,18 +245,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> %% %% Description: Close an ssl connection %%-------------------------------------------------------------------- +close(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:close(Pid); close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) -> - CbMod:close(ListenSocket); -close(#sslsocket{pid = Pid}) -> - ssl_connection:close(Pid). + CbMod:close(ListenSocket). %%-------------------------------------------------------------------- -spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- -send(#sslsocket{pid = Pid}, Data) -> - ssl_connection:send(Pid, Data). +send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) -> + ssl_connection:send(Pid, Data); +send(#sslsocket{pid = {ListenSocket, #config{cb={CbModule, _, _, _}}}}, Data) -> + CbModule:send(ListenSocket, Data). %% {error,enotconn} %%-------------------------------------------------------------------- -spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. @@ -266,8 +268,10 @@ send(#sslsocket{pid = Pid}, Data) -> %%-------------------------------------------------------------------- recv(Socket, Length) -> recv(Socket, Length, infinity). -recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) -> - ssl_connection:recv(Pid, Length, Timeout). +recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) -> + ssl_connection:recv(Pid, Length, Timeout); +recv(#sslsocket{pid = {Listen, #config{cb={CbModule, _, _, _}}}}, _,_) when is_port(Listen)-> + CbModule:recv(Listen, 0). %% {error,enotconn} %%-------------------------------------------------------------------- -spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. @@ -275,8 +279,12 @@ recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) -> %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- -controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) -> - ssl_connection:new_user(Pid, NewOwner). +controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) -> + ssl_connection:new_user(Pid, NewOwner); +controlling_process(#sslsocket{pid = {Listen, + #config{cb={CbModule, _, _, _}}}}, NewOwner) when is_port(Listen), + is_pid(NewOwner) -> + CbModule:controlling_process(Listen, NewOwner). %%-------------------------------------------------------------------- -spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | @@ -284,29 +292,35 @@ controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) -> %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- -connection_info(#sslsocket{pid = Pid}) -> - ssl_connection:info(Pid). +connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:info(Pid); +connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. %% %% Description: same as inet:peername/1. %%-------------------------------------------------------------------- -peername(#sslsocket{pid = Pid}) -> - ssl_connection:peername(Pid). +peername(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid)-> + inet:peername(Socket); +peername(#sslsocket{pid = {ListenSocket, _}}) -> + inet:peername(ListenSocket). %% Will return {error, enotconn} %%-------------------------------------------------------------------- -spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}. %% %% Description: Returns the peercert. %%-------------------------------------------------------------------- -peercert(#sslsocket{pid = Pid}) -> +peercert(#sslsocket{pid = Pid}) when is_pid(Pid) -> case ssl_connection:peer_certificate(Pid) of {ok, undefined} -> {error, no_peercert}; Result -> Result - end. + end; +peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec suite_definition(cipher_suite()) -> erl_cipher_suite(). @@ -323,7 +337,7 @@ suite_definition(S) -> %% Description: Returns the next protocol that has been negotiated. If no %% protocol has been negotiated will return {error, next_protocol_not_negotiated} %%-------------------------------------------------------------------- -negotiated_next_protocol(#sslsocket{fd = new_ssl, pid = Pid}) -> +negotiated_next_protocol(#sslsocket{pid = Pid}) -> ssl_connection:negotiated_next_protocol(Pid). -spec cipher_suites() -> [erl_cipher_suite()]. @@ -396,8 +410,9 @@ setopts(#sslsocket{}, Options) -> %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- -shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}, How) -> - CbMod:shutdown(ListenSocket, How); +shutdown(#sslsocket{pid = {Listen, #config{cb={CbMod,_, _, _}}}}, + How) when is_port(Listen) -> + CbMod:shutdown(Listen, How); shutdown(#sslsocket{pid = Pid}, How) -> ssl_connection:shutdown(Pid, How). @@ -406,11 +421,11 @@ shutdown(#sslsocket{pid = Pid}, How) -> %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- -sockname(#sslsocket{pid = {ListenSocket, _}}) -> - inet:sockname(ListenSocket); +sockname(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + inet:sockname(Listen); -sockname(#sslsocket{pid = Pid}) -> - ssl_connection:sockname(Pid). +sockname(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid) -> + inet:sockname(Socket). %%--------------------------------------------------------------- -spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. @@ -418,12 +433,14 @@ sockname(#sslsocket{pid = Pid}) -> %% Description: Returns list of session info currently [{session_id, session_id(), %% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- -session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:session_info(Pid). +session_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:session_info(Pid); +session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. %%--------------------------------------------------------------- -spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} | - {available, [tls_atom_version()]}]. + {available, [tls_atom_version()]}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -439,8 +456,10 @@ versions() -> %% %% Description: Initiates a renegotiation. %%-------------------------------------------------------------------- -renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:renegotiation(Pid). +renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:renegotiation(Pid); +renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec prf(#sslsocket{}, binary() | 'master_secret', binary(), @@ -449,10 +468,11 @@ renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> %% %% Description: use a ssl sessions TLS PRF to generate key material %%-------------------------------------------------------------------- -prf(#sslsocket{pid = Pid, fd = new_ssl}, - Secret, Label, Seed, WantedLength) -> - ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength). - +prf(#sslsocket{pid = Pid}, + Secret, Label, Seed, WantedLength) when is_pid(Pid) -> + ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength); +prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec clear_pem_cache() -> ok. @@ -849,10 +869,10 @@ internal_inet_values() -> socket_options(InetValues) -> #socket_options{ - mode = proplists:get_value(mode, InetValues), - header = proplists:get_value(header, InetValues), - active = proplists:get_value(active, InetValues), - packet = proplists:get_value(packet, InetValues), + mode = proplists:get_value(mode, InetValues, lists), + header = proplists:get_value(header, InetValues, 0), + active = proplists:get_value(active, InetValues, active), + packet = proplists:get_value(packet, InetValues, 0), packet_size = proplists:get_value(packet_size, InetValues) }. @@ -941,7 +961,5 @@ make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) -> %% 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. +pid(#sslsocket{})-> + whereis(ssl_connection_sup). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 23f22987df..87cf49d07d 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -40,8 +40,7 @@ -export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2, socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, - peer_certificate/1, sockname/1, peername/1, renegotiation/1, - negotiated_next_protocol/1, prf/5]). + peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]). %% Called by ssl_connection_sup -export([start_link/7]). @@ -120,7 +119,7 @@ send(Pid, Data) -> sync_send_all_state_event(Pid, {application_data, %% iolist_to_binary should really %% be called iodata_to_binary() - erlang:iolist_to_binary(Data)}, infinity). + erlang:iolist_to_binary(Data)}). %%-------------------------------------------------------------------- -spec recv(pid(), integer(), timeout()) -> @@ -129,7 +128,7 @@ send(Pid, Data) -> %% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> - sync_send_all_state_event(Pid, {recv, Length}, Timeout). + sync_send_all_state_event(Pid, {recv, Length, Timeout}). %%-------------------------------------------------------------------- -spec connect(host(), inet:port_number(), port(), {#ssl_options{}, #socket_options{}}, pid(), tuple(), timeout()) -> @@ -166,7 +165,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- handshake(#sslsocket{pid = Pid}, Timeout) -> - case sync_send_all_state_event(Pid, start, Timeout) of + case sync_send_all_state_event(Pid, {start, Timeout}) of connected -> ok; Error -> @@ -181,7 +180,7 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> socket_control(Socket, Pid, CbModule) -> case CbModule:controlling_process(Socket, Pid) of ok -> - {ok, sslsocket(Pid)}; + {ok, sslsocket(Pid, Socket)}; {error, Reason} -> {error, Reason} end. @@ -215,13 +214,7 @@ shutdown(ConnectionPid, How) -> %%-------------------------------------------------------------------- new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). -%%-------------------------------------------------------------------- --spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:sockname/1 -%%-------------------------------------------------------------------- -sockname(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, sockname). + %%-------------------------------------------------------------------- -spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}. %% @@ -229,13 +222,7 @@ sockname(ConnectionPid) -> %%-------------------------------------------------------------------- negotiated_next_protocol(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, negotiated_next_protocol). -%%-------------------------------------------------------------------- --spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:peername/1 -%%-------------------------------------------------------------------- -peername(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, peername). + %%-------------------------------------------------------------------- -spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% @@ -344,15 +331,15 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> #state{}) -> gen_fsm_state_return(). %%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, - ssl_options = SslOpts, - session = #session{own_certificate = Cert} = Session0, - session_cache = Cache, session_cache_cb = CacheCb, - transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} = State0) -> + ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, + transport_cb = Transport, socket = Socket, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) -> Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), - + Version = Hello#client_hello.client_version, Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = @@ -383,23 +370,22 @@ hello(#server_hello{cipher_suite = CipherSuite, renegotiation = {Renegotiation, _}, ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, hello, State0), - {stop, normal, State0}; - + #alert{} = Alert -> + handle_own_alert(Alert, ReqVersion, hello, State0), + {stop, {shutdown, own_alert}, State0}; {Version, NewId, ConnectionStates, NextProtocol} -> {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - - NewNextProtocol = case NextProtocol of - undefined -> - State0#state.next_protocol; - _ -> - NextProtocol - end, - + + NewNextProtocol = case NextProtocol of + undefined -> + State0#state.next_protocol; + _ -> + NextProtocol + end, + State = State0#state{key_algorithm = KeyAlgorithm, hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, @@ -407,13 +393,13 @@ hello(#server_hello{cipher_suite = CipherSuite, premaster_secret = PremasterSecret, expecting_next_protocol_negotiation = NextProtocol =/= undefined, next_protocol = NewNextProtocol}, - + case ssl_session:is_new(OldId, NewId) of true -> handle_new_session(NewId, CipherSuite, Compression, State#state{connection_states = ConnectionStates}); false -> - handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end end; @@ -432,8 +418,7 @@ hello(Hello = #client_hello{client_version = ClientVersion}, negotiated_version = Version, session = Session}); #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State), - {stop, normal, State} + handle_own_alert(Alert, ClientVersion, hello, State) end; hello(timeout, State) -> @@ -464,8 +449,7 @@ abbreviated(#finished{verify_data = Data} = Finished, next_state_connection(abbreviated, ack_connection(State#state{connection_states = ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State) end; abbreviated(#finished{verify_data = Data} = Finished, @@ -485,8 +469,7 @@ abbreviated(#finished{verify_data = Data} = Finished, connection_states = ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State) end; abbreviated(timeout, State) -> @@ -510,8 +493,7 @@ certify(#certificate{asn1_certificates = []}, fail_if_no_peer_cert = true}} = State) -> Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State}; + handle_own_alert(Alert, Version, certify, State); certify(#certificate{asn1_certificates = []}, #state{role = server, @@ -534,8 +516,7 @@ certify(#certificate{} = Cert, handle_peer_cert(PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State} + handle_own_alert(Alert, Version, certify, State) end; certify(#server_key_exchange{} = KeyExchangeMsg, @@ -547,8 +528,7 @@ certify(#server_key_exchange{} = KeyExchangeMsg, {Record, State} = next_record(State1), next_state(certify, certify, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify(#server_key_exchange{} = Msg, @@ -572,8 +552,7 @@ certify(#server_hello_done{}, State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; %% Master secret is calculated from premaster_secret @@ -591,8 +570,7 @@ certify(#server_hello_done{}, session = Session}, client_certify_and_key_exchange(State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify(#client_key_exchange{} = Msg, @@ -608,8 +586,7 @@ certify(#client_key_exchange{exchange_keys = Keys}, certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State} + handle_own_alert(Alert, Version, certify, State) end; @@ -634,8 +611,7 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, @@ -648,8 +624,7 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end. %%-------------------------------------------------------------------- @@ -678,8 +653,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, cipher, State0) end; % client must send a next protocol message if we are expecting it @@ -705,8 +679,7 @@ cipher(#finished{verify_data = Data} = Finished, Session = register_session(Role, Host, Port, Session0), cipher_role(Role, Data, Session, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State), - {stop, normal, State} + handle_own_alert(Alert, Version, cipher, State) end; % only allowed to send next_protocol message after change cipher spec @@ -801,7 +774,8 @@ handle_sync_event({application_data, Data}, From, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}, get_timeout(State)}; -handle_sync_event(start, StartFrom, hello, State) -> +handle_sync_event({start, Timeout}, StartFrom, hello, State) -> + start_or_recv_cancel_timer(Timeout, StartFrom), hello(start, State#state{start_or_recv_from = StartFrom}); %% The two clauses below could happen if a server upgrades a socket in @@ -811,12 +785,14 @@ handle_sync_event(start, StartFrom, hello, State) -> %% mode before telling the client that it is willing to upgrade %% and before calling ssl:ssl_accept/2. These clauses are %% here to make sure it is the users problem and not owers if -%% they upgrade a active socket. -handle_sync_event(start, _, connection, State) -> +%% they upgrade an active socket. +handle_sync_event({start,_}, _, connection, State) -> {reply, connected, connection, State, get_timeout(State)}; -handle_sync_event(start, _From, error, {Error, State = #state{}}) -> +handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) -> {stop, {shutdown, Error}, {error, Error}, State}; -handle_sync_event(start, StartFrom, StateName, State) -> + +handle_sync_event({start, Timeout}, StartFrom, StateName, State) -> + start_or_recv_cancel_timer(Timeout, StartFrom), {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)}; handle_sync_event(close, _, StateName, State) -> @@ -848,12 +824,14 @@ handle_sync_event({shutdown, How0}, _, StateName, {stop, normal, Error, State} end; -handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) -> +handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName, State0) -> + start_or_recv_cancel_timer(Timeout, RecvFrom), passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName); %% Doing renegotiate wait with handling request until renegotiate is %% finished. Will be handled by next_state_is_connection/2. -handle_sync_event({recv, N}, RecvFrom, StateName, State) -> +handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) -> + start_or_recv_cancel_timer(Timeout, RecvFrom), {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, get_timeout(State)}; @@ -870,19 +848,10 @@ handle_sync_event({get_opts, OptTags}, _From, StateName, OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []), {reply, OptsReply, StateName, State, get_timeout(State)}; -handle_sync_event(sockname, _From, StateName, - #state{socket = Socket} = State) -> - SockNameReply = inet:sockname(Socket), - {reply, SockNameReply, StateName, State, get_timeout(State)}; - handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) -> {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)}; handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) -> {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)}; -handle_sync_event(peername, _From, StateName, - #state{socket = Socket} = State) -> - PeerNameReply = inet:peername(Socket), - {reply, PeerNameReply, StateName, State, get_timeout(State)}; handle_sync_event({set_opts, Opts0}, _From, StateName, #state{socket_options = Opts1, @@ -985,7 +954,7 @@ handle_info({Protocol, _, Data}, StateName, next_state(StateName, StateName, Record, State); #alert{} = Alert -> handle_normal_shutdown(Alert, StateName, State0), - {stop, normal, State0} + {stop, {shutdown, own_alert}, State0} end; handle_info({CloseTag, Socket}, StateName, @@ -1006,12 +975,12 @@ handle_info({CloseTag, Socket}, StateName, ok end, handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - {stop, normal, State}; + {stop, {shutdown, transport_closed}, State}; handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag} = State) when StateName =/= connection -> - alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + alert_user(Socket, StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, @@ -1027,7 +996,14 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _, handle_info(allow_renegotiate, StateName, State) -> {next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)}; - + +handle_info({cancel_start_or_recv, RecvFrom}, connection = StateName, #state{start_or_recv_from = RecvFrom} = State) -> + gen_fsm:reply(RecvFrom, {error, timeout}), + {next_state, StateName, State#state{start_or_recv_from = undefined}, get_timeout(State)}; + +handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) -> + {next_state, StateName, State, get_timeout(State)}; + handle_info(Msg, StateName, State) -> Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]), error_logger:info_report(Report), @@ -1044,6 +1020,20 @@ terminate(_, _, #state{terminated = true}) -> %% we want to guarantee that Transport:close has been called %% when ssl:close/1 returns. ok; + +terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue, + renegotiation = Renegotiate} = State) -> + handle_unrecv_data(StateName, State), + handle_trusted_certs_db(State), + notify_senders(SendQueue), + notify_renegotiater(Renegotiate); + +terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), + notify_senders(SendQueue), + notify_renegotiater(Renegotiate); + terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, @@ -1054,16 +1044,14 @@ terminate(Reason, connection, #state{negotiated_version = Version, notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), - workaround_transport_delivery_problems(Socket, Transport, Reason), - Transport:close(Socket); + workaround_transport_delivery_problems(Socket, Transport); -terminate(Reason, _StateName, #state{transport_cb = Transport, +terminate(_Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), - workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket). %%-------------------------------------------------------------------- @@ -1238,18 +1226,13 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) -> end. sync_send_all_state_event(FsmPid, Event) -> - sync_send_all_state_event(FsmPid, Event, infinity). - -sync_send_all_state_event(FsmPid, Event, Timeout) -> - try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout) + try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity) catch exit:{noproc, _} -> {error, closed}; - exit:{timeout, _} -> - {error, timeout}; exit:{normal, _} -> {error, closed}; - exit:{shutdown, _} -> + exit:{{shutdown, _},_} -> {error, closed} end. @@ -1346,8 +1329,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, next_state(hello, certify, Record, State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. resumed_server_hello(#state{session = Session, @@ -1367,8 +1349,7 @@ resumed_server_hello(#state{session = Session, {Record, State} = next_record(State2), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> @@ -1393,8 +1374,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, session = Session}), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. @@ -1411,8 +1391,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = next_state(certify, cipher, Record, State) catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end. do_client_certify_and_key_exchange(State0) -> @@ -1787,10 +1766,11 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> end. read_application_data(Data, #state{user_application = {_Mon, Pid}, - socket_options = SOpts, - bytes_to_read = BytesToRead, - start_or_recv_from = RecvFrom, - user_data_buffer = Buffer0} = State0) -> + socket = Socket, + socket_options = SOpts, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = Buffer0} = State0) -> Buffer1 = if Buffer0 =:= <<>> -> Data; Data =:= <<>> -> Buffer0; @@ -1798,7 +1778,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), State = State0#state{user_data_buffer = Buffer, start_or_recv_from = undefined, bytes_to_read = 0, @@ -1815,7 +1795,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, {more, Buffer} -> % no reply, we need more data next_record(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. @@ -1894,9 +1874,9 @@ decode_packet(Type, Buffer, PacketOpts) -> %% Note that if the user has explicitly configured the socket to expect %% HTTP headers using the {packet, httph} option, we don't do any automatic %% switching of states. -deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, - Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_reply(SOpts, Data)), +deliver_app_data(Socket, SOpts = #socket_options{active=Active, packet=Type}, + Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_reply(Socket, SOpts, Data)), SO = case Data of {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), ((Type =:= http) or (Type =:= http_bin)) -> @@ -1915,31 +1895,31 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, SO end. -format_reply(#socket_options{active = false, mode = Mode, packet = Packet, +format_reply(_,#socket_options{active = false, mode = Mode, packet = Packet, header = Header}, Data) -> - {ok, format_reply(Mode, Packet, Header, Data)}; -format_reply(#socket_options{active = _, mode = Mode, packet = Packet, + {ok, do_format_reply(Mode, Packet, Header, Data)}; +format_reply(Socket, #socket_options{active = _, mode = Mode, packet = Packet, header = Header}, Data) -> - {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}. + {ssl, sslsocket(self(), Socket), do_format_reply(Mode, Packet, Header, Data)}. -deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_packet_error(SO, Data)). +deliver_packet_error(Socket, SO= #socket_options{active = Active}, Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_packet_error(Socket, SO, Data)). -format_packet_error(#socket_options{active = false, mode = Mode}, Data) -> - {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}}; -format_packet_error(#socket_options{active = _, mode = Mode}, Data) -> - {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}. +format_packet_error(_,#socket_options{active = false, mode = Mode}, Data) -> + {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; +format_packet_error(Socket, #socket_options{active = _, mode = Mode}, Data) -> + {ssl_error, sslsocket(self(), Socket), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. -format_reply(binary, _, N, Data) when N > 0 -> % Header mode +do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode header(N, Data); -format_reply(binary, _, _, Data) -> +do_format_reply(binary, _, _, Data) -> Data; -format_reply(list, Packet, _, Data) +do_format_reply(list, Packet, _, Data) when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph; Packet == httph_bin -> Data; -format_reply(list, _,_, Data) -> +do_format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> @@ -1983,8 +1963,7 @@ handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} end. next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) -> - handle_own_alert(Alert, Version, Current, State), - {stop, normal, State}; + handle_own_alert(Alert, Version, Current, State); next_state(_,Next, no_record, State) -> {next_state, Next, State, get_timeout(State)}; @@ -2022,8 +2001,7 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, handle_tls_handshake(Handle, Next, State) catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, Current, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, Current, State0) end; next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> @@ -2171,11 +2149,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, send_queue = queue:new() }. -sslsocket(Pid) -> - #sslsocket{pid = Pid, fd = new_ssl}. - -sslsocket() -> - sslsocket(self()). +sslsocket(Pid, Socket) -> + #sslsocket{pid = Pid, fd = Socket}. get_socket_opts(_,[], _, Acc) -> {ok, Acc}; @@ -2271,24 +2246,24 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{start_or_recv_from = From, host = Host, port = Port, session = Session, - user_application = {_Mon, Pid}, + #state{socket = Socket, start_or_recv_from = From, host = Host, + port = Port, session = Session, user_application = {_Mon, Pid}, log_alert = Log, role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), log_alert(Log, StateName, Alert), - alert_user(StateName, Opts, Pid, From, Alert, Role), + alert_user(Socket, StateName, Opts, Pid, From, Alert, Role), {stop, normal, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, StateName, State) -> handle_normal_shutdown(Alert, StateName, State), - {stop, normal, State}; + {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, #state{log_alert = Log, renegotiation = {true, internal}} = State) -> log_alert(Log, StateName, Alert), handle_normal_shutdown(Alert, StateName, State), - {stop, normal, State}; + {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, #state{log_alert = Log, renegotiation = {true, From}} = State0) -> @@ -2303,28 +2278,28 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta {Record, State} = next_record(State0), next_state(StateName, StateName, Record, State). -alert_user(connection, Opts, Pid, From, Alert, Role) -> - alert_user(Opts#socket_options.active, Pid, From, Alert, Role); -alert_user(_, _, _, From, Alert, Role) -> - alert_user(From, Alert, Role). +alert_user(Socket, connection, Opts, Pid, From, Alert, Role) -> + alert_user(Socket, Opts#socket_options.active, Pid, From, Alert, Role); +alert_user(Socket,_, _, _, From, Alert, Role) -> + alert_user(Socket, From, Alert, Role). -alert_user(From, Alert, Role) -> - alert_user(false, no_pid, From, Alert, Role). +alert_user(Socket, From, Alert, Role) -> + alert_user(Socket, false, no_pid, From, Alert, Role). -alert_user(false = Active, Pid, From, Alert, Role) -> +alert_user(_Socket, false = Active, Pid, From, Alert, Role) -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. ReasonCode = ssl_alert:reason_code(Alert, Role), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Active, Pid, From, Alert, Role) -> +alert_user(Socket, Active, Pid, From, Alert, Role) -> case ssl_alert:reason_code(Alert, Role) of closed -> send_or_reply(Active, Pid, From, - {ssl_closed, sslsocket()}); + {ssl_closed, sslsocket(self(), Socket)}); ReasonCode -> send_or_reply(Active, Pid, From, - {ssl_error, sslsocket(), ReasonCode}) + {ssl_error, sslsocket(self(), Socket), ReasonCode}) end. log_alert(true, Info, Alert) -> @@ -2341,8 +2316,8 @@ handle_own_alert(Alert, Version, StateName, try %% Try to tell the other side {BinMsg, _} = encode_alert(Alert, Version, ConnectionStates), - linux_workaround_transport_delivery_problems(Alert, Socket), - Transport:send(Socket, BinMsg) + Transport:send(Socket, BinMsg), + workaround_transport_delivery_problems(Socket, Transport) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, @@ -2351,20 +2326,23 @@ handle_own_alert(Alert, Version, StateName, handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok - end. + end, + {stop, {shutdown, own_alert}, State}. -handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) -> - alert_user(StartFrom, Alert, Role); +handle_normal_shutdown(Alert, _, #state{socket = Socket, + start_or_recv_from = StartFrom, + role = Role, renegotiation = {false, first}}) -> + alert_user(Socket, StartFrom, Alert, Role); -handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts, +handle_normal_shutdown(Alert, StateName, #state{socket = Socket, + socket_options = Opts, user_application = {_Mon, Pid}, start_or_recv_from = RecvFrom, role = Role}) -> - alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role). + alert_user(Socket, StateName, Opts, Pid, RecvFrom, Alert, Role). handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), - handle_own_alert(Alert, Version, {Info, Msg}, State), - {stop, normal, State}. + handle_own_alert(Alert, Version, {Info, Msg}, State). make_premaster_secret({MajVer, MinVer}, rsa) -> Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), @@ -2422,36 +2400,35 @@ notify_renegotiater({true, From}) when not is_atom(From) -> notify_renegotiater(_) -> ok. -terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown; +terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == user_close -> {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Version, ConnectionStates), BinAlert; +terminate_alert({shutdown, _}, Version, ConnectionStates) -> + {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + Version, ConnectionStates), + BinAlert; + terminate_alert(_, Version, ConnectionStates) -> {BinAlert, _} = encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR), Version, ConnectionStates), BinAlert. -workaround_transport_delivery_problems(_,_, user_close) -> - ok; -workaround_transport_delivery_problems(Socket, Transport, _) -> +workaround_transport_delivery_problems(Socket, gen_tcp = Transport) -> %% Standard trick to try to make sure all - %% data sent to to tcp port is really sent - %% before tcp port is closed so that the peer will - %% get a correct error message. + %% data sent to the tcp port is really delivered to the + %% peer application before tcp port is closed so that the peer will + %% get the correct TLS alert message and not only a transport close. inet:setopts(Socket, [{active, false}]), Transport:shutdown(Socket, write), - Transport:recv(Socket, 0). - -linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) -> - case os:type() of - {unix, linux} -> - inet:setopts(Socket, [{nodelay, true}]); - _ -> - ok - end; -linux_workaround_transport_delivery_problems(_, _) -> - ok. + %% Will return when other side has closed or after 30 s + %% e.g. we do not want to hang if something goes wrong + %% with the network but we want to maximise the odds that + %% peer application gets all data sent on the tcp connection. + Transport:recv(Socket, 0, 30000); +workaround_transport_delivery_problems(Socket, Transport) -> + Transport:close(Socket). get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) -> infinity; @@ -2524,3 +2501,26 @@ default_hashsign(_Version, KeyExchange) default_hashsign(_Version, KeyExchange) when KeyExchange == dh_anon -> {null, anon}. + +start_or_recv_cancel_timer(infinity, _RecvFrom) -> + ok; +start_or_recv_cancel_timer(Timeout, RecvFrom) -> + erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}). + +handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) -> + inet:setopts(Socket, [{active, false}]), + case Transport:recv(Socket, 0, 0) of + {error, closed} -> + ok; + {ok, Data} -> + handle_close_alert(Data, StateName, State) + end. + +handle_close_alert(Data, StateName, State0) -> + case next_tls_record(Data, State0) of + {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} -> + [Alert|_] = decode_alerts(EncAlerts), + handle_normal_shutdown(Alert, StateName, State); + _ -> + ok + end. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index fa1784714f..db21dac942 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1401,6 +1401,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_manager.erl b/lib/ssl/src/ssl_manager.erl index 0cf4f2ce33..13689ce7d8 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -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, 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. |
