diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/src/dtls.erl | 46 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 43 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 6 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.hrl | 4 | ||||
-rw-r--r-- | lib/ssl/src/ssl_manager.erl | 8 | ||||
-rw-r--r-- | lib/ssl/src/ssl_pkix_db.erl | 4 | ||||
-rw-r--r-- | lib/ssl/src/tls.erl | 45 | ||||
-rw-r--r-- | lib/ssl/src/tls_record.erl | 4 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 48 |
9 files changed, 137 insertions, 71 deletions
diff --git a/lib/ssl/src/dtls.erl b/lib/ssl/src/dtls.erl index 1cad9560b5..780bddeb10 100644 --- a/lib/ssl/src/dtls.erl +++ b/lib/ssl/src/dtls.erl @@ -31,25 +31,29 @@ handshake/1, handshake/2, handshake/3]). %%-------------------------------------------------------------------- +%% +%% Description: Connect to a DTLS server. +%%-------------------------------------------------------------------- + -spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | {error, reason()}. + +connect(Socket, Options) when is_port(Socket) -> + connect(Socket, Options, infinity). + -spec connect(host() | port(), [connect_option()] | inet:port_number(), timeout() | list()) -> {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), inet:port_number(), list(), timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. - -%% -%% Description: Connect to an DTLS server. -%%-------------------------------------------------------------------- -connect(Socket, Options) when is_port(Socket) -> - connect(Socket, Options, infinity). connect(Socket, SslOptions, Timeout) when is_port(Socket) -> DTLSOpts = [{protocol, dtls} | SslOptions], ssl:connect(Socket, DTLSOpts, Timeout); connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). + +-spec connect(host() | port(), inet:port_number(), list(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. + connect(Host, Port, Options, Timeout) -> DTLSOpts = [{protocol, dtls} | Options], ssl:connect(Host, Port, DTLSOpts, Timeout). @@ -65,38 +69,44 @@ listen(Port, Options) -> ssl:listen(Port, DTLSOpts). %%-------------------------------------------------------------------- --spec accept(#sslsocket{}) -> {ok, #sslsocket{}} | - {error, reason()}. --spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {error, reason()}. %% %% Description: Performs transport accept on an ssl listen socket %%-------------------------------------------------------------------- +-spec accept(#sslsocket{}) -> {ok, #sslsocket{}} | + {error, reason()}. accept(ListenSocket) -> accept(ListenSocket, infinity). + +-spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | + {error, reason()}. accept(Socket, Timeout) -> ssl:transport_accept(Socket, Timeout). %%-------------------------------------------------------------------- --spec handshake(#sslsocket{}) -> ok | {error, reason()}. --spec handshake(#sslsocket{} | port(), timeout()| [ssl_option() - | transport_option()]) -> - ok | {ok, #sslsocket{}} | {error, reason()}. --spec handshake(port(), [ssl_option()| transport_option()], timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- +-spec handshake(#sslsocket{}) -> ok | {error, reason()}. + handshake(ListenSocket) -> handshake(ListenSocket, infinity). + +-spec handshake(#sslsocket{} | port(), timeout()| [ssl_option() + | transport_option()]) -> + ok | {ok, #sslsocket{}} | {error, reason()}. + handshake(#sslsocket{} = Socket, Timeout) -> ssl:ssl_accept(Socket, Timeout); handshake(ListenSocket, SslOptions) when is_port(ListenSocket) -> handshake(ListenSocket, SslOptions, infinity). + +-spec handshake(port(), [ssl_option()| transport_option()], timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. + handshake(Socket, SslOptions, Timeout) when is_port(Socket) -> ssl:ssl_accept(Socket, SslOptions, Timeout). diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7edc6554ca..c3bdeb1a54 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -626,7 +626,7 @@ handle_options(Opts0, _Role) -> user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined), psk_identity = handle_option(psk_identity, Opts, undefined), srp_identity = handle_option(srp_identity, Opts, undefined), - ciphers = handle_option(ciphers, Opts, []), + ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), hd(Versions)), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), reuse_sessions = handle_option(reuse_sessions, Opts, true), @@ -769,15 +769,6 @@ validate_option(srp_identity, {Username, Password}) {unicode:characters_to_binary(Username), unicode:characters_to_binary(Password)}; -validate_option(ciphers, Value) when is_list(Value) -> - Version = tls_record:highest_protocol_version([]), - try cipher_suites(Version, Value) - catch - exit:_ -> - throw({error, {options, {ciphers, Value}}}); - error:_-> - throw({error, {options, {ciphers, Value}}}) - end; validate_option(reuse_session, Value) when is_function(Value) -> Value; validate_option(reuse_sessions, Value) when is_boolean(Value) -> @@ -937,16 +928,26 @@ emulated_options([Opt|Opts], Inet, Emulated) -> emulated_options([], Inet,Emulated) -> {Inet, Emulated}. -cipher_suites(Version, []) -> +handle_cipher_option(Value, Version) when is_list(Value) -> + try binary_cipher_suites(Version, Value) of + Suites -> + Suites + catch + exit:_ -> + throw({error, {options, {ciphers, Value}}}); + error:_-> + throw({error, {options, {ciphers, Value}}}) + end. +binary_cipher_suites(Version, []) -> %% Defaults to all supported suits ssl_cipher:suites(Version); -cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility +binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0], - cipher_suites(Version, Ciphers); -cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> + binary_cipher_suites(Version, Ciphers); +binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], - cipher_suites(Version, Ciphers); + binary_cipher_suites(Version, Ciphers); -cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> +binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> Supported0 = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites() ++ ssl_cipher:psk_suites(Version) @@ -954,18 +955,18 @@ cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> Supported = ssl_cipher:filter_suites(Supported0), case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of [] -> - Supported; + Supported; %% Defaults to all supported suits Ciphers -> Ciphers end; -cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) -> +binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) -> %% Format: ["RC4-SHA","RC4-MD5"] Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0], - cipher_suites(Version, Ciphers); -cipher_suites(Version, Ciphers0) -> + binary_cipher_suites(Version, Ciphers); +binary_cipher_suites(Version, Ciphers0) -> %% Format: "RC4-SHA:RC4-MD5" Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")], - cipher_suites(Version, Ciphers). + binary_cipher_suites(Version, Ciphers). unexpected_format(Error) -> lists:flatten(io_lib:format("Unexpected error: ~p", [Error])). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 82106935cb..e283e6079e 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 @@ -1757,12 +1757,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); diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index adb2e1debe..341a4217e4 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -2,7 +2,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 @@ -73,7 +73,7 @@ renegotiation :: undefined | {boolean(), From::term() | internal | peer}, start_or_recv_from :: term(), timer :: undefined | reference(), % start_or_recive_timer - send_queue :: queue(), + send_queue :: queue:queue(), terminated = false ::boolean(), allow_renegotiate = true ::boolean(), expecting_next_protocol_negotiation = false ::boolean(), diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 4d5eaeb607..fbc73e0e42 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -167,27 +167,27 @@ clean_cert_db(Ref, File) -> ok. %%-------------------------------------------------------------------- --spec register_session(inet:port_number(), #session{}) -> ok. --spec register_session(host(), inet:port_number(), #session{}) -> ok. %% %% Description: Make the session available for reuse. %%-------------------------------------------------------------------- +-spec register_session(host(), inet:port_number(), #session{}) -> ok. register_session(Host, Port, Session) -> cast({register_session, Host, Port, Session}). +-spec register_session(inet:port_number(), #session{}) -> ok. register_session(Port, Session) -> cast({register_session, Port, Session}). %%-------------------------------------------------------------------- --spec invalidate_session(inet:port_number(), #session{}) -> ok. --spec invalidate_session(host(), inet:port_number(), #session{}) -> ok. %% %% Description: Make the session unavailable for reuse. After %% a the session has been marked "is_resumable = false" for some while %% it will be safe to remove the data from the session database. %%-------------------------------------------------------------------- +-spec invalidate_session(host(), inet:port_number(), #session{}) -> ok. invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). +-spec invalidate_session(inet:port_number(), #session{}) -> ok. invalidate_session(Port, Session) -> cast({invalidate_session, Port, Session}). diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl index 9de50c8f26..e59aba0618 100644 --- a/lib/ssl/src/ssl_pkix_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -115,17 +115,17 @@ 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()]) -> {ok, term()}. --spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}. %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- +-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}. cache_pem_file({MD5, File}, [_CertsDb, _RefDb, PemChache]) -> {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), insert(MD5, Content, PemChache), {ok, Content}. +-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}. cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) -> {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), diff --git a/lib/ssl/src/tls.erl b/lib/ssl/src/tls.erl index 3e7b2db9c2..c829129250 100644 --- a/lib/ssl/src/tls.erl +++ b/lib/ssl/src/tls.erl @@ -30,25 +30,29 @@ handshake/1, handshake/2, handshake/3]). %%-------------------------------------------------------------------- --spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | - {error, reason()}. --spec connect(host() | port(), [connect_option()] | inet:port_number(), - timeout() | list()) -> - {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), inet:port_number(), list(), timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. - %% %% Description: Connect to an TLS server. %%-------------------------------------------------------------------- +-spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | + {error, reason()}. + connect(Socket, Options) when is_port(Socket) -> connect(Socket, Options, infinity). + +-spec connect(host() | port(), [connect_option()] | inet:port_number(), + timeout() | list()) -> + {ok, #sslsocket{}} | {error, reason()}. + connect(Socket, SslOptions, Timeout) when is_port(Socket) -> TLSOpts = [{protocol, tls} | SslOptions], ssl:connect(Socket, TLSOpts, Timeout); connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). + +-spec connect(host() | port(), inet:port_number(), list(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. + connect(Host, Port, Options, Timeout) -> TLSOpts = [{protocol, tls} | Options], ssl:connect(Host, Port, TLSOpts, Timeout). @@ -64,39 +68,44 @@ listen(Port, Options) -> ssl:listen(Port, TLSOpts). %%-------------------------------------------------------------------- --spec accept(#sslsocket{}) -> {ok, #sslsocket{}} | - {error, reason()}. --spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {error, reason()}. %% %% Description: Performs transport accept on an ssl listen socket %%-------------------------------------------------------------------- +-spec accept(#sslsocket{}) -> {ok, #sslsocket{}} | + {error, reason()}. accept(ListenSocket) -> accept(ListenSocket, infinity). + +-spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | + {error, reason()}. accept(Socket, Timeout) -> ssl:transport_accept(Socket, Timeout). %%-------------------------------------------------------------------- --spec handshake(#sslsocket{}) -> ok | {error, reason()}. --spec handshake(#sslsocket{} | port(), timeout()| [ssl_option() - | transport_option()]) -> - ok | {ok, #sslsocket{}} | {error, reason()}. --spec handshake(port(), [ssl_option()| transport_option()], timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- +-spec handshake(#sslsocket{}) -> ok | {error, reason()}. + handshake(ListenSocket) -> handshake(ListenSocket, infinity). +-spec handshake(#sslsocket{} | port(), timeout()| [ssl_option() + | transport_option()]) -> + ok | {ok, #sslsocket{}} | {error, reason()}. + handshake(#sslsocket{} = Socket, Timeout) -> ssl:ssl_accept(Socket, Timeout); handshake(ListenSocket, SslOptions) when is_port(ListenSocket) -> handshake(ListenSocket, SslOptions, infinity). + +-spec handshake(port(), [ssl_option()| transport_option()], timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. + handshake(Socket, SslOptions, Timeout) when is_port(Socket) -> ssl:ssl_accept(Socket, SslOptions, Timeout). diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 88107557a0..8c0c4f3c91 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -262,18 +262,18 @@ supported_protocol_versions([_|_] = Vsns) -> 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. %% %%-------------------------------------------------------------------- +-spec is_acceptable_version(tls_version()) -> boolean(). is_acceptable_version({N,_}) when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> true; is_acceptable_version(_) -> false. +-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean(). is_acceptable_version({N,_} = Version, Versions) when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> lists:member(Version, Versions); diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index ddc511c652..523760aba6 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -84,8 +84,10 @@ all_versions_groups ()-> basic_tests() -> [app, + appup, alerts, send_close, + version_option, connect_twice, connect_dist, clear_pem_cache @@ -291,6 +293,11 @@ app() -> app(Config) when is_list(Config) -> ok = ?t:app_test(ssl). %%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the ssl appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(ssl). +%%-------------------------------------------------------------------- alerts() -> [{doc, "Test ssl_alert:alert_txt/1"}]. alerts(Config) when is_list(Config) -> @@ -1072,6 +1079,13 @@ send_close(Config) when is_list(Config) -> {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- +version_option() -> + [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}]. +version_option(Config) when is_list(Config) -> + Versions = proplists:get_value(supported, ssl:versions()), + [version_option_test(Config, Version) || Version <- Versions]. + +%%-------------------------------------------------------------------- close_transport_accept() -> [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}]. @@ -2200,7 +2214,14 @@ der_input(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + ssl_test_lib:close(Client), + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + [CADb | _] = element(5, State), + [] = ets:tab2list(CADb). + %%-------------------------------------------------------------------- der_input_opts(Opts) -> Certfile = proplists:get_value(certfile, Opts), @@ -3488,3 +3509,28 @@ shutdown_both_result(Socket, client) -> peername_result(S) -> ssl:peername(S). + +version_option_test(Config, Version) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {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, []}}, + {options, [{active, false}, {versions, [Version]}| 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, []}}, + {options, [{active, false}, {versions, [Version]}| ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). |