aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/ssl/src/ssl.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/ssl/src/ssl.erl')
-rw-r--r--lib/ssl/src/ssl.erl841
1 files changed, 841 insertions, 0 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
new file mode 100644
index 0000000000..1222fe97fd
--- /dev/null
+++ b/lib/ssl/src/ssl.erl
@@ -0,0 +1,841 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+%%% Purpose : Main API module for SSL.
+
+-module(ssl).
+
+-export([start/0, start/1, stop/0, transport_accept/1,
+ transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
+ ciphers/0, cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
+ connect/3, connect/2, connect/4, connection_info/1,
+ controlling_process/2, listen/2, pid/1, peername/1, recv/2, recv/3,
+ send/2, getopts/2, setopts/2, seed/1, sockname/1, peercert/1,
+ peercert/2, version/0, versions/0, session_info/1, format_error/1]).
+
+%% Should be deprecated as soon as old ssl is removed
+%%-deprecated({pid, 1, next_major_release}).
+
+-include("ssl_int.hrl").
+-include("ssl_internal.hrl").
+
+-record(config, {ssl, %% SSL parameters
+ inet_user, %% User set inet options
+ emulated, %% #socket_option{} emulated
+ inet_ssl, %% inet options for internal ssl socket
+ cb %% Callback info
+ }).
+
+%%--------------------------------------------------------------------
+%% Function: start([, Type]) -> ok
+%%
+%% Type = permanent | transient | temporary
+%% Vsns = [Vsn]
+%% Vsn = ssl3 | tlsv1 | 'tlsv1.1'
+%%
+%% Description: Starts the ssl application. Default type
+%% is temporary. see application(3)
+%%--------------------------------------------------------------------
+start() ->
+ application:start(ssl).
+start(Type) ->
+ application:start(ssl, Type).
+
+%%--------------------------------------------------------------------
+%% Function: stop() -> ok
+%%
+%% Description: Stops the ssl application.
+%%--------------------------------------------------------------------
+stop() ->
+ application:stop(ssl).
+
+%%--------------------------------------------------------------------
+%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket}
+%%
+%% Description: Connect to a ssl server.
+%%--------------------------------------------------------------------
+connect(Socket, SslOptions) when is_port(Socket) ->
+ connect(Socket, SslOptions, infinity).
+
+connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
+ EmulatedOptions = emulated_options(),
+ {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
+ inet:setopts(Socket, internal_inet_values()),
+ try handle_options(SslOptions0 ++ InetValues, client) of
+ {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} ->
+ case inet:peername(Socket) of
+ {ok, {Address, Port}} ->
+ ssl_connection:connect(Address, Port, Socket,
+ {SslOptions, EmOpts},
+ self(), CbInfo, Timeout);
+ {error, Error} ->
+ {error, Error}
+ end
+ catch
+ _:{error, Reason} ->
+ {error, Reason}
+ end;
+
+connect(Address, Port, Options) ->
+ connect(Address, Port, Options, infinity).
+
+connect(Address, Port, Options0, Timeout) ->
+ case proplists:get_value(ssl_imp, Options0, old) of
+ new ->
+ new_connect(Address, Port, Options0, Timeout);
+ old ->
+ %% Allow the option reuseaddr to be present
+ %% so that new and old ssl can be run by the same
+ %% code, however the option will be ignored by old ssl
+ %% that hardcodes reuseaddr to true in its portprogram.
+ Options1 = proplists:delete(reuseaddr, Options0),
+ Options = proplists:delete(ssl_imp, Options1),
+ old_connect(Address, Port, Options, Timeout);
+ Value ->
+ {error, {eoptions, {ssl_imp, Value}}}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason}
+%%
+%% Description: Creates a ssl listen socket.
+%%--------------------------------------------------------------------
+listen(_Port, []) ->
+ {error, enooptions};
+listen(Port, Options0) ->
+ case proplists:get_value(ssl_imp, Options0, old) of
+ new ->
+ new_listen(Port, Options0);
+ old ->
+ %% Allow the option reuseaddr to be present
+ %% so that new and old ssl can be run by the same
+ %% code, however the option will be ignored by old ssl
+ %% that hardcodes reuseaddr to true in its portprogram.
+ Options = proplists:delete(reuseaddr, Options0),
+ old_listen(Port, Options);
+ Value ->
+ {error, {eoptions, {ssl_imp, Value}}}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}.
+%%
+%% Description: Performs transport accept on a ssl listen socket
+%%--------------------------------------------------------------------
+transport_accept(ListenSocket) ->
+ transport_accept(ListenSocket, infinity).
+
+transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}},
+ fd = new_ssl} = SslSocket, Timeout) ->
+
+ %% The setopt could have been invoked on the listen socket
+ %% and options should be inherited.
+ EmOptions = emulated_options(),
+ {ok, InetValues} = inet:getopts(ListenSocket, EmOptions),
+ {CbModule,_,_} = CbInfo,
+ {ok, Socket} = CbModule:accept(ListenSocket, Timeout),
+ inet:setopts(Socket, internal_inet_values()),
+ {ok, Port} = inet:port(Socket),
+ case ssl_connection_sup:start_child([server, "localhost", Port, Socket,
+ {SslOpts, socket_options(InetValues)}, self(),
+ CbInfo]) of
+ {ok, Pid} ->
+ CbModule:controlling_process(Socket, Pid),
+ {ok, SslSocket#sslsocket{pid = Pid}};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+
+transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(acceptor),
+ ssl_broker:transport_accept(Pid, ListenSocket, Timeout).
+
+%%--------------------------------------------------------------------
+%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} |
+%% {error, Reason}
+%%
+%% Description: Performs accept on a ssl listen socket. e.i. performs
+%% ssl handshake.
+%%--------------------------------------------------------------------
+ssl_accept(ListenSocket) ->
+ ssl_accept(ListenSocket, infinity).
+
+ssl_accept(#sslsocket{pid = Pid, fd = new_ssl}, Timeout) ->
+ gen_fsm:send_event(Pid, socket_control),
+ try gen_fsm:sync_send_all_state_event(Pid, started, Timeout) of
+ connected ->
+ ok;
+ {error, _} = Error ->
+ Error
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{timeout, _} ->
+ {error, timeout};
+ exit:{normal, _} ->
+ {error, closed}
+ end;
+
+ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
+ ssl_accept(ListenSocket, SslOptions, infinity);
+
+%% Old ssl
+ssl_accept(#sslsocket{} = Socket, Timeout) ->
+ ensure_old_ssl_started(),
+ ssl_broker:ssl_accept(Socket, Timeout).
+
+ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
+ EmulatedOptions = emulated_options(),
+ {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
+ inet:setopts(Socket, internal_inet_values()),
+ try handle_options(SslOptions ++ InetValues, server) of
+ {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} ->
+ {ok, Port} = inet:port(Socket),
+ ssl_connection:accept(Port, Socket,
+ {SslOpts, EmOpts},
+ self(), CbInfo, Timeout)
+ catch
+ Error = {error, _Reason} -> Error
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: close() -> ok
+%%
+%% Description: Close a ssl connection
+%%--------------------------------------------------------------------
+close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}) ->
+ CbMod:close(ListenSocket);
+close(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:close(Pid);
+close(Socket = #sslsocket{}) ->
+ ensure_old_ssl_started(),
+ ssl_broker:close(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: send(Socket, Data) -> ok
+%%
+%% Description: Sends data over the ssl connection
+%%--------------------------------------------------------------------
+send(#sslsocket{pid = Pid, fd = new_ssl}, Data) ->
+ ssl_connection:send(Pid, Data);
+
+send(#sslsocket{} = Socket, Data) ->
+ ensure_old_ssl_started(),
+ ssl_broker:send(Socket, Data).
+
+%%--------------------------------------------------------------------
+%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason}
+%%
+%% Description: Receives data when active = false
+%%--------------------------------------------------------------------
+recv(Socket, Length) ->
+ recv(Socket, Length, infinity).
+recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
+ ssl_connection:recv(Pid, Length, Timeout);
+
+recv(Socket = #sslsocket{}, Length, Timeout) ->
+ ensure_old_ssl_started(),
+ ssl_broker:recv(Socket, Length, Timeout).
+
+%%--------------------------------------------------------------------
+%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason}
+%%
+%% Description: Changes process that receives the messages when active = true
+%% or once.
+%%--------------------------------------------------------------------
+controlling_process(#sslsocket{pid = Pid, fd = new_ssl}, NewOwner)
+ when is_pid(Pid) ->
+ ssl_connection:new_user(Pid, NewOwner);
+
+controlling_process(Socket, NewOwner) when is_pid(NewOwner) ->
+ ensure_old_ssl_started(),
+ ssl_broker:controlling_process(Socket, NewOwner).
+
+%%--------------------------------------------------------------------
+%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} |
+%% {error, Reason}
+%% Protocol = sslv3 | tlsv1 | tlsv1.1
+%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable}
+%%
+%%
+%% Description: Returns ssl protocol and cipher used for the connection
+%%--------------------------------------------------------------------
+connection_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:info(Pid);
+
+connection_info(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:connection_info(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peercert(Socket) ->
+ peercert(Socket, []).
+
+peercert(#sslsocket{pid = Pid, fd = new_ssl}, Opts) ->
+ case ssl_connection:peer_certificate(Pid) of
+ {ok, undefined} ->
+ {error, no_peercert};
+ {ok, BinCert} ->
+ PKOpts = [case Opt of ssl -> otp; pkix -> plain end ||
+ Opt <- Opts, Opt =:= ssl orelse Opt =:= pkix],
+ case PKOpts of
+ [Opt] ->
+ public_key:pkix_decode_cert(BinCert, Opt);
+ [] ->
+ {ok, BinCert}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+
+peercert(#sslsocket{} = Socket, Opts) ->
+ ensure_old_ssl_started(),
+ case ssl_broker:peercert(Socket) of
+ {ok, Bin} ->
+ ssl_pkix:decode_cert(Bin, Opts);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peername(#sslsocket{fd = new_ssl, pid = Pid}) ->
+ ssl_connection:peername(Pid);
+
+peername(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:peername(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: cipher_suites() ->
+%%
+%% Description:
+%%--------------------------------------------------------------------
+cipher_suites() ->
+ cipher_suites(erlang).
+
+cipher_suites(erlang) ->
+ Version = ssl_record:highest_protocol_version([]),
+ [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)];
+
+cipher_suites(openssl) ->
+ Version = ssl_record:highest_protocol_version([]),
+ [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)].
+
+%%--------------------------------------------------------------------
+%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptTags) when is_pid(Pid) ->
+ ssl_connection:get_opts(Pid, OptTags);
+getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
+ inet:getopts(ListenSocket, OptTags);
+getopts(#sslsocket{} = Socket, Options) ->
+ ensure_old_ssl_started(),
+ ssl_broker:getopts(Socket, Options).
+
+%%--------------------------------------------------------------------
+%% Function: setopts(Socket, Options) -> ok | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options) when is_pid(Pid) ->
+ ssl_connection:set_opts(Pid, Options);
+setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
+ inet:setopts(ListenSocket, OptTags);
+setopts(#sslsocket{} = Socket, Options) ->
+ ensure_old_ssl_started(),
+ ssl_broker:setopts(Socket, Options).
+
+%%---------------------------------------------------------------
+%% Function: shutdown(Socket, How) -> ok | {error, Reason}
+%%
+%% Description: Same as gen_tcp:shutdown/2
+%%--------------------------------------------------------------------
+shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}, How) ->
+ CbMod:shutdown(ListenSocket, How);
+shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) ->
+ ssl_connection:shutdown(Pid, How).
+
+%%--------------------------------------------------------------------
+%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}
+%%
+%% Description: Same as inet:sockname/1
+%%--------------------------------------------------------------------
+sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) ->
+ inet:sockname(ListenSocket);
+
+sockname(#sslsocket{fd = new_ssl, pid = Pid}) ->
+ ssl_connection:sockname(Pid);
+
+sockname(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:sockname(Socket).
+
+%%---------------------------------------------------------------
+%% Function: seed(Data) -> ok | {error, edata}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+%% TODO: crypto:seed ?
+seed(Data) ->
+ ensure_old_ssl_started(),
+ ssl_server:seed(Data).
+
+%%---------------------------------------------------------------
+%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+session_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:session_info(Pid).
+
+%%---------------------------------------------------------------
+%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}]
+%%
+%% SslAppVer = string() - t.ex: ssl-4.0
+%% SupportedSslVer = [SslVer]
+%% AvailableSslVsn = [SSLVer]
+%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1'
+%%
+%% Description: Returns a list of relevant versions.
+%%--------------------------------------------------------------------
+versions() ->
+ Vsns = ssl_record:supported_protocol_versions(),
+ SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns],
+ AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
+ [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+
+%%%--------------------------------------------------------------
+%%% Internal functions
+%%%--------------------------------------------------------------------
+new_connect(Address, Port, Options, Timeout) when is_list(Options) ->
+ try handle_options(Options, client) of
+ {ok, Config} ->
+ do_new_connect(Address,Port,Config,Timeout)
+ catch
+ throw:Error ->
+ Error
+ end.
+
+do_new_connect(Address, Port,
+ #config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts,
+ emulated=EmOpts,inet_ssl=SocketOpts},
+ Timeout) ->
+ {CbModule, _, _} = CbInfo,
+ try CbModule:connect(Address, Port, SocketOpts, Timeout) of
+ {ok, Socket} ->
+ ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts},
+ self(), CbInfo, Timeout);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{function_clause, _} ->
+ {error, {eoptions, {cb_info, CbInfo}}};
+ exit:{badarg, _} ->
+ {error,{eoptions, {inet_options, UserOpts}}}
+ end.
+
+old_connect(Address, Port, Options, Timeout) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(connector),
+ ssl_broker:connect(Pid, Address, Port, Options, Timeout).
+
+new_listen(Port, Options0) ->
+ try
+ {ok, Config} = handle_options(Options0, server),
+ #config{cb={CbModule, _, _},inet_user=Options} = Config,
+ case CbModule:listen(Port, Options) of
+ {ok, ListenSocket} ->
+ {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
+ Err = {error, _} ->
+ Err
+ end
+ catch
+ Error = {error, _} ->
+ Error
+ end.
+
+old_listen(Port, Options) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(listener),
+ ssl_broker:listen(Pid, Port, Options).
+
+handle_options(Opts0, Role) ->
+ Opts = proplists:expand([{binary, [{mode, binary}]},
+ {list, [{mode, list}]}], Opts0),
+
+ ReuseSessionFun = fun(_, _, _, _) -> true end,
+
+ AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc;
+ (Other, Acc) -> [Other | Acc]
+ end,
+
+ VerifyFun =
+ fun(ErrorList) ->
+ case lists:foldl(AcceptBadCa, [], ErrorList) of
+ [] -> true;
+ [_|_] -> false
+ end
+ end,
+
+ {Verify, FailIfNoPeerCert, CaCertDefault} =
+ %% Handle 0, 1, 2 for backwards compatibility
+ case proplists:get_value(verify, Opts, verify_none) of
+ 0 ->
+ {verify_none, false, ca_cert_default(verify_none, Role)};
+ 1 ->
+ {verify_peer, false, ca_cert_default(verify_peer, Role)};
+ 2 ->
+ {verify_peer, true, ca_cert_default(verify_peer, Role)};
+ verify_none ->
+ {verify_none, false, ca_cert_default(verify_none, Role)};
+ verify_peer ->
+ {verify_peer, proplists:get_value(fail_if_no_peer_cert,
+ Opts, false),
+ ca_cert_default(verify_peer, Role)};
+ Value ->
+ throw({error, {eoptions, {verify, Value}}})
+ end,
+
+ CertFile = handle_option(certfile, Opts, ""),
+
+ SSLOptions = #ssl_options{
+ versions = handle_option(versions, Opts, []),
+ verify = validate_option(verify, Verify),
+ verify_fun = handle_option(verify_fun, Opts, VerifyFun),
+ fail_if_no_peer_cert = validate_option(fail_if_no_peer_cert,
+ FailIfNoPeerCert),
+ verify_client_once = handle_option(verify_client_once, Opts, false),
+ depth = handle_option(depth, Opts, 1),
+ certfile = CertFile,
+ keyfile = handle_option(keyfile, Opts, CertFile),
+ key = handle_option(key, Opts, undefined),
+ password = handle_option(password, Opts, ""),
+ cacertfile = handle_option(cacertfile, Opts, CaCertDefault),
+ ciphers = handle_option(ciphers, Opts, []),
+ %% Server side option
+ reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
+ reuse_sessions = handle_option(reuse_sessions, Opts, true),
+ debug = handle_option(debug, Opts, [])
+ },
+
+ CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}),
+ SslOptions = [versions, verify, verify_fun,
+ depth, certfile, keyfile,
+ key, password, cacertfile, ciphers,
+ debug, reuse_session, reuse_sessions, ssl_imp,
+ cd_info],
+
+ SockOpts = lists:foldl(fun(Key, PropList) ->
+ proplists:delete(Key, PropList)
+ end, Opts, SslOptions),
+
+ {SSLsock, Emulated} = emulated_options(SockOpts),
+ {ok, #config{ssl=SSLOptions, emulated=Emulated, inet_ssl=SSLsock,
+ inet_user=SockOpts, cb=CbInfo}}.
+
+handle_option(OptionName, Opts, Default) ->
+ validate_option(OptionName,
+ proplists:get_value(OptionName, Opts, Default)).
+
+
+validate_option(versions, Versions) ->
+ validate_versions(Versions, Versions);
+validate_option(ssl_imp, Value) when Value == new; Value == old ->
+ Value;
+validate_option(verify, Value)
+ when Value == verify_none; Value == verify_peer ->
+ Value;
+validate_option(verify_fun, Value) when is_function(Value) ->
+ Value;
+validate_option(fail_if_no_peer_cert, Value)
+ when Value == true; Value == false ->
+ Value;
+validate_option(verify_client_once, Value)
+ when Value == true; Value == false ->
+ Value;
+validate_option(depth, Value) when is_integer(Value),
+ Value >= 0, Value =< 255->
+ Value;
+validate_option(certfile, Value) when is_list(Value) ->
+ Value;
+validate_option(keyfile, Value) when is_list(Value) ->
+ Value;
+validate_option(key, Value) when Value == undefined;
+ is_tuple(Value) ->
+ %% element(1, Value)=='RSAPrivateKey' ->
+ Value;
+validate_option(password, Value) when is_list(Value) ->
+ Value;
+
+%% certfile must be present in some cases otherwhise it can be set
+%% to the empty string.
+validate_option(cacertfile, undefined) ->
+ "";
+validate_option(cacertfile, Value) when is_list(Value), Value =/= "" ->
+ Value;
+validate_option(ciphers, Value) when is_list(Value) ->
+ Version = ssl_record:highest_protocol_version([]),
+ try cipher_suites(Version, Value)
+ catch
+ exit:_ ->
+ throw({error, {eoptions, {ciphers, Value}}})
+ end;
+validate_option(reuse_session, Value) when is_function(Value) ->
+ Value;
+validate_option(reuse_sessions, Value) when Value == true;
+ Value == false ->
+ Value;
+validate_option(debug, Value) when is_list(Value); Value == true ->
+ Value;
+validate_option(Opt, Value) ->
+ throw({error, {eoptions, {Opt, Value}}}).
+
+validate_versions([], Versions) ->
+ Versions;
+validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ validate_versions(Rest, Versions);
+validate_versions(Ver, Versions) ->
+ throw({error, {eoptions, {Ver, {versions, Versions}}}}).
+
+validate_inet_option(mode, Value)
+ when Value =/= list, Value =/= binary ->
+ throw({error, {eoptions, {mode,Value}}});
+validate_inet_option(packet, Value)
+ when not (is_atom(Value) orelse is_integer(Value)) ->
+ throw({error, {eoptions, {packet,Value}}});
+validate_inet_option(packet_size, Value)
+ when not is_integer(Value) ->
+ throw({error, {eoptions, {packet_size,Value}}});
+validate_inet_option(header, Value)
+ when not is_integer(Value) ->
+ throw({error, {eoptions, {header,Value}}});
+validate_inet_option(active, Value)
+ when Value =/= true, Value =/= false, Value =/= once ->
+ throw({error, {eoptions, {active,Value}}});
+validate_inet_option(_, _) ->
+ ok.
+
+ca_cert_default(verify_none, _) ->
+ undefined;
+%% Client may leave verification up to the user
+ca_cert_default(verify_peer, client) ->
+ undefined;
+%% Server that wants to verify_peer must have
+%% some trusted certs.
+ca_cert_default(verify_peer, server) ->
+ "".
+
+emulated_options() ->
+ [mode, packet, active, header, packet_size].
+
+internal_inet_values() ->
+ [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}].
+ %%[{packet, ssl},{header, 0},{active, false},{mode,binary}].
+
+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),
+ packet_size = proplists:get_value(packet_size, InetValues)
+ }.
+
+emulated_options(Opts) ->
+ emulated_options(Opts, internal_inet_values(), #socket_options{}).
+
+emulated_options([{mode,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(mode,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt});
+emulated_options([{header,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(header,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{header=Opt});
+emulated_options([{active,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(active,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{active=Opt});
+emulated_options([{packet,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(packet,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt});
+emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(packet_size,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt});
+emulated_options([Opt|Opts], Inet, Emulated) ->
+ emulated_options(Opts, [Opt|Inet], Emulated);
+emulated_options([], Inet,Emulated) ->
+ {Inet, Emulated}.
+
+cipher_suites(Version, []) ->
+ ssl_cipher:suites(Version);
+cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) ->
+ Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
+ cipher_suites(Version, Ciphers);
+cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
+ Supported = ssl_cipher:suites(Version),
+ case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
+ [] ->
+ Supported;
+ Ciphers ->
+ Ciphers
+ end;
+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) ->
+ %% Format: "RC4-SHA:RC4-MD5"
+ Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
+ cipher_suites(Version, Ciphers).
+
+format_error({error, Reason}) ->
+ format_error(Reason);
+format_error(closed) ->
+ "Connection closed for the operation in question.";
+format_error(ebadsocket) ->
+ "Connection not found (internal error).";
+format_error(ebadstate) ->
+ "Connection not in connect state (internal error).";
+format_error(ebrokertype) ->
+ "Wrong broker type (internal error).";
+format_error(ecacertfile) ->
+ "Own CA certificate file is invalid.";
+format_error(ecertfile) ->
+ "Own certificate file is invalid.";
+format_error(echaintoolong) ->
+ "The chain of certificates provided by peer is too long.";
+format_error(ecipher) ->
+ "Own list of specified ciphers is invalid.";
+format_error(ekeyfile) ->
+ "Own private key file is invalid.";
+format_error(ekeymismatch) ->
+ "Own private key does not match own certificate.";
+format_error(enoissuercert) ->
+ "Cannot find certificate of issuer of certificate provided by peer.";
+format_error(enoservercert) ->
+ "Attempt to do accept without having set own certificate.";
+format_error(enotlistener) ->
+ "Attempt to accept on a non-listening socket.";
+format_error(enoproxysocket) ->
+ "No proxy socket found (internal error or max number of file "
+ "descriptors exceeded).";
+format_error(enooptions) ->
+ "List of options is empty.";
+format_error(enotstarted) ->
+ "The SSL application has not been started.";
+format_error(eoptions) ->
+ "Invalid list of options.";
+format_error(epeercert) ->
+ "Certificate provided by peer is in error.";
+format_error(epeercertexpired) ->
+ "Certificate provided by peer has expired.";
+format_error(epeercertinvalid) ->
+ "Certificate provided by peer is invalid.";
+format_error(eselfsignedcert) ->
+ "Certificate provided by peer is self signed.";
+format_error(esslaccept) ->
+ "Server SSL handshake procedure between client and server failed.";
+format_error(esslconnect) ->
+ "Client SSL handshake procedure between client and server failed.";
+format_error(esslerrssl) ->
+ "SSL protocol failure. Typically because of a fatal alert from peer.";
+format_error(ewantconnect) ->
+ "Protocol wants to connect, which is not supported in this "
+ "version of the SSL application.";
+format_error(ex509lookup) ->
+ "Protocol wants X.509 lookup, which is not supported in this "
+ "version of the SSL application.";
+format_error({badcall, _Call}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+format_error({badcast, _Cast}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+
+format_error({badinfo, _Info}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+format_error(Error) ->
+ case (catch inet:format_error(Error)) of
+ "unkknown POSIX" ++ _ ->
+ no_format(Error);
+ {'EXIT', _} ->
+ no_format(Error);
+ Other ->
+ Other
+ end.
+
+no_format(Error) ->
+ io_lib:format("No format string for error: \"~p\" available.", [Error]).
+
+%% Start old ssl port program if needed.
+ensure_old_ssl_started() ->
+ case whereis(ssl_server) of
+ undefined ->
+ (catch supervisor:start_child(ssl_sup,
+ {ssl_server, {ssl_server, start_link, []},
+ permanent, 2000, worker, [ssl_server]}));
+ _ ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%% Deprecated %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ciphers() ->
+ ensure_old_ssl_started(),
+ case (catch ssl_server:ciphers()) of
+ {'EXIT', _} ->
+ {error, enotstarted};
+ Res = {ok, _} ->
+ Res
+ end.
+
+version() ->
+ ensure_old_ssl_started(),
+ SSLVsn = ?VSN,
+ {CompVsn, LibVsn} = case (catch ssl_server:version()) of
+ {'EXIT', _} ->
+ {"", ""};
+ {ok, Vsns} ->
+ Vsns
+ end,
+ {ok, {SSLVsn, CompVsn, LibVsn}}.
+
+%% Only used to remove exit messages from old ssl
+%% First is a nonsense clause to provide some
+%% backward compability for orber that uses this
+%% function in a none recommended way, but will
+%% work correctly if a valid pid is returned.
+pid(#sslsocket{fd = new_ssl}) ->
+ whereis(ssl_connection_sup);
+pid(#sslsocket{pid = Pid}) ->
+ Pid.