diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/src/ssl.erl | 9 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 17 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 21 | ||||
-rw-r--r-- | lib/ssl/src/ssl_record.erl | 12 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 63 | ||||
-rw-r--r-- | lib/ssl/test/ssl_npn_handshake_SUITE.erl | 54 |
6 files changed, 139 insertions, 37 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 0ba59cede2..fc06b5f1b0 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -612,8 +612,15 @@ handle_options(Opts0, _Role) -> CertFile = handle_option(certfile, Opts, <<>>), + Versions = case handle_option(versions, Opts, []) of + [] -> + ssl_record:supported_protocol_versions(); + Vsns -> + [ssl_record:protocol_version(Vsn) || Vsn <- Vsns] + end, + SSLOptions = #ssl_options{ - versions = handle_option(versions, Opts, []), + versions = Versions, verify = validate_option(verify, Verify), verify_fun = VerifyFun, fail_if_no_peer_cert = FailIfNoPeerCert, diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 8f4fd88d42..4d29ecce7a 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -73,7 +73,6 @@ session_cache, % session_cache_cb, % negotiated_version, % tls_version() - supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite hashsign_algorithm, % atom as defined by cipher_suite @@ -472,6 +471,13 @@ abbreviated(#finished{verify_data = Data} = Finished, handle_own_alert(Alert, Version, abbreviated, State) end; +%% only allowed to send next_protocol message after change cipher spec +%% & before finished message and it is not allowed during renegotiation +abbreviated(#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(abbreviated, abbreviated, Record, State); + abbreviated(timeout, State) -> { next_state, abbreviated, State, hibernate }; @@ -656,11 +662,10 @@ 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 +%% 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}; + handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0); cipher(#finished{verify_data = Data} = Finished, #state{negotiated_version = Version, @@ -682,8 +687,8 @@ 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 +%% 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}), diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 1929370991..889d310ca8 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.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 @@ -61,11 +61,7 @@ client_hello(Host, Port, ConnectionStates, ciphers = UserSuites } = SslOpts, Cache, CacheCb, Renegotiation, OwnCert) -> - - Fun = fun(Version) -> - ssl_record:protocol_version(Version) - end, - Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)), + Version = ssl_record:highest_protocol_version(Versions), Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), @@ -139,10 +135,11 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, session_id = SessionId, renegotiation_info = Info, hash_signs = _HashSigns} = Hello, - #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector}, + #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector, + versions = SupportedVersions}, ConnectionStates0, Renegotiation) -> %%TODO: select hash and signature algorigthm - case ssl_record:is_acceptable_version(Version) of + case ssl_record:is_acceptable_version(Version, SupportedVersions) of true -> case handle_renegotiation_info(client, Info, ConnectionStates0, Renegotiation, SecureRenegotation, []) of @@ -171,7 +168,7 @@ hello(#client_hello{client_version = ClientVersion, random = Random, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> %% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), - case ssl_record:is_acceptable_version(Version) of + case ssl_record:is_acceptable_version(Version, Versions) of true -> {Type, #session{cipher_suite = CipherSuite, compression_method = Compression} = Session} @@ -869,11 +866,7 @@ hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random, }. select_version(ClientVersion, Versions) -> - Fun = fun(Version) -> - ssl_record:protocol_version(Version) - end, - ServerVersion = ssl_record:highest_protocol_version(lists:map(Fun, - Versions)), + ServerVersion = ssl_record:highest_protocol_version(Versions), ssl_record:lowest_protocol_version(ClientVersion, ServerVersion). select_cipher_suite([], _) -> diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 173b9611c6..26aca56739 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.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 @@ -56,7 +56,7 @@ %% Misc. -export([protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, supported_protocol_versions/0, - is_acceptable_version/1]). + is_acceptable_version/1, is_acceptable_version/2]). -export([compressions/0]). @@ -475,8 +475,10 @@ supported_protocol_versions([_|_] = Vsns) -> %%-------------------------------------------------------------------- -spec is_acceptable_version(tls_version()) -> boolean(). +-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. +%% %%-------------------------------------------------------------------- is_acceptable_version({N,_}) when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> @@ -484,6 +486,12 @@ is_acceptable_version({N,_}) is_acceptable_version(_) -> false. +is_acceptable_version({N,_} = Version, Versions) + when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> + lists:member(Version, Versions); +is_acceptable_version(_,_) -> + false. + %%-------------------------------------------------------------------- -spec compressions() -> [binary()]. %% diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index b5c6a1da49..6b8f226a77 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -126,7 +126,8 @@ api_tests() -> hibernate, listen_socket, ssl_accept_timeout, - ssl_recv_timeout + ssl_recv_timeout, + versions_option ]. session_tests() -> @@ -1194,12 +1195,12 @@ tcp_connect(Config) when is_list(Config) -> {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), @@ -1222,12 +1223,12 @@ tcp_connect_big(Config) when is_list(Config) -> {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), @@ -2659,6 +2660,42 @@ session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- + +versions_option() -> + [{doc,"Test API versions option to connect/listen."}]. +versions_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + Supported = proplists:get_value(supported, ssl:versions()), + Available = proplists:get_value(available, ssl:versions()), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{versions, Supported} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + Server ! listen, + + ErrClient = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{versions , Available -- Supported} | ClientOpts]}]), + receive + {Server, _} -> + ok + end, + + ssl_test_lib:check_result(ErrClient, {error, {tls_alert, "protocol version"}}). +%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- send_recv_result(Socket) -> diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 862690cd7b..8c1b22cf5e 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -24,6 +24,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-define(SLEEP, 500). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -55,7 +56,8 @@ next_protocol_tests() -> fallback_npn_handshake_server_preference, client_negotiate_server_does_not_support, no_client_negotiate_but_server_supports_npn, - renegotiate_from_client_after_npn_handshake + renegotiate_from_client_after_npn_handshake, + npn_handshake_session_reused ]. next_protocol_not_supported() -> @@ -231,6 +233,56 @@ npn_not_supported_server(Config) when is_list(Config)-> {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts). +%-------------------------------------------------------------------------------- +npn_handshake_session_reused(Config) when is_list(Config)-> + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = [{client_preferred_next_protocols, + {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, + ServerOpts0 = ?config(server_opts, Config), + ServerOpts =[{next_protocols_advertised, + [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result_msg, []}}, + {options, ClientOpts}]), + + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + + %% Make sure session is registered + ct:sleep(?SLEEP), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client1, SessionInfo} -> + ok; + {Client1, Other} -> + ct:fail(Other) + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ssl_test_lib:close(Client1). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- |