diff options
-rw-r--r-- | lib/ssl/doc/src/ssl.xml | 69 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 12 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 31 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 1 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 34 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 25 |
6 files changed, 130 insertions, 42 deletions
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 4bc1a9a644..ffee4bd1af 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -768,39 +768,45 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>ssl_accept(ListenSocket) -> </name> - <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name> - <fsummary>Perform server-side SSL handshake</fsummary> + <name>ssl_accept(Socket) -> </name> + <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name> + <fsummary>Perform server-side SSL/TLS handshake</fsummary> <type> - <v>ListenSocket = sslsocket()</v> + <v>Socket = sslsocket()</v> <v>Timeout = integer()</v> <v>Reason = term()</v> </type> <desc> - <p>The <c>ssl_accept</c> function establish the SSL connection - on the server side. It should be called directly after - <c>transport_accept</c>, in the spawned server-loop.</p> + <p> Performs the SSL/TLS server-side handshake <c>Socket</c> is a socket as returned + by <seealso + marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso> + </p> </desc> </func> <func> - <name>ssl_accept(ListenSocket, SslOptions) -> </name> - <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name> - <fsummary>Perform server-side SSL handshake</fsummary> + <name>ssl_accept(Socket, SslOptions) -> </name> + <name>ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name> + <fsummary>Perform server-side SSL/TLS handshake</fsummary> <type> - <v>ListenSocket = socket()</v> + <v>Socket = socket() | sslsocket() </v> <v>SslOptions = ssloptions()</v> <v>Timeout = integer()</v> <v>Reason = term()</v> </type> <desc> - <p> Upgrades a gen_tcp, or - equivalent, socket to an ssl socket i.e. performs the - ssl server-side handshake.</p> + <p> If <c>Socket</c> is a socket() - upgrades a gen_tcp, or equivalent, socket to an ssl socket + i.e. performs the SSL/TLS server-side handshake and returns the ssl socket. + </p> + <warning><p>Note that the listen socket should be in {active, false} mode before telling the client that the server is ready to upgrade - and calling this function, otherwise the upgrade may + by calling this function, otherwise the upgrade may or may not succeed depending on timing.</p></warning> + + <p> If <c>Socket</c> is an sslsocket() - provides additional SSL/TLS options to those specified in <seealso + marker="#listen-2">ssl:listen/2 </seealso> and then performs the SSL/TLS handshake. + </p> </desc> </func> @@ -842,33 +848,38 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>transport_accept(Socket) -></name> - <name>transport_accept(Socket, Timeout) -> + <name>transport_accept(ListenSocket) -></name> + <name>transport_accept(ListenSocket, Timeout) -> {ok, NewSocket} | {error, Reason}</name> <fsummary>Accept an incoming connection and prepare for <c>ssl_accept</c></fsummary> <type> - <v>Socket = NewSocket = sslsocket()</v> + <v>ListenSocket = NewSocket = sslsocket()</v> <v>Timeout = integer()</v> <v>Reason = reason()</v> </type> <desc> <p>Accepts an incoming connection request on a listen socket. - <c>ListenSocket</c> must be a socket returned from - <c>listen/2</c>. The socket returned should be passed to - <c>ssl_accept</c> to complete ssl handshaking and - establishing the connection.</p> + <c>ListenSocket</c> must be a socket returned from + <seealso + marker="#listen-2"> ssl:listen/2</seealso>. + The socket returned should be passed to + <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso> + to complete handshaking i.e + establishing the SSL/TLS connection.</p> <warning> - <p>The socket returned can only be used with <c>ssl_accept</c>, - no traffic can be sent or received before that call.</p> + <p>The socket returned can only be used with + <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso> + no traffic can be sent or received before that call.</p> </warning> <p>The accepted socket inherits the options set for - <c>ListenSocket</c> in <c>listen/2</c>.</p> + <c>ListenSocket</c> in <seealso + marker="#listen-2"> ssl:listen/2</seealso>.</p> <p>The default - value for <c>Timeout</c> is <c>infinity</c>. If - <c>Timeout</c> is specified, and no connection is accepted - within the given time, <c>{error, timeout}</c> is - returned.</p> + value for <c>Timeout</c> is <c>infinity</c>. If + <c>Timeout</c> is specified, and no connection is accepted + within the given time, <c>{error, timeout}</c> is + returned.</p> </desc> </func> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index a88bf45293..743753bf7d 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -195,7 +195,8 @@ transport_accept(#sslsocket{pid = {ListenSocket, -spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() | transport_option()]) -> ok | {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) -> + +-spec ssl_accept(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on an ssl listen socket. e.i. performs @@ -210,6 +211,15 @@ ssl_accept(#sslsocket{} = Socket, Timeout) -> ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(ListenSocket, SslOptions, infinity). +ssl_accept(#sslsocket{} = Socket, [], Timeout) -> + ssl_accept(#sslsocket{} = Socket, Timeout); +ssl_accept(#sslsocket{} = Socket, SslOptions, Timeout) -> + try + {ok, #config{ssl = SSL}} = handle_options(SslOptions, server), + ssl_connection:handshake(Socket, SSL, Timeout) + catch + Error = {error, _Reason} -> Error + end; ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}), diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index ed9e4d344f..c2810a199f 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -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 @@ -100,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()}. @@ -650,6 +664,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 @@ -1855,3 +1873,14 @@ 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]). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index cec5d8fbb1..8bf5b30a83 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -101,7 +101,6 @@ reuse_sessions :: boolean(), renegotiate_at, secure_renegotiate, - debug, %% undefined if not hibernating, or number of ms of %% inactivity after which ssl_connection will go into %% hibernation diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 0148e1f5bc..8e3d2e4b80 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -119,7 +119,8 @@ options_tests() -> ]. api_tests() -> - [connection_info, + [new_options_in_accept, + connection_info, peername, peercert, peercert_with_client_cert, @@ -325,6 +326,37 @@ alerts(Config) when is_list(Config) -> end end, Alerts). %%-------------------------------------------------------------------- +new_options_in_accept() -> + [{doc,"Test that you can set ssl options in ssl_accept/3 and not tcp upgrade"}]. +new_options_in_accept(Config) when is_list(Config) -> + 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()}, + {ssl_opts, [{versions, [sslv3]}, + {ciphers,[{rsa,rc4_128,sha}]}]}, %% To be set in ssl_accept/3 + {mfa, {?MODULE, connection_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, {?MODULE, connection_info_result, []}}, + {options, [{versions, [sslv3]} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ServerMsg = ClientMsg = {ok, {sslv3, {rsa, rc4_128, sha}}}, + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + connection_info() -> [{doc,"Test the API function ssl:connection_info/1"}]. connection_info(Config) when is_list(Config) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 7d8ece8d19..59f10d53a6 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -106,7 +106,8 @@ connect(#sslsocket{} = ListenSocket, Opts) -> Node = proplists:get_value(node, Opts), ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0), Timeout = proplists:get_value(timeout, Opts, infinity), - AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout), + SslOpts = proplists:get_value(ssl_opts, Opts, []), + AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout, SslOpts), case ReconnectTimes of 0 -> AcceptSocket; @@ -121,24 +122,30 @@ connect(ListenSocket, Opts) -> [ListenSocket]), AcceptSocket. -connect(_, _, 0, AcceptSocket, _) -> +connect(_, _, 0, AcceptSocket, _, _) -> AcceptSocket; -connect(ListenSocket, Node, N, _, Timeout) -> - ct:log("~p:~p~nssl:transport_accept(~p)~n", [?MODULE,?LINE, ListenSocket]), + +connect(ListenSocket, Node, N, _, Timeout, []) -> + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, [ListenSocket]), ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, Timeout]), case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of ok -> -ct:log("~p:~p~nok from ssl:ssl_accept@~p",[?MODULE,?LINE, Node]), - connect(ListenSocket, Node, N-1, AcceptSocket, Timeout); + connect(ListenSocket, Node, N-1, AcceptSocket, Timeout, []); Result -> -ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]), + ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]), Result - end. + end; +connect(ListenSocket, Node, _, _, Timeout, Opts) -> + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), + {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, + [ListenSocket]), + ct:log("ssl:ssl_accept(~p,~p, ~p)~n", [AcceptSocket, Opts, Timeout]), + rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Opts, Timeout]), + AcceptSocket. - remove_close_msg(0) -> ok; remove_close_msg(ReconnectTimes) -> |