aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQijiang Fan <[email protected]>2014-12-30 22:40:28 +0800
committerIngela Anderton Andin <[email protected]>2015-05-12 13:57:25 +0200
commit4fe38c4b8b2c8024afb60990e598ff823743fd54 (patch)
treeea9c593b1a6ac3e76913c77dfce1caae97c41bf0
parent996380765561b1a0e3d215437d4560d2e7a72cfb (diff)
downloadotp-4fe38c4b8b2c8024afb60990e598ff823743fd54.tar.gz
otp-4fe38c4b8b2c8024afb60990e598ff823743fd54.tar.bz2
otp-4fe38c4b8b2c8024afb60990e598ff823743fd54.zip
ssl: add SNI server support
-rw-r--r--lib/ssl/src/ssl.erl15
-rw-r--r--lib/ssl/src/ssl_connection.erl16
-rw-r--r--lib/ssl/src/ssl_connection.hrl3
-rw-r--r--lib/ssl/src/ssl_internal.hrl1
-rw-r--r--lib/ssl/src/tls_connection.erl42
5 files changed, 71 insertions, 6 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 6461f64c1c..54cc5e71b6 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -40,7 +40,7 @@
connection_info/1, versions/0, session_info/1, format_error/1,
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1]).
%% Misc
--export([random_bytes/1]).
+-export([random_bytes/1, handle_options/2]).
-deprecated({negotiated_next_protocol, 1, next_major_release}).
@@ -671,6 +671,7 @@ handle_options(Opts0) ->
handle_option(client_preferred_next_protocols, Opts, undefined)),
log_alert = handle_option(log_alert, Opts, true),
server_name_indication = handle_option(server_name_indication, Opts, undefined),
+ sni_hosts = handle_option(sni_hosts, Opts, []),
honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
@@ -687,7 +688,7 @@ handle_options(Opts0) ->
user_lookup_fun, psk_identity, srp_identity, ciphers,
reuse_session, reuse_sessions, ssl_imp,
cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
- erl_dist, alpn_advertised_protocols,
+ erl_dist, alpn_advertised_protocols, sni_hosts,
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
@@ -881,6 +882,10 @@ validate_option(server_name_indication, disable) ->
disable;
validate_option(server_name_indication, undefined) ->
undefined;
+validate_option(sni_hosts, []) ->
+ [];
+validate_option(sni_hosts, [{Hostname, SSLOptions} | Tail]) when is_list(Hostname) ->
+ [{Hostname, validate_options(SSLOptions)} | validate_option(sni_hosts, Tail)];
validate_option(honor_cipher_order, Value) when is_boolean(Value) ->
Value;
validate_option(padding_check, Value) when is_boolean(Value) ->
@@ -896,6 +901,12 @@ validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) an
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
+
+validate_options([]) ->
+ [];
+validate_options([{Opt, Value} | Tail]) ->
+ [{Opt, validate_option(Opt, Value)} | validate_options(Tail)].
+
validate_npn_ordering(client) ->
ok;
validate_npn_ordering(server) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 4a839872a6..2c0b1b6257 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -42,7 +42,8 @@
%% User Events
-export([send/2, recv/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5
+ peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
+ sni_hostname/1
]).
-export([handle_session/7]).
@@ -161,6 +162,14 @@ recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
+-spec sni_hostname(pid()) -> undefined | string().
+%%
+%% Description: Get the SNI hostname
+%%--------------------------------------------------------------------
+sni_hostname(Pid) when is_pid(Pid) ->
+ sync_send_all_state_event(Pid, sni_hostname).
+
+%%--------------------------------------------------------------------
-spec close(pid()) -> ok | {error, reason()}.
%%
%% Description: Close an ssl connection
@@ -845,7 +854,10 @@ handle_sync_event(session_info, _, StateName,
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
- {reply, {ok, Cert}, StateName, State, get_timeout(State)}.
+ {reply, {ok, Cert}, StateName, State, get_timeout(State)};
+handle_sync_event(sni_hostname, _, StateName, #state{sni_hostname = SNIHostname} = State) ->
+ {reply, SNIHostname, StateName, State}.
+
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index e569d706af..d95b51132a 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -80,7 +80,8 @@
expecting_finished = false ::boolean(),
negotiated_protocol = undefined :: undefined | binary(),
client_ecc, % {Curves, PointFmt}
- tracker :: pid() %% Tracker process for listen socket
+ tracker :: pid(), %% Tracker process for listen socket
+ sni_hostname = undefined
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 90f8b8a412..e285f48202 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -122,6 +122,7 @@
next_protocol_selector = undefined, %% fun([binary()]) -> binary())
log_alert :: boolean(),
server_name_indication = undefined,
+ sni_hosts :: [{inet:hostname(), [tuple()]}],
%% Should the server prefer its own cipher order over the one provided by
%% the client?
honor_cipher_order = false :: boolean(),
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 0577222980..d804d7ad37 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -398,6 +398,15 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
tracker = Tracker
}.
+
+update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+ case proplists:get_value(SNIHostname, OrigSSLOptions#ssl_options.sni_hosts) of
+ undefined ->
+ undefined;
+ SSLOption ->
+ ssl:handle_options(SSLOption, OrigSSLOptions)
+ end.
+
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
handle_own_alert(Alert, Version, Current, State);
@@ -439,7 +448,38 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
end,
try
{Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
- State = State0#state{protocol_buffers =
+ State1 = case Packets of
+ [{#client_hello{extensions=HelloExtensions} = ClientHello, _}] ->
+ case HelloExtensions#hello_extensions.sni of
+ undefined ->
+ State0;
+ #sni{hostname = Hostname} ->
+ OrigSSLOptions = State0#state.ssl_options,
+ NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
+ case NewOptions of
+ undefined ->
+ State0;
+ _ ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} =
+ ssl_config:init(NewOptions, State0#state.role),
+ State0#state{
+ session = State0#state.session#session{own_certificate = OwnCert},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = NewOptions,
+ sni_hostname = Hostname
+ }
+ end
+ end;
+ _ ->
+ State0
+ end,
+ State = State1#state{protocol_buffers =
Buffers#protocol_buffers{tls_packets = Packets,
tls_handshake_buffer = Buf}},
handle_tls_handshake(Handle, Next, State)