From eaa2564532d6ac87fda2aa9a1d6bce0ac9d35829 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 20 Apr 2012 13:03:52 +0200 Subject: ssl: Move ets:select bottleneck in server Only use ssl_manager for selecting new ids to guarantee uniqueness, but reuse check does not need to be performed by the manager. --- lib/ssl/src/ssl_handshake.erl | 23 ++++++------ lib/ssl/src/ssl_manager.erl | 53 ++++++++++++++++++-------- lib/ssl/src/ssl_session.erl | 87 ++++++++++++++++--------------------------- 3 files changed, 80 insertions(+), 83 deletions(-) (limited to 'lib/ssl') diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index baeceb9bba..06d45966c1 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -70,7 +70,7 @@ client_hello(Host, Port, ConnectionStates, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), - Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), + Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), #client_hello{session_id = Id, client_version = Version, @@ -587,24 +587,23 @@ path_validation_alert({bad_cert, unknown_ca}) -> path_validation_alert(_) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). -select_session(Hello, Port, Session, Version, +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, Cert), - - Suites = available_suites(Cert, UserSuites, Version), - case ssl_session:is_new(SuggestedSessionId, SessionId) of - true -> - CipherSuite = - select_cipher_suite(Hello#client_hello.cipher_suites, Suites), + {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId, + SslOpts, Cert, + Cache, CacheCb), + Suites = available_suites(Cert, UserSuites, Version), + case Resumed of + undefined -> + CipherSuite = select_cipher_suite(Hello#client_hello.cipher_suites, Suites), Compressions = Hello#client_hello.compression_methods, Compression = select_compression(Compressions), {new, Session#session{session_id = SessionId, cipher_suite = CipherSuite, compression_method = Compression}}; - false -> - {resumed, CacheCb:lookup(Cache, {Port, SessionId})} + _ -> + {resumed, Resumed} end. available_suites(UserSuites, Version) -> diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 6d0d010e10..7ee8f6e9d6 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -30,7 +30,7 @@ -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, lookup_trusted_cert/4, - server_session_id/4, + new_session_id/1, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -56,6 +56,7 @@ -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(CLEAN_SESSION_DB, 60000). @@ -95,12 +96,10 @@ connection_init(Trustedcerts, Role) -> %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - try file:read_file_info(File) of + case file:read_file_info(File) of {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite, DbHandle) - catch - _:Reason -> - {error, Reason} + cache_pem_file(File, LastWrite, DbHandle); + Error -> Error end. %%-------------------------------------------------------------------- -spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> @@ -114,13 +113,12 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec server_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert()) -> session_id(). +-spec new_session_id(integer()) -> session_id(). %% -%% Description: Select a session id for the server. +%% Description: Creates a session id for the server. %%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). +new_session_id(Port) -> + call({new_session_id, Port}). %%-------------------------------------------------------------------- -spec register_session(inet:port_number(), #session{}) -> ok. @@ -206,12 +204,10 @@ handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, end, {reply, Result, 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}, _, @@ -433,3 +429,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. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index b10263a5f2..2ad422fc03 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -28,9 +28,9 @@ -include("ssl_internal.hrl"). %% Internal application API --export([is_new/2, id/4, id/7, valid_session/2]). +-export([is_new/2, client_id/4, server_id/6, valid_session/2]). --define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-define('24H_in_sec', 8640). -type seconds() :: integer(). @@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), +-spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% -%% Description: Should be called by the client side to get an id +%% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -id(ClientInfo, Cache, CacheCb, OwnCert) -> +client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; @@ -62,27 +62,6 @@ id(ClientInfo, Cache, CacheCb, OwnCert) -> SessionId end. -%%-------------------------------------------------------------------- --spec id(inet:port_number(), binary(), #ssl_options{}, db_handle(), - 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, _, _) -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb); - -id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, - reuse_session = ReuseFun}, - Cache, CacheCb, SecondLifeTime, OwnCert) -> - case is_resumable(SuggestedSessionId, Port, ReuseEnabled, - ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of - true -> - SuggestedSessionId; - false -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) - end. -%%-------------------------------------------------------------------- -spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired @@ -91,6 +70,25 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> Now = calendar:datetime_to_gregorian_seconds({date(), time()}), Now - TimeStamp < LifeTime. +server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> + {ssl_manager:new_session_id(Port), undefined}; +server_id(Port, SuggestedId, + #ssl_options{reuse_sessions = ReuseEnabled, + reuse_session = ReuseFun}, + Cert, Cache, CacheCb) -> + LifeTime = case application:get_env(ssl, session_lifetime) of + {ok, Time} when is_integer(Time) -> Time; + _ -> ?'24H_in_sec' + end, + case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun, + Cache, CacheCb, LifeTime, Cert) + of + {true, Resumed} -> + {SuggestedId, Resumed}; + {false, undefined} -> + {ssl_manager:new_session_id(Port), undefined} + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -114,33 +112,8 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> [[Id, _]|_] -> Id end. -%% 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. - is_resumable(_, _, false, _, _, _, _, _) -> - false; + {false, undefined}; is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of @@ -149,13 +122,17 @@ is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, compression_method = Compression, is_resumable = IsResumable, peer_certificate = PeerCert} = Session -> - resumable(IsResumable) + case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) andalso ReuseFun(SuggestedSessionId, PeerCert, - Compression, CipherSuite); + Compression, CipherSuite) + of + true -> {true, Session}; + false -> {false, undefined} + end; undefined -> - false + {false, undefined} end. resumable(new) -> -- cgit v1.2.3