63
66
%%--------------------------------------------------------------------
64
67
-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}.
66
%% Description: Starts the server
69
%% Description: Starts the ssl manager that takes care of sessions
70
%% and certificate caching.
67
71
%%--------------------------------------------------------------------
68
72
start_link(Opts) ->
69
gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []).
71
%%--------------------------------------------------------------------
72
-spec connection_init(string()| {der, list()}, client | server) -> {ok, reference(), cache_ref()}.
73
gen_server:start_link({local, ?MODULE}, ?MODULE, [?MODULE, Opts], []).
75
%%--------------------------------------------------------------------
76
-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}.
78
%% Description: Starts a special instance of the ssl manager to
79
%% be used by the erlang distribution. Note disables soft upgrade!
80
%%--------------------------------------------------------------------
81
start_link_dist(Opts) ->
82
gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []).
84
%%--------------------------------------------------------------------
85
-spec connection_init(string()| {der, list()}, client | server) ->
86
{ok, certdb_ref(), db_handle(), db_handle()}.
74
88
%% Description: Do necessary initializations for a new connection.
75
89
%%--------------------------------------------------------------------
76
90
connection_init(Trustedcerts, Role) ->
77
91
call({connection_init, Trustedcerts, Role}).
78
92
%%--------------------------------------------------------------------
79
-spec cache_pem_file(string()) -> {ok, term()}.
93
-spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}.
81
%% Description: Cach a pem file and
95
%% Description: Cach a pem file and return its content.
82
96
%%--------------------------------------------------------------------
83
cache_pem_file(File) ->
84
case ssl_certificate_db:lookup_cached_certs(File) of
88
call({cache_pem, File})
97
cache_pem_file(File, DbHandle) ->
98
try file:read_file_info(File) of
99
{ok, #file_info{mtime = LastWrite}} ->
100
cache_pem_file(File, LastWrite, DbHandle)
90
105
%%--------------------------------------------------------------------
91
-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
106
-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) ->
93
108
{ok, {der_cert(), #'OTPCertificate'{}}}.
95
110
%% Description: Lookup the trusted cert with Key = {reference(),
96
111
%% serialnumber(), issuer()}.
97
112
%% --------------------------------------------------------------------
98
lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
99
ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer).
100
%%--------------------------------------------------------------------
101
-spec issuer_candidate(cert_key() | no_candidate) ->
102
{cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates.
104
%% Description: Return next issuer candidate.
105
%%--------------------------------------------------------------------
106
issuer_candidate(PrevCandidateKey) ->
107
ssl_certificate_db:issuer_candidate(PrevCandidateKey).
108
%%--------------------------------------------------------------------
109
-spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id().
113
lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
114
ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer).
116
%%--------------------------------------------------------------------
117
-spec client_session_id(host(), inet:port_number(), #ssl_options{},
118
der_cert() | undefined) -> session_id().
111
120
%% Description: Select a session id for the client.
112
121
%%--------------------------------------------------------------------
113
client_session_id(Host, Port, SslOpts) ->
114
call({client_session_id, Host, Port, SslOpts}).
122
client_session_id(Host, Port, SslOpts, OwnCert) ->
123
call({client_session_id, Host, Port, SslOpts, OwnCert}).
116
125
%%--------------------------------------------------------------------
117
-spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id().
126
-spec server_session_id(host(), inet:port_number(), #ssl_options{},
127
der_cert()) -> session_id().
119
129
%% Description: Select a session id for the server.
120
130
%%--------------------------------------------------------------------
121
server_session_id(Port, SuggestedSessionId, SslOpts) ->
122
call({server_session_id, Port, SuggestedSessionId, SslOpts}).
131
server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) ->
132
call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}).
124
134
%%--------------------------------------------------------------------
125
-spec register_session(port_num(), #session{}) -> ok.
126
-spec register_session(host(), port_num(), #session{}) -> ok.
135
-spec register_session(inet:port_number(), #session{}) -> ok.
136
-spec register_session(host(), inet:port_number(), #session{}) -> ok.
128
138
%% Description: Make the session available for reuse.
129
139
%%--------------------------------------------------------------------
182
195
%% Description: Handling call messages
183
196
%%--------------------------------------------------------------------
184
197
handle_call({{connection_init, "", _Role}, Pid}, _From,
185
#state{session_cache = Cache} = State) ->
198
#state{certificate_db = [CertDb |_],
199
session_cache = Cache} = State) ->
186
200
erlang:monitor(process, Pid),
187
Result = {ok, make_ref(), Cache},
201
Result = {ok, make_ref(),CertDb, Cache},
188
202
{reply, Result, State};
190
204
handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
191
#state{certificate_db = Db,
205
#state{certificate_db = [CertDb|_] =Db,
192
206
session_cache = Cache} = State) ->
193
207
erlang:monitor(process, Pid),
196
210
{ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db),
211
{ok, Ref, CertDb, Cache}
202
216
{reply, Result, State};
204
handle_call({{client_session_id, Host, Port, SslOpts}, _}, _,
218
handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _,
205
219
#state{session_cache = Cache,
206
220
session_cache_cb = CacheCb} = State) ->
207
Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb),
221
Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
208
222
{reply, Id, State};
210
handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _},
224
handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _},
211
225
_, #state{session_cache_cb = CacheCb,
212
226
session_cache = Cache,
213
227
session_lifetime = LifeTime} = State) ->
214
228
Id = ssl_session:id(Port, SuggestedSessionId, SslOpts,
215
Cache, CacheCb, LifeTime),
229
Cache, CacheCb, LifeTime, OwnCert),
216
230
{reply, Id, State};
218
handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) ->
219
try ssl_certificate_db:cache_pem_file(Pid,File,Db) of
232
handle_call({{cache_pem, File, LastWrite}, Pid}, _,
233
#state{certificate_db = Db} = State) ->
234
try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of
221
236
{reply, Result, State}
224
239
{reply, {error, Reason}, State}
241
handle_call({{recache_pem, File, LastWrite}, Pid}, From,
242
#state{certificate_db = Db} = State) ->
243
ssl_certificate_db:uncache_pem_file(File, Db),
244
cast({recache_pem, File, LastWrite, Pid, From}),
226
247
%%--------------------------------------------------------------------
227
248
-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
228
249
%% Possible return values not used now.
248
269
CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession),
249
270
{noreply, State};
251
handle_cast({invalidate_session, Host, Port,
252
#session{session_id = ID}},
253
#state{session_cache = Cache,
254
session_cache_cb = CacheCb} = State) ->
255
CacheCb:delete(Cache, {{Host, Port}, ID}),
258
handle_cast({invalidate_session, Port, #session{session_id = ID}},
259
#state{session_cache = Cache,
260
session_cache_cb = CacheCb} = State) ->
261
CacheCb:delete(Cache, {Port, ID}),
272
handle_cast({invalidate_session, Host, Port,
273
#session{session_id = ID} = Session},
274
#state{session_cache = Cache,
275
session_cache_cb = CacheCb} = State) ->
276
invalidate_session(Cache, CacheCb, {{Host, Port}, ID}, Session, State);
278
handle_cast({invalidate_session, Port, #session{session_id = ID} = Session},
279
#state{session_cache = Cache,
280
session_cache_cb = CacheCb} = State) ->
281
invalidate_session(Cache, CacheCb, {Port, ID}, Session, State);
283
handle_cast({recache_pem, File, LastWrite, Pid, From},
284
#state{certificate_db = [_, FileToRefDb, _]} = State0) ->
285
case ssl_certificate_db:lookup(File, FileToRefDb) of
287
{reply, Msg, State} =
288
handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0),
289
gen_server:reply(From, Msg),
291
_ -> %% Send message to self letting cleanup messages be handled
292
%% first so that no reference to the old version of file
293
%% exists when we cache the new one.
294
cast({recache_pem, File, LastWrite, Pid, From}),
264
298
%%--------------------------------------------------------------------
265
299
-spec handle_info(msg(), #state{}) -> {noreply, #state{}}.
362
405
session_validation({{Port, _}, Session}, LifeTime) ->
363
406
validate_session(Port, Session, LifeTime),
409
cache_pem_file(File, LastWrite, DbHandle) ->
410
case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of
411
[{_, {Mtime, Content}}] ->
416
call({recache_pem, File, LastWrite})
419
call({cache_pem, File, LastWrite})
423
case application:get_env(ssl, session_delay_cleanup_time) of
424
{ok, Time} when is_integer(Time) ->
430
invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastTimer} = State) ->
431
case CacheCb:lookup(Cache, Key) of
432
undefined -> %% Session is already invalidated
434
#session{is_resumable = new} ->
435
CacheCb:delete(Cache, Key),
438
%% When a registered session is invalidated we need to wait a while before deleting
439
%% it as there might be pending connections that rightfully needs to look
440
%% up the session data but new connections should not get to use this session.
441
CacheCb:update(Cache, Key, Session#session{is_resumable = false}),
443
erlang:send_after(delay_time(), self(), {delayed_clean_session, Key}),
444
{noreply, State#state{last_delay_timer = last_delay_timer(Key, TRef, LastTimer)}}
447
last_delay_timer({{_,_},_}, TRef, {LastServer, _}) ->
449
last_delay_timer({_,_}, TRef, {_, LastClient}) ->