aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
authorIngela Anderton Andin <[email protected]>2010-03-25 08:24:48 +0000
committerErlang/OTP <[email protected]>2010-03-25 08:24:48 +0000
commit6ec62f7ca98bcc674b806b39d73ded6f0b9a772d (patch)
treeea7a23ca2646172d1c0961cb577a11fab11051c3 /lib/ssl/src
parent985d201454d0cb43d5ed3230d6afeaeea0a1fe2c (diff)
downloadotp-6ec62f7ca98bcc674b806b39d73ded6f0b9a772d.tar.gz
otp-6ec62f7ca98bcc674b806b39d73ded6f0b9a772d.tar.bz2
otp-6ec62f7ca98bcc674b806b39d73ded6f0b9a772d.zip
OTP-8517 Renegotiation
New ssl now properly handles ssl renegotiation, and initiates a renegotiation if ssl/ltls-sequence numbers comes close to the max value.
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/ssl.erl19
-rw-r--r--lib/ssl/src/ssl_connection.erl314
-rw-r--r--lib/ssl/src/ssl_handshake.erl13
-rw-r--r--lib/ssl/src/ssl_internal.hrl11
-rw-r--r--lib/ssl/src/ssl_record.erl64
-rw-r--r--lib/ssl/src/ssl_record.hrl17
6 files changed, 294 insertions, 144 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index de74c91505..cb678e3f53 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -29,13 +29,15 @@
connect/3, connect/2, connect/4, connection_info/1,
controlling_process/2, listen/2, pid/1, peername/1, recv/2, recv/3,
send/2, getopts/2, setopts/2, seed/1, sockname/1, peercert/1,
- peercert/2, version/0, versions/0, session_info/1, format_error/1]).
+ peercert/2, version/0, versions/0, session_info/1, format_error/1,
+ renegotiate/1]).
%% Should be deprecated as soon as old ssl is removed
%%-deprecated({pid, 1, next_major_release}).
-include("ssl_int.hrl").
-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -436,6 +438,10 @@ versions() ->
AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+
+renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:renegotiation(Pid).
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -547,6 +553,7 @@ handle_options(Opts0, Role) ->
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
+ renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, [])
},
@@ -555,7 +562,7 @@ handle_options(Opts0, Role) ->
depth, certfile, keyfile,
key, password, cacertfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cd_info],
+ cd_info, renegotiate_at],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -617,6 +624,9 @@ validate_option(reuse_session, Value) when is_function(Value) ->
validate_option(reuse_sessions, Value) when Value == true;
Value == false ->
Value;
+validate_option(renegotiate_at, Value) when is_integer(Value) ->
+ min(Value, ?DEFAULT_RENEGOTIATE_AT);
+
validate_option(debug, Value) when is_list(Value); Value == true ->
Value;
validate_option(Opt, Value) ->
@@ -832,6 +842,11 @@ version() ->
Vsns
end,
{ok, {SSLVsn, CompVsn, LibVsn}}.
+
+min(N,M) when N < M ->
+ N;
+min(_, M) ->
+ M.
%% Only used to remove exit messages from old ssl
%% First is a nonsense clause to provide some
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 4847fd278d..3ede21d377 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -41,14 +41,13 @@
%% Internal application API
-export([send/2, send/3, recv/3, connect/7, accept/6, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1,
- sockname/1, peername/1]).
+ peer_certificate/1, sockname/1, peername/1, renegotiation/1]).
%% Called by ssl_connection_sup
-export([start_link/7]).
%% gen_fsm callbacks
--export([init/1, hello/2, certify/2, cipher/2, connection/2, connection/3, abbreviated/2,
+-export([init/1, hello/2, certify/2, cipher/2, connection/2, abbreviated/2,
handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
@@ -86,7 +85,10 @@
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
%% tls_buffer, % Keeps a lookahead one packet if available
- log_alert % boolan()
+ log_alert, % boolean()
+ renegotiation, % boolean()
+ recv_during_renegotiation, %boolean()
+ send_queue % queue()
}).
%%====================================================================
@@ -99,15 +101,15 @@
%% Description:
%%--------------------------------------------------------------------
send(Pid, Data) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
send(Pid, Data, Timeout) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
%%--------------------------------------------------------------------
%% Function:
%%
%% Description:
%%--------------------------------------------------------------------
-recv(Pid, Length, Timeout) -> % TODO: Prio with renegotiate?
+recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length}, Timeout).
%%--------------------------------------------------------------------
%% Function:
@@ -209,6 +211,14 @@ session_info(ConnectionPid) ->
peer_certificate(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peer_certificate).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+renegotiation(ConnectionPid) ->
+ send_all_state_event(ConnectionPid, renegotiate).
+
%%====================================================================
%% ssl_connection_sup API
%%====================================================================
@@ -289,7 +299,7 @@ hello(socket_control, #state{host = Host, port = Port, role = client,
hello(socket_control, #state{role = server} = State) ->
{next_state, hello, next_record(State)};
-hello(hello, #state{role = client} = State) ->
+hello(#hello_request{}, #state{role = client} = State) ->
{next_state, hello, State};
hello(#server_hello{cipher_suite = CipherSuite,
@@ -358,21 +368,20 @@ hello(Hello = #client_hello{client_version = ClientVersion},
abbreviated(socket_control, #state{role = server} = State) ->
{next_state, abbreviated, State};
-abbreviated(hello, State) ->
+abbreviated(#hello_request{}, State) ->
{next_state, certify, State};
abbreviated(Finished = #finished{},
#state{role = server,
negotiated_version = Version,
tls_handshake_hashes = Hashes,
- session = #session{master_secret = MasterSecret},
- from = From} = State) ->
+ session = #session{master_secret = MasterSecret}} = State) ->
case ssl_handshake:verify_connection(Version, Finished, client,
MasterSecret, Hashes) of
verified ->
- gen_fsm:reply(From, connected),
- {next_state, connection, next_record_if_active(State)};
- #alert{} = Alert ->
+ ack_connection(State),
+ next_state_connection(State);
+ #alert{} = Alert ->
handle_own_alert(Alert, Version, abbreviated, State),
{stop, normal, State}
end;
@@ -380,17 +389,15 @@ abbreviated(Finished = #finished{},
abbreviated(Finished = #finished{},
#state{role = client, tls_handshake_hashes = Hashes0,
session = #session{master_secret = MasterSecret},
- from = From,
negotiated_version = Version} = State) ->
case ssl_handshake:verify_connection(Version, Finished, server,
MasterSecret, Hashes0) of
verified ->
{ConnectionStates, Hashes} = finalize_client_handshake(State),
- gen_fsm:reply(From, connected),
- {next_state, connection,
- next_record_if_active(State#state{tls_handshake_hashes = Hashes,
- connection_states =
- ConnectionStates})};
+ ack_connection(State),
+ next_state_connection(State#state{tls_handshake_hashes = Hashes,
+ connection_states =
+ ConnectionStates});
#alert{} = Alert ->
handle_own_alert(Alert, Version, abbreviated, State),
{stop, normal, State}
@@ -398,7 +405,7 @@ abbreviated(Finished = #finished{},
certify(socket_control, #state{role = server} = State) ->
{next_state, certify, State};
-certify(hello, State) ->
+certify(#hello_request{}, State) ->
{next_state, certify, State};
certify(#certificate{asn1_certificates = []},
@@ -427,7 +434,7 @@ certify(#certificate{} = Cert,
Opts#ssl_options.verify_fun) of
{PeerCert, PublicKeyInfo} ->
State = State0#state{session =
- Session#session{peer_certificate = PeerCert},
+ Session#session{peer_certificate = PeerCert},
public_key_info = PublicKeyInfo,
client_certificate_requested = false
},
@@ -521,7 +528,7 @@ certify(#client_key_exchange{exchange_keys
cipher(socket_control, #state{role = server} = State) ->
{next_state, cipher, State};
-cipher(hello, State) ->
+cipher(#hello_request{}, State) ->
{next_state, cipher, State};
cipher(#certificate_verify{signature = Signature},
@@ -543,8 +550,7 @@ cipher(#certificate_verify{signature = Signature},
end;
cipher(#finished{} = Finished,
- State = #state{from = From,
- negotiated_version = Version,
+ State = #state{negotiated_version = Version,
host = Host,
port = Port,
role = Role,
@@ -556,12 +562,11 @@ cipher(#finished{} = Finished,
opposite_role(Role),
MasterSecret, Hashes) of
verified ->
- gen_fsm:reply(From, connected),
+ ack_connection(State),
Session = register_session(Role, Host, Port, Session0),
case Role of
client ->
- {next_state, connection,
- next_record_if_active(State#state{session = Session})};
+ next_state_connection(State#state{session = Session});
server ->
{NewConnectionStates, NewHashes} =
finalize_server_handshake(State#state{
@@ -570,7 +575,7 @@ cipher(#finished{} = Finished,
State#state{connection_states = NewConnectionStates,
session = Session,
tls_handshake_hashes = NewHashes},
- {next_state, connection, next_record_if_active(NewState)}
+ next_state_connection(NewState)
end;
#alert{} = Alert ->
handle_own_alert(Alert, Version, cipher, State),
@@ -579,7 +584,7 @@ cipher(#finished{} = Finished,
connection(socket_control, #state{role = server} = State) ->
{next_state, connection, State};
-connection(hello, State = #state{host = Host, port = Port,
+connection(#hello_request{}, State = #state{host = Host, port = Port,
socket = Socket,
ssl_options = SslOpts,
negotiated_version = Version,
@@ -592,42 +597,12 @@ connection(hello, State = #state{host = Host, port = Port,
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
- {next_state, hello, State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1}}.
-
-%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}|
-%% {stop, Reason, Reply, NewState}
-%% Description: There should be one instance of this function for each
-%% possible state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:sync_send_event/2,3, the instance of this function with the same
-%% name as the current state name StateName is called to handle the event.
-%%--------------------------------------------------------------------
-connection({application_data, Data0}, _From,
- State = #state{socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0}) ->
- %% We should look into having a worker process to do this to
- %% parallize send and receive decoding and not block the receiver
- %% if sending is overloading the socket.
- try
- Data = encode_packet(Data0, State#state.socket_options),
- {Msgs, ConnectionStates1} = encode_data(Data, Version, ConnectionStates0),
- Result = Transport:send(Socket, Msgs),
- {reply, Result,
- connection, State#state{connection_states = ConnectionStates1}}
-
- catch throw:Error ->
- {reply, Error, connection, State}
- end.
+ {next_state, hello, next_record(State#state{connection_states =
+ ConnectionStates1,
+ tls_handshake_hashes = Hashes1,
+ renegotiation = true})};
+connection(#client_hello{} = Hello, #state{role = server} = State) ->
+ hello(Hello, State).
%%--------------------------------------------------------------------
%% Function:
@@ -642,22 +617,36 @@ connection({application_data, Data0}, _From,
%%--------------------------------------------------------------------
handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
StateName,
- State = #state{key_algorithm = KeyAlg,
- tls_handshake_buffer = Buf0,
- negotiated_version = Version}) ->
+ State0 = #state{key_algorithm = KeyAlg,
+ tls_handshake_buffer = Buf0,
+ negotiated_version = Version}) ->
Handle =
- fun({Packet, Raw}, {next_state, SName, AS=#state{tls_handshake_hashes=Hs0}}) ->
+ fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_hashes(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0});
+ ({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. If allready in negotiation it will be ignored!
+ ?MODULE:SName(Packet, State);
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ Hs0 = ssl_handshake:init_hashes(),
+ Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ renegotiation = true});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, AS#state{tls_handshake_hashes=Hs1});
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
(_, StopState) -> StopState
end,
try
{Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
- Start = {next_state, StateName, State#state{tls_handshake_buffer = Buf}},
+ Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}},
lists:foldl(Handle, Start, Packets)
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, StateName, State0),
+ {stop, normal, State0}
end;
handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data},
@@ -717,8 +706,13 @@ handle_event(#alert{level = ?WARNING} = Alert, StateName,
log_alert(Log, StateName, Alert),
%%TODO: Could be user_canceled or no_negotiation should the latter be
%% treated as fatal?!
- {next_state, StateName, next_record(State)}.
+ {next_state, StateName, next_record(State)};
+handle_event(renegotiate, connection, State) ->
+ renegotiate(State);
+handle_event(renegotiate, StateName, State) ->
+ %% Already in renegotiate ignore
+ {next_state, StateName, State}.
%%--------------------------------------------------------------------
%% Function:
%% handle_sync_event(Event, From, StateName,
@@ -734,6 +728,42 @@ handle_event(#alert{level = ?WARNING} = Alert, StateName,
%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
%% the event.
%%--------------------------------------------------------------------
+handle_sync_event({application_data, Data0}, From, connection,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ send_queue = SendQueue,
+ socket_options = SockOpts,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}}
+ = State) ->
+ %% We should look into having a worker process to do this to
+ %% parallize send and receive decoding and not block the receiver
+ %% if sending is overloading the socket.
+ try
+ Data = encode_packet(Data0, SockOpts),
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ {reply, Result,
+ connection, State#state{connection_states = ConnectionStates}};
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData}, SendQueue)})
+ end
+ catch throw:Error ->
+ {reply, Error, connection, State}
+ end;
+handle_sync_event({application_data, Data}, From, StateName,
+ #state{send_queue = Queue} = State) ->
+ %% In renegotiation priorities handshake, send data when handshake is finished
+ {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}};
handle_sync_event(started, From, StateName, State) ->
{next_state, StateName, State#state{from = From}};
@@ -750,23 +780,14 @@ handle_sync_event({shutdown, How}, From, StateName,
{stop, normal, Error, State#state{from = From}}
end;
-%% TODO: men vad g�r next_record om det �r t.ex. renegotiate? kanske
-%% inte bra... t�l att t�nkas p�!
-handle_sync_event({recv, N}, From, StateName,
- State0 = #state{user_data_buffer = Buffer}) ->
- State1 = State0#state{bytes_to_read = N, from = From},
- case Buffer of
- <<>> ->
- State = next_record(State1),
- {next_state, StateName, State};
- _ ->
- case application_data(<<>>, State1) of
- Stop = {stop, _, _} ->
- Stop;
- State ->
- {next_state, StateName, State}
- end
- end;
+handle_sync_event({recv, N}, From, connection = StateName, State0) ->
+ passive_receive(State0#state{bytes_to_read = N, from = From}, StateName);
+
+%% Doing renegotiate wait with handling request until renegotiate is
+%% finished. Will be handled by next_state_connection/1.
+handle_sync_event({recv, N}, From, StateName, State) ->
+ {next_state, StateName, State#state{bytes_to_read = N, from = From,
+ recv_during_renegotiation = true}};
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
@@ -834,7 +855,6 @@ handle_sync_event(peer_certificate, _, StateName,
= State) ->
{reply, {ok, Cert}, StateName, State}.
-
%%--------------------------------------------------------------------
%% Function:
%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
@@ -1042,20 +1062,6 @@ init_private_key(PrivateKey, _, _,_) ->
send_event(FsmPid, Event) ->
gen_fsm:send_event(FsmPid, Event).
-sync_send_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_event(FsmPid, Event, Timeout) of
- Reply ->
- Reply
- catch
- exit:{noproc, _} ->
- {error, closed};
- exit:{timeout, _} ->
- {error, timeout};
- exit:{normal, _} ->
- {error, closed}
- end.
-
-
send_all_state_event(FsmPid, Event) ->
gen_fsm:send_all_state_event(FsmPid, Event).
@@ -1442,8 +1448,8 @@ encode_size_packet(Bin, Size, Max) ->
false -> <<Len:Size, Bin/binary>>
end.
-encode_data(Data, Version, ConnectionStates) ->
- ssl_record:encode_data(Data, Version, ConnectionStates).
+encode_data(Data, Version, ConnectionStates, RenegotiateAt) ->
+ ssl_record:encode_data(Data, Version, ConnectionStates, RenegotiateAt).
decode_alerts(Bin) ->
decode_alerts(Bin, []).
@@ -1454,6 +1460,20 @@ decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
decode_alerts(<<>>, Acc) ->
lists:reverse(Acc, []).
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ State = next_record(State0),
+ {next_state, StateName, State};
+ _ ->
+ case application_data(<<>>, State0) of
+ Stop = {stop, _, _} ->
+ Stop;
+ State ->
+ {next_state, StateName, State}
+ end
+ end.
+
application_data(Data, #state{user_application = {_Mon, Pid},
socket_options = SOpts,
bytes_to_read = BytesToRead,
@@ -1594,13 +1614,56 @@ next_record(#state{tls_cipher_texts = [CT | Rest],
gen_fsm:send_all_state_event(self(), Plain),
State#state{tls_cipher_texts = Rest, connection_states = ConnStates}.
+
next_record_if_active(State =
#state{socket_options =
#socket_options{active = false}}) ->
State;
+
next_record_if_active(State) ->
next_record(State).
+next_state_connection(#state{send_queue = Queue0,
+ negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}
+ } = State) ->
+ %% Send queued up data
+ case queue:out(Queue0) of
+ {{value, {From, Data}}, Queue} ->
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ gen_fsm:reply(From, Result),
+ next_state_connection(State#state{connection_states = ConnectionStates,
+ send_queue = Queue});
+ %% This is unlikely to happen. User configuration of the
+ %% undocumented test option renegotiation_at can make it more likely.
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData})})
+ end;
+ {empty, Queue0} ->
+ next_state_is_connection(State)
+ end.
+
+next_state_is_connection(State =
+ #state{recv_during_renegotiation = true, socket_options =
+ #socket_options{active = false}}) ->
+ passive_receive(State#state{recv_during_renegotiation = false}, connection);
+
+next_state_is_connection(State) ->
+ {next_state, connection, next_record_if_active(State)}.
+
+
register_session(_, _, _, #session{is_resumable = true} = Session) ->
Session; %% Already registered
register_session(client, Host, Port, Session0) ->
@@ -1650,7 +1713,10 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
bytes_to_read = 0,
user_data_buffer = <<>>,
log_alert = true,
- session_cache_cb = SessionCacheCb
+ session_cache_cb = SessionCacheCb,
+ renegotiation = false,
+ recv_during_renegotiation = false,
+ send_queue = queue:new()
}.
sslsocket(Pid) ->
@@ -1747,3 +1813,31 @@ handle_own_alert(Alert, Version, StateName,
make_premaster_secret({MajVer, MinVer}) ->
Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>.
+
+
+ack_connection(#state{renegotiation = true}) ->
+ ok;
+ack_connection(#state{renegotiation = false, from = From}) ->
+ gen_fsm:reply(From, connected).
+
+
+renegotiate(#state{role = client} = State) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_hashes(),
+ connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+renegotiate(#state{role = server,
+ socket = Socket,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined),
+ Hs0 = ssl_handshake:init_hashes(),
+ {BinMsg, ConnectionStates} =
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ {next_state, hello, next_record(State#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hs0,
+ renegotiation = true})}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 8c598135ca..4e1c495fda 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,8 +31,8 @@
-include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/4, server_hello/3, hello/2,
- certify/5, certificate/3,
+-export([master_secret/4, client_hello/4, server_hello/3, hello/2,
+ hello_request/0, certify/5, certificate/3,
client_certificate_verify/6,
certificate_verify/6, certificate_request/2,
key_exchange/2, finished/4,
@@ -97,6 +97,15 @@ server_hello(SessionId, Version, ConnectionStates) ->
}.
%%--------------------------------------------------------------------
+%% Function: hello_request() -> #hello_request{}
+%%
+%% Description: Creates a hello request message sent by server to
+%% trigger renegotiation.
+%%--------------------------------------------------------------------
+hello_request() ->
+ #hello_request{}.
+
+%%--------------------------------------------------------------------
%% Function: hello(Hello, Info) ->
%% {Version, Id, NewConnectionStates} |
%% #alert{}
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 23a5c93452..edb8d9741d 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. 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%
%%
@@ -71,6 +71,7 @@
%% If false sessions will never be reused, if true they
%% will be reused if possible.
reuse_sessions, % boolean()
+ renegotiate_at,
debug %
}).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 37a3d1b639..da48f049f6 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. 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%
%%
@@ -45,7 +45,7 @@
%% Encoding records
-export([encode_handshake/3, encode_alert_record/3,
- encode_change_cipher_spec/2, encode_data/3]).
+ encode_change_cipher_spec/2, encode_data/4]).
%% Decoding
-export([decode_cipher_text/2]).
@@ -474,19 +474,31 @@ split_bin(Bin, ChunkSize, Acc) ->
lists:reverse(Acc, [Bin])
end.
-encode_data(Frag, Version, ConnectionStates)
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt)
when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
- encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates);
-encode_data(Frag, Version, ConnectionStates) ->
+ case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of
+ {renegotiate, Data} ->
+ {[], Data, ConnectionStates};
+ {Msg, CS} ->
+ {Msg, [], CS}
+ end;
+
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) ->
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
- {CS1, Acc} =
- lists:foldl(fun(B, {CS0, Acc}) ->
- {ET, CS1} =
- encode_plain_text(?APPLICATION_DATA,
- Version, B, CS0),
- {CS1, [ET | Acc]}
- end, {ConnectionStates, []}, Data),
- {lists:reverse(Acc), CS1}.
+ encode_data(Data, Version, ConnectionStates, RenegotiateAt);
+
+encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) ->
+ {ConnectionStates, EncodedMsg, NotEncdedData} =
+ lists:foldl(fun(B, {CS0, Encoded, Rest}) ->
+ case encode_plain_text(?APPLICATION_DATA,
+ Version, B, CS0, RenegotiateAt) of
+ {renegotiate, NotEnc} ->
+ {CS0, Encoded, [NotEnc | Rest]};
+ {Enc, CS1} ->
+ {CS1, [Enc | Encoded], Rest}
+ end
+ end, {ConnectionStates0, [], []}, Data),
+ {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}.
encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
@@ -499,6 +511,16 @@ encode_alert_record(#alert{level = Level, description = Description},
encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
+encode_plain_text(Type, Version, Data, ConnectionStates, RenegotiateAt) ->
+ #connection_states{current_write =
+ #connection_state{sequence_number = Num}} = ConnectionStates,
+ case renegotiate(Num, RenegotiateAt) of
+ false ->
+ encode_plain_text(Type, Version, Data, ConnectionStates);
+ true ->
+ {renegotiate, Data}
+ end.
+
encode_plain_text(Type, Version, Data, ConnectionStates) ->
#connection_states{current_write=#connection_state{
compression_state=CompS0,
@@ -511,6 +533,11 @@ encode_plain_text(Type, Version, Data, ConnectionStates) ->
CTBin = encode_tls_cipher_text(Type, Version, CipherText),
{CTBin, ConnectionStates#connection_states{current_write = CS2}}.
+renegotiate(N, M) when N < M->
+ false;
+renegotiate(_,_) ->
+ true.
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
@@ -529,9 +556,6 @@ cipher(Type, Version, Fragment, CS0) ->
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
-decipher(TLS=#ssl_tls{type = ?CHANGE_CIPHER_SPEC}, CS) ->
- %% These are never encrypted
- {TLS, CS};
decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
SP = CS0#connection_state.security_parameters,
BCA = SP#security_parameters.bulk_cipher_algorithm, % eller Cipher?
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 7370e0f0b3..362b7039d4 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. 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%
%%
@@ -63,6 +63,13 @@
sequence_number
}).
+-define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
+%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
+%% We will renegotiate a little before so that there will be sequence numbers left
+%% for the rehandshake and a little data.
+-define(MARGIN, 100).
+-define(DEFAULT_RENEGOTIATE_AT, ?MAX_SEQENCE_NUMBER - ?MARGIN).
+
%% ConnectionEnd
-define(SERVER, 0).
-define(CLIENT, 1).