%% %% %CopyrightBegin% %% %% 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 %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %% %%---------------------------------------------------------------------- %% Purpose: Handles ssl sessions %%---------------------------------------------------------------------- -module(ssl_session). -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). %% Internal application API -export([is_new/2, client_id/4, server_id/6, valid_session/2]). -define('24H_in_sec', 8640). -type seconds() :: integer(). %%-------------------------------------------------------------------- -spec is_new(session_id(), session_id()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. %%-------------------------------------------------------------------- is_new(<<>>, _) -> true; is_new(SessionId, SessionId) -> false; is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- -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 %% for the client hello message. %%-------------------------------------------------------------------- client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; SessionId -> SessionId end. -spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- 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 %%-------------------------------------------------------------------- select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), select_session(Sessions, SslOpts, OwnCert). select_session([], _, _) -> no_session; select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> IsNotResumable = fun([_Id, Session]) -> not (resumable(Session#session.is_resumable) andalso lists:member(Session#session.cipher_suite, Ciphers) andalso (OwnCert == Session#session.own_certificate)) end, case lists:dropwhile(IsNotResumable, Sessions) of [] -> no_session; [[Id, _]|_] -> Id end. is_resumable(_, _, false, _, _, _, _, _) -> {false, undefined}; is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, own_certificate = SessionOwnCert, compression_method = Compression, is_resumable = IsResumable, peer_certificate = PeerCert} = Session -> case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) andalso ReuseFun(SuggestedSessionId, PeerCert, Compression, CipherSuite) of true -> {true, Session}; false -> {false, undefined} end; undefined -> {false, undefined} end. resumable(new) -> false; resumable(IsResumable) -> IsResumable.