diff options
Diffstat (limited to 'lib/ssl/src/ssl_manager.erl')
-rw-r--r-- | lib/ssl/src/ssl_manager.erl | 204 |
1 files changed, 111 insertions, 93 deletions
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 6389ff03f5..af2bfa394d 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -30,9 +30,9 @@ -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, lookup_trusted_cert/4, - client_session_id/4, server_session_id/4, + new_session_id/1, clean_cert_db/2, register_session/2, register_session/3, invalidate_session/2, - invalidate_session/3]). + invalidate_session/3, clear_pem_cache/0]). % Spawn export -export([init_session_validator/1]). @@ -56,9 +56,12 @@ -define('24H_in_msec', 8640000). -define('24H_in_sec', 8640). +-define(GEN_UNIQUE_ID_MAX_TRIES, 10). -define(SESSION_VALIDATION_INTERVAL, 60000). --define(CERTIFICATE_CACHE_CLEANUP, 30000). +-define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). +-define(CLEAN_CERT_DB, 500). +-define(NOT_TO_BIG, 10). %%==================================================================== %% API @@ -82,26 +85,46 @@ start_link_dist(Opts) -> gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []). %%-------------------------------------------------------------------- --spec connection_init(string()| {der, list()}, client | server) -> - {ok, certdb_ref(), db_handle(), db_handle()}. +-spec connection_init(binary()| {der, list()}, client | server) -> + {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- +connection_init({der, _} = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + +connection_init(<<>> = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). + %%-------------------------------------------------------------------- --spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}. +-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. %% %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - try file:read_file_info(File) of - {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite, DbHandle) - catch - _:Reason -> - {error, Reason} + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of + [{Content,_}] -> + {ok, Content}; + [Content] -> + {ok, Content}; + undefined -> + call({cache_pem, {MD5, File}}) end. + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + %% Not supported for distribution at the moement, should it be? + put(ssl_manager, ssl_manager), + call(unconditionally_clear_pem_cache). + %%-------------------------------------------------------------------- -spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | @@ -114,22 +137,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec client_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert() | undefined) -> session_id(). +-spec new_session_id(integer()) -> session_id(). %% -%% Description: Select a session id for the client. +%% Description: Creates a session id for the server. %%-------------------------------------------------------------------- -client_session_id(Host, Port, SslOpts, OwnCert) -> - call({client_session_id, Host, Port, SslOpts, OwnCert}). +new_session_id(Port) -> + call({new_session_id, Port}). -%%-------------------------------------------------------------------- --spec server_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert()) -> session_id(). -%% -%% Description: Select a session id for the server. -%%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). +clean_cert_db(Ref, File) -> + erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}). %%-------------------------------------------------------------------- -spec register_session(inet:port_number(), #session{}) -> ok. @@ -177,6 +193,7 @@ init([Name, Opts]) -> SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])), Timer = erlang:send_after(SessionLifeTime * 1000, self(), validate_sessions), + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {ok, #state{certificate_db = CertDb, session_cache = SessionCache, session_cache_cb = CacheCb, @@ -194,55 +211,44 @@ init([Name, Opts]) -> %% %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({{connection_init, "", _Role}, Pid}, _From, - #state{certificate_db = [CertDb |_], +handle_call({{connection_init, <<>>, _Role}, _Pid}, _From, + #state{certificate_db = [CertDb, FileRefDb, PemChace], session_cache = Cache} = State) -> - erlang:monitor(process, Pid), - Result = {ok, make_ref(),CertDb, Cache}, + Result = {ok, make_ref(),CertDb, FileRefDb, PemChace, Cache}, {reply, Result, State}; handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, - #state{certificate_db = [CertDb|_] =Db, + #state{certificate_db = [CertDb, FileRefDb, PemChace] = Db, session_cache = Cache} = State) -> - erlang:monitor(process, Pid), Result = try {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), - {ok, Ref, CertDb, Cache} + {ok, Ref, CertDb, FileRefDb, PemChace, Cache} catch _:Reason -> {error, Reason} end, {reply, Result, State}; -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, OwnCert), - {reply, Id, State}; - -handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _}, +handle_call({{new_session_id,Port}, _}, _, #state{session_cache_cb = CacheCb, - session_cache = Cache, - session_lifetime = LifeTime} = State) -> - Id = ssl_session:id(Port, SuggestedSessionId, SslOpts, - Cache, CacheCb, LifeTime, OwnCert), + session_cache = Cache} = State) -> + Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), {reply, Id, State}; -handle_call({{cache_pem, File, LastWrite}, Pid}, _, + +handle_call({{cache_pem, File}, _Pid}, _, #state{certificate_db = Db} = State) -> - try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of + try ssl_certificate_db:cache_pem_file(File, Db) of Result -> {reply, Result, State} catch _:Reason -> {reply, {error, Reason}, State} 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}. +handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) -> + ssl_certificate_db:clear(PemChace), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -278,22 +284,7 @@ handle_cast({invalidate_session, Host, Port, handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - invalidate_session(Cache, CacheCb, {Port, ID}, Session, 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. + invalidate_session(Cache, CacheCb, {Port, ID}, Session, State). %%-------------------------------------------------------------------- -spec handle_info(msg(), #state{}) -> {noreply, #state{}}. @@ -318,23 +309,38 @@ handle_info({delayed_clean_session, Key}, #state{session_cache = Cache, CacheCb:delete(Cache, Key), {noreply, State}; -handle_info({'EXIT', _, _}, State) -> - %% Session validator died!! Do we need to take any action? - %% maybe error log +handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) -> + case ssl_certificate_db:db_size(PemChace) of + N when N < ?NOT_TO_BIG -> + ok; + _ -> + ssl_certificate_db:clear(PemChace) + end, + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {noreply, 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}), +handle_info({clean_cert_db, Ref, File}, + #state{certificate_db = [CertDb,RefDb, PemCache]} = State) -> + case ssl_certificate_db:ref_count(Ref, RefDb, 0) of + 0 -> + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of + [{Content, Ref}] -> + ssl_certificate_db:insert(MD5, Content, PemCache); + undefined -> + ok + end, + ssl_certificate_db:remove(Ref, RefDb), + ssl_certificate_db:remove_trusted_certs(Ref, CertDb); + _ -> + ok + end, {noreply, State}; -handle_info({remove_trusted_certs, Pid}, - #state{certificate_db = Db} = State) -> - ssl_certificate_db:remove_trusted_certs(Pid, Db), + +handle_info({'EXIT', _, _}, State) -> + %% Session validator died!! Do we need to take any action? + %% maybe error log {noreply, State}; handle_info(_Info, State) -> @@ -406,19 +412,6 @@ session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. -cache_pem_file(File, LastWrite, DbHandle) -> - case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of - [{_, {Mtime, Content}}] -> - case LastWrite of - Mtime -> - {ok, Content}; - _ -> - call({recache_pem, File, LastWrite}) - end; - [] -> - call({cache_pem, File, LastWrite}) - end. - delay_time() -> case application:get_env(ssl, session_delay_cleanup_time) of {ok, Time} when is_integer(Time) -> @@ -448,3 +441,28 @@ last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> {LastServer, TRef}; last_delay_timer({_,_}, TRef, {_, LastClient}) -> {TRef, LastClient}. + +%% If we can not generate a not allready in use session ID in +%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The +%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which +%% states : "If we can not find a session id in +%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone +%% is trying to open roughly very close to 2^128 (or 2^256) SSL +%% sessions to our server" +new_id(_, 0, _, _) -> + <<>>; +new_id(Port, Tries, Cache, CacheCb) -> + Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), + case CacheCb:lookup(Cache, {Port, Id}) of + undefined -> + Now = calendar:datetime_to_gregorian_seconds({date(), time()}), + %% New sessions can not be set to resumable + %% until handshake is compleate and the + %% other session values are set. + CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, + is_resumable = false, + time_stamp = Now}), + Id; + _ -> + new_id(Port, Tries - 1, Cache, CacheCb) + end. |