aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2010-12-17 15:41:27 +0100
committerErlang/OTP <[email protected]>2010-12-17 15:41:27 +0100
commitdadf9c6583f68ac1738a2bd238c76b4504f7a556 (patch)
tree69403231379e7946d2e09c4dad40c7a5e19c456d /lib/ssl/src
parent15daa43c1d5b953b28c998b30d39cec9ab2514b4 (diff)
parent38d3b11a9d62aa1cfe51377b426c43a5ea7f3350 (diff)
downloadotp-dadf9c6583f68ac1738a2bd238c76b4504f7a556.tar.gz
otp-dadf9c6583f68ac1738a2bd238c76b4504f7a556.tar.bz2
otp-dadf9c6583f68ac1738a2bd238c76b4504f7a556.zip
Merge branch 'ia/ssl/session-and-cert-cache-handling/OTP-8965' into maint-r14
* ia/ssl/session-and-cert-cache-handling/OTP-8965: Prepare for release Cache invalidation and consistent user closing Cache invaldation first version does not break old test cases
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl56
-rw-r--r--lib/ssl/src/ssl_connection.erl42
-rw-r--r--lib/ssl/src/ssl_handshake.erl10
-rw-r--r--lib/ssl/src/ssl_handshake.hrl1
-rw-r--r--lib/ssl/src/ssl_manager.erl89
-rw-r--r--lib/ssl/src/ssl_session.erl30
7 files changed, 161 insertions, 69 deletions
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 51c5289bd2..a9c07ec87c 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,10 +1,12 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"4.1.1", [{restart_application, ssl}]},
{"4.1", [{restart_application, ssl}]},
{"4.0.1", [{restart_application, ssl}]}
],
[
+ {"4.1.1", [{restart_application, ssl}]},
{"4.1", [{restart_application, ssl}]},
{"4.0.1", [{restart_application, ssl}]}
]}.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 2a5a7f3394..f34459de37 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -27,7 +27,9 @@
-export([create/0, remove/1, add_trusted_certs/3,
remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
- lookup_cached_certs/1, cache_pem_file/3]).
+ lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]).
+
+-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
%%====================================================================
%% Internal application API
@@ -98,17 +100,35 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
insert(Pid, File, PidToFileDb),
{ok, Ref}.
%%--------------------------------------------------------------------
--spec cache_pem_file(pid(), string(), certdb_ref()) -> term().
+-spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
-cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) ->
+cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) ->
{ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
- insert({file, File}, Content, CertsDb),
+ insert({file, File}, {Time, Content}, CertsDb),
insert(Pid, File, PidToFileDb),
{ok, Content}.
+%--------------------------------------------------------------------
+-spec uncache_pem_file(string(), certdb_ref()) -> no_return().
+%%
+%% Description: If a cached file is no longer valid (changed on disk)
+%% we must terminate the connections using the old file content, and
+%% when those processes are finish the cache will be cleaned. It is
+%% a rare but possible case a new ssl client/server is started with
+%% a filename with the same name as previously started client/server
+%% but with different content.
+%% --------------------------------------------------------------------
+uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) ->
+ [Pids] = select(PidToFileDb, [{{'$1', File},[],['$$']}]),
+ lists:foreach(fun(Pid) ->
+ exit(Pid, shutdown)
+ end, Pids).
+
+
+
%%--------------------------------------------------------------------
-spec remove_trusted_certs(pid(), certdb_ref()) -> term().
@@ -174,6 +194,22 @@ issuer_candidate(PrevCandidateKey) ->
end.
%%--------------------------------------------------------------------
+-spec lookup(term(), term()) -> term() | undefined.
+%%
+%% Description: Looks up an element in a certificat <Db>.
+%%--------------------------------------------------------------------
+lookup(Key, Db) ->
+ case ets:lookup(Db, Key) of
+ [] ->
+ undefined;
+ Contents ->
+ Pick = fun({_, Data}) -> Data;
+ ({_,_,Data}) -> Data
+ end,
+ [Pick(Data) || Data <- Contents]
+ end.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
certificate_db_name() ->
@@ -191,16 +227,8 @@ ref_count(Key, Db,N) ->
delete(Key, Db) ->
_ = ets:delete(Db, Key).
-lookup(Key, Db) ->
- case ets:lookup(Db, Key) of
- [] ->
- undefined;
- Contents ->
- Pick = fun({_, Data}) -> Data;
- ({_,_,Data}) -> Data
- end,
- [Pick(Data) || Data <- Contents]
- end.
+select(Db, MatchSpec)->
+ ets:select(Db, MatchSpec).
remove_certs(Ref, CertsDb) ->
ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 6c9ac65b64..675e5e44bd 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -70,7 +70,7 @@
%% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary())
tls_handshake_hashes, % see above
tls_cipher_texts, % list() received but not deciphered yet
- own_cert, % binary()
+ own_cert, % binary() | undefined
session, % #session{} from ssl_handshake.hrl
session_cache, %
session_cache_cb, %
@@ -90,7 +90,8 @@
log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
recv_during_renegotiation, %boolean()
- send_queue % queue()
+ send_queue, % queue()
+ terminated = false %
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
@@ -304,8 +305,10 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
try ssl_init(SSLOpts0, Role) of
{ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
+ Session = State0#state.session,
State = State0#state{tls_handshake_hashes = Hashes0,
own_cert = OwnCert,
+ session = Session#session{own_certificate = OwnCert},
cert_db_ref = Ref,
session_cache = CacheRef,
private_key = Key,
@@ -331,6 +334,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
%%--------------------------------------------------------------------
hello(start, #state{host = Host, port = Port, role = client,
ssl_options = SslOpts,
+ own_cert = Cert,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates,
renegotiation = {Renegotiation, _}}
@@ -338,7 +342,7 @@ hello(start, #state{host = Host, port = Port, role = client,
Hello = ssl_handshake:client_hello(Host, Port,
ConnectionStates,
- SslOpts, Renegotiation),
+ SslOpts, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
Hashes0 = ssl_handshake:init_hashes(),
@@ -678,6 +682,7 @@ cipher(Msg, State) ->
%%--------------------------------------------------------------------
connection(#hello_request{}, #state{host = Host, port = Port,
socket = Socket,
+ own_cert = Cert,
ssl_options = SslOpts,
negotiated_version = Version,
transport_cb = Transport,
@@ -686,7 +691,7 @@ connection(#hello_request{}, #state{host = Host, port = Port,
tls_handshake_hashes = Hashes0} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0,
- SslOpts, Renegotiation),
+ SslOpts, Renegotiation, Cert),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
@@ -777,8 +782,12 @@ handle_sync_event(start, _, connection, State) ->
handle_sync_event(start, From, StateName, State) ->
{next_state, StateName, State#state{from = From}};
-handle_sync_event(close, _, _StateName, State) ->
- {stop, normal, ok, State};
+handle_sync_event(close, _, StateName, State) ->
+ %% Run terminate before returning
+ %% so that the reuseaddr inet-option will work
+ %% as intended.
+ (catch terminate(user_close, StateName, State)),
+ {stop, normal, ok, State#state{terminated = true}};
handle_sync_event({shutdown, How0}, _, StateName,
#state{transport_cb = Transport,
@@ -966,6 +975,11 @@ handle_info(Msg, StateName, State) ->
%% necessary cleaning up. When it returns, the gen_fsm terminates with
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
+terminate(_, _, #state{terminated = true}) ->
+ %% Happens when user closes the connection using ssl:close/1
+ %% we want to guarantee that Transport:close has been called
+ %% when ssl:close/1 returns.
+ ok;
terminate(Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
@@ -975,14 +989,14 @@ terminate(Reason, connection, #state{negotiated_version = Version,
notify_renegotiater(Renegotiate),
BinAlert = terminate_alert(Reason, Version, ConnectionStates),
Transport:send(Socket, BinAlert),
- workaround_transport_delivery_problems(Socket, Transport),
+ workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket);
-terminate(_Reason, _StateName, #state{transport_cb = Transport,
+terminate(Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate}) ->
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- workaround_transport_delivery_problems(Socket, Transport),
+ workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket).
%%--------------------------------------------------------------------
@@ -2185,7 +2199,8 @@ notify_renegotiater({true, From}) when not is_atom(From) ->
notify_renegotiater(_) ->
ok.
-terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown ->
+terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown;
+ Reason == user_close ->
{BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates),
BinAlert;
@@ -2194,10 +2209,13 @@ terminate_alert(_, Version, ConnectionStates) ->
Version, ConnectionStates),
BinAlert.
-workaround_transport_delivery_problems(Socket, Transport) ->
+workaround_transport_delivery_problems(_,_, user_close) ->
+ ok;
+workaround_transport_delivery_problems(Socket, Transport, _) ->
%% Standard trick to try to make sure all
%% data sent to to tcp port is really sent
- %% before tcp port is closed.
+ %% before tcp port is closed so that the peer will
+ %% get a correct error message.
inet:setopts(Socket, [{active, false}]),
Transport:shutdown(Socket, write),
Transport:recv(Socket, 0).
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index c7a1c4965d..125c28b373 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -30,7 +30,7 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/5, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/6, server_hello/4, hello/4,
hello_request/0, certify/6, certificate/3,
client_certificate_verify/5, certificate_verify/5,
certificate_request/2, key_exchange/2, server_key_exchange_hash/2,
@@ -49,13 +49,13 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(host(), port_num(), #connection_states{},
- #ssl_options{}, boolean()) -> #client_hello{}.
+ #ssl_options{}, boolean(), der_cert()) -> #client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
ciphers = UserSuites}
- = SslOpts, Renegotiation) ->
+ = SslOpts, Renegotiation, OwnCert) ->
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
@@ -65,7 +65,7 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
SecParams = Pending#connection_state.security_parameters,
Ciphers = available_suites(UserSuites, Version),
- Id = ssl_manager:client_session_id(Host, Port, SslOpts),
+ Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert),
#client_hello{session_id = Id,
client_version = Version,
@@ -571,7 +571,7 @@ select_session(Hello, Port, Session, Version,
#ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
SuggestedSessionId = Hello#client_hello.session_id,
SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId,
- SslOpts),
+ SslOpts, Cert),
Suites = available_suites(Cert, UserSuites, Version),
case ssl_session:is_new(SuggestedSessionId, SessionId) of
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 68a7802ef2..8ae4d2332e 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -36,6 +36,7 @@
-record(session, {
session_id,
peer_certificate,
+ own_certificate,
compression_method,
cipher_suite,
master_secret,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 3b02d96562..f845b1ecc0 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -29,8 +29,8 @@
%% Internal application API
-export([start_link/1,
connection_init/2, cache_pem_file/1,
- lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3,
- server_session_id/3,
+ lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4,
+ server_session_id/4,
register_session/2, register_session/3, invalidate_session/2,
invalidate_session/3]).
@@ -43,6 +43,7 @@
-include("ssl_handshake.hrl").
-include("ssl_internal.hrl").
+-include_lib("kernel/include/file.hrl").
-record(state, {
session_cache,
@@ -76,16 +77,17 @@ start_link(Opts) ->
connection_init(Trustedcerts, Role) ->
call({connection_init, Trustedcerts, Role}).
%%--------------------------------------------------------------------
--spec cache_pem_file(string()) -> {ok, term()}.
+-spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}.
%%
-%% Description: Cach a pem file and
+%% Description: Cach a pem file and return its content.
%%--------------------------------------------------------------------
-cache_pem_file(File) ->
- case ssl_certificate_db:lookup_cached_certs(File) of
- [{_,Content}] ->
- {ok, Content};
- [] ->
- call({cache_pem, File})
+cache_pem_file(File) ->
+ try file:read_file_info(File) of
+ {ok, #file_info{mtime = LastWrite}} ->
+ cache_pem_file(File, LastWrite)
+ catch
+ _:Reason ->
+ {error, Reason}
end.
%%--------------------------------------------------------------------
-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
@@ -106,20 +108,21 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
issuer_candidate(PrevCandidateKey) ->
ssl_certificate_db:issuer_candidate(PrevCandidateKey).
%%--------------------------------------------------------------------
--spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id().
+-spec client_session_id(host(), port_num(), #ssl_options{},
+ der_cert() | undefined) -> session_id().
%%
%% Description: Select a session id for the client.
%%--------------------------------------------------------------------
-client_session_id(Host, Port, SslOpts) ->
- call({client_session_id, Host, Port, SslOpts}).
+client_session_id(Host, Port, SslOpts, OwnCert) ->
+ call({client_session_id, Host, Port, SslOpts, OwnCert}).
%%--------------------------------------------------------------------
--spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id().
+-spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id().
%%
%% Description: Select a session id for the server.
%%--------------------------------------------------------------------
-server_session_id(Port, SuggestedSessionId, SslOpts) ->
- call({server_session_id, Port, SuggestedSessionId, SslOpts}).
+server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) ->
+ call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}).
%%--------------------------------------------------------------------
-spec register_session(port_num(), #session{}) -> ok.
@@ -201,28 +204,35 @@ handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
end,
{reply, Result, State};
-handle_call({{client_session_id, Host, Port, SslOpts}, _}, _,
+handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _,
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
- Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb),
+ Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
{reply, Id, State};
-handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _},
+handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _},
_, #state{session_cache_cb = CacheCb,
session_cache = Cache,
session_lifetime = LifeTime} = State) ->
Id = ssl_session:id(Port, SuggestedSessionId, SslOpts,
- Cache, CacheCb, LifeTime),
+ Cache, CacheCb, LifeTime, OwnCert),
{reply, Id, State};
-handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) ->
- try ssl_certificate_db:cache_pem_file(Pid,File,Db) of
+handle_call({{cache_pem, File, LastWrite}, Pid}, _,
+ #state{certificate_db = Db} = State) ->
+ try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of
Result ->
{reply, Result, State}
catch
_:Reason ->
{reply, {error, Reason}, State}
- end.
+ end;
+handle_call({{recache_pem, File, LastWrite}, Pid}, From,
+ #state{certificate_db = Db} = State) ->
+ ssl_certificate_db:uncache_pem_file(File, Db),
+ cast({recache_pem, File, LastWrite, Pid, From}),
+ {noreply, State}.
+
%%--------------------------------------------------------------------
-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
%% Possible return values not used now.
@@ -259,7 +269,21 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}},
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
CacheCb:delete(Cache, {Port, ID}),
- {noreply, State}.
+ {noreply, State};
+
+handle_cast({recache_pem, File, LastWrite, Pid, From},
+ #state{certificate_db = [_, FileToRefDb, _]} = State0) ->
+ case ssl_certificate_db:lookup(File, FileToRefDb) of
+ undefined ->
+ {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0),
+ gen_server:reply(From, Msg),
+ {noreply, State};
+ _ -> %% Send message to self letting cleanup messages be handled
+ %% first so that no reference to the old version of file
+ %% exists when we cache the new one.
+ cast({recache_pem, File, LastWrite, Pid, From}),
+ {noreply, State0}
+ end.
%%--------------------------------------------------------------------
-spec handle_info(msg(), #state{}) -> {noreply, #state{}}.
@@ -286,12 +310,14 @@ handle_info({'EXIT', _, _}, State) ->
handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) ->
{noreply, State};
+handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) ->
+ handle_info({remove_trusted_certs, Pid}, State);
handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) ->
erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(),
{remove_trusted_certs, Pid}),
{noreply, State};
handle_info({remove_trusted_certs, Pid},
- State = #state{certificate_db = Db}) ->
+ #state{certificate_db = Db} = State) ->
ssl_certificate_db:remove_trusted_certs(Pid, Db),
{noreply, State};
@@ -362,3 +388,16 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) ->
session_validation({{Port, _}, Session}, LifeTime) ->
validate_session(Port, Session, LifeTime),
LifeTime.
+
+cache_pem_file(File, LastWrite) ->
+ case ssl_certificate_db:lookup_cached_certs(File) of
+ [{_, {Mtime, Content}}] ->
+ case LastWrite of
+ Mtime ->
+ {ok, Content};
+ _ ->
+ call({recache_pem, File, LastWrite})
+ end;
+ [] ->
+ call({cache_pem, File, LastWrite})
+ end.
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 25e7445180..dc4b7a711c 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -28,7 +28,7 @@
-include("ssl_internal.hrl").
%% Internal application API
--export([is_new/2, id/3, id/6, valid_session/2]).
+-export([is_new/2, id/4, id/7, valid_session/2]).
-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
@@ -48,13 +48,14 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary().
+-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(),
+ undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
%% for the client hello message.
%%--------------------------------------------------------------------
-id(ClientInfo, Cache, CacheCb) ->
- case select_session(ClientInfo, Cache, CacheCb) of
+id(ClientInfo, Cache, CacheCb, OwnCert) ->
+ case select_session(ClientInfo, Cache, CacheCb, OwnCert) of
no_session ->
<<>>;
SessionId ->
@@ -63,19 +64,19 @@ id(ClientInfo, Cache, CacheCb) ->
%%--------------------------------------------------------------------
-spec id(port_num(), binary(), #ssl_options{}, cache_ref(),
- atom(), seconds()) -> binary().
+ atom(), seconds(), binary()) -> binary().
%%
%% Description: Should be called by the server side to get an id
%% for the server hello message.
%%--------------------------------------------------------------------
-id(Port, <<>>, _, Cache, CacheCb, _) ->
+id(Port, <<>>, _, Cache, CacheCb, _, _) ->
new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb);
id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled,
reuse_session = ReuseFun},
- Cache, CacheCb, SecondLifeTime) ->
+ Cache, CacheCb, SecondLifeTime, OwnCert) ->
case is_resumable(SuggestedSessionId, Port, ReuseEnabled,
- ReuseFun, Cache, CacheCb, SecondLifeTime) of
+ ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of
true ->
SuggestedSessionId;
false ->
@@ -93,19 +94,20 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-select_session({HostIP, Port, SslOpts}, Cache, CacheCb) ->
+select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
Sessions = CacheCb:select_session(Cache, {HostIP, Port}),
- select_session(Sessions, SslOpts).
+ select_session(Sessions, SslOpts, OwnCert).
-select_session([], _) ->
+select_session([], _, _) ->
no_session;
select_session(Sessions, #ssl_options{ciphers = Ciphers,
- reuse_sessions = ReuseSession}) ->
+ reuse_sessions = ReuseSession}, OwnCert) ->
IsResumable =
fun(Session) ->
ReuseSession andalso (Session#session.is_resumable) andalso
lists:member(Session#session.cipher_suite, Ciphers)
+ andalso (OwnCert == Session#session.own_certificate)
end,
case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of
[] ->
@@ -140,14 +142,16 @@ new_id(Port, Tries, Cache, CacheCb) ->
end.
is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache,
- CacheCb, SecondLifeTime) ->
+ CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
+ own_certificate = SessionOwnCert,
compression_method = Compression,
is_resumable = Is_resumable,
peer_certificate = PeerCert} = Session ->
ReuseEnabled
andalso Is_resumable
+ andalso (OwnCert == SessionOwnCert)
andalso valid_session(Session, SecondLifeTime)
andalso ReuseFun(SuggestedSessionId, PeerCert,
Compression, CipherSuite);