CVE-2016-1232: Add Dialback Key Generation and Validation support (XEP-0185)

This commit is contained in:
Evgeniy Khramtsov 2016-01-11 14:22:17 +03:00
parent 15245e9ec4
commit c7931b4a4f
3 changed files with 44 additions and 55 deletions

View File

@ -35,8 +35,8 @@
%% API
-export([start_link/0, route/3, have_connection/1,
has_key/2, get_connections_pids/1, try_register/1,
remove_connection/3, find_connection/2,
make_key/2, get_connections_pids/1, try_register/1,
remove_connection/2, find_connection/2,
dirty_get_connections/0, allow_host/2,
incoming_s2s_number/0, outgoing_s2s_number/0,
clean_temporarily_blocked_table/0,
@ -75,8 +75,7 @@
%% once a server is temporarly blocked, it stay blocked for 60 seconds
-record(s2s, {fromto = {<<"">>, <<"">>} :: {binary(), binary()} | '_',
pid = self() :: pid() | '_' | '$1',
key = <<"">> :: binary() | '_'}).
pid = self() :: pid() | '_' | '$1'}).
-record(state, {}).
@ -134,19 +133,15 @@ is_temporarly_blocked(Host) ->
end.
-spec remove_connection({binary(), binary()},
pid(), binary()) -> {atomic, ok} |
ok |
{aborted, any()}.
pid()) -> {atomic, ok} | ok | {aborted, any()}.
remove_connection(FromTo, Pid, Key) ->
remove_connection(FromTo, Pid) ->
case catch mnesia:dirty_match_object(s2s,
#s2s{fromto = FromTo, pid = Pid,
_ = '_'})
#s2s{fromto = FromTo, pid = Pid})
of
[#s2s{pid = Pid, key = Key}] ->
[#s2s{pid = Pid}] ->
F = fun () ->
mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid,
key = Key})
mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
end,
mnesia:transaction(F);
_ -> ok
@ -162,19 +157,6 @@ have_connection(FromTo) ->
false
end.
-spec has_key({binary(), binary()}, binary()) -> boolean().
has_key(FromTo, Key) ->
case mnesia:dirty_select(s2s,
[{#s2s{fromto = FromTo, key = Key, _ = '_'},
[],
['$_']}]) of
[] ->
false;
_ ->
true
end.
-spec get_connections_pids({binary(), binary()}) -> [pid()].
get_connections_pids(FromTo) ->
@ -185,10 +167,9 @@ get_connections_pids(FromTo) ->
[]
end.
-spec try_register({binary(), binary()}) -> {key, binary()} | false.
-spec try_register({binary(), binary()}) -> boolean().
try_register(FromTo) ->
Key = randoms:get_string(),
MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo),
MaxS2SConnectionsNumberPerNode =
max_s2s_connections_number_per_node(FromTo),
@ -198,9 +179,8 @@ try_register(FromTo) ->
MaxS2SConnectionsNumber,
MaxS2SConnectionsNumberPerNode),
if NeededConnections > 0 ->
mnesia:write(#s2s{fromto = FromTo, pid = self(),
key = Key}),
{key, Key};
mnesia:write(#s2s{fromto = FromTo, pid = self()}),
true;
true -> false
end
end,
@ -241,6 +221,12 @@ check_peer_certificate(SockMod, Sock, Peer) ->
{error, <<"Cannot get peer certificate">>}
end.
make_key({From, To}, StreamID) ->
Secret = ejabberd_config:get_option(shared_key, fun(V) -> V end),
p1_sha:to_hexlist(
crypto:hmac(sha256, p1_sha:to_hexlist(crypto:hash(sha256, Secret)),
[To, " ", From, " ", StreamID])).
%%====================================================================
%% gen_server callbacks
%%====================================================================
@ -407,17 +393,15 @@ open_several_connections(N, MyServer, Server, From,
new_connection(MyServer, Server, From, FromTo,
MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) ->
Key = randoms:get_string(),
{ok, Pid} = ejabberd_s2s_out:start(
MyServer, Server, {new, Key}),
MyServer, Server, new),
F = fun() ->
L = mnesia:read({s2s, FromTo}),
NeededConnections = needed_connections_number(L,
MaxS2SConnectionsNumber,
MaxS2SConnectionsNumberPerNode),
if NeededConnections > 0 ->
mnesia:write(#s2s{fromto = FromTo, pid = Pid,
key = Key}),
mnesia:write(#s2s{fromto = FromTo, pid = Pid}),
?INFO_MSG("New s2s connection started ~p", [Pid]),
Pid;
true -> choose_connection(From, L)
@ -520,9 +504,12 @@ update_tables() ->
end,
case catch mnesia:table_info(s2s, attributes) of
[fromto, node, key] ->
mnesia:transform_table(s2s, ignore, [fromto, pid, key]),
mnesia:transform_table(s2s, ignore, [fromto, pid]),
mnesia:clear_table(s2s);
[fromto, pid, key] -> ok;
[fromto, pid, key] ->
mnesia:transform_table(s2s, ignore, [fromto, pid]),
mnesia:clear_table(s2s);
[fromto, pid] -> ok;
{'EXIT', _} -> ok
end,
case lists:member(local_s2s, mnesia:system_info(tables)) of

View File

@ -432,9 +432,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
?DEBUG("VERIFY KEY: ~p", [{To, From, Id, Key}]),
LTo = jid:nameprep(To),
LFrom = jid:nameprep(From),
Type = case ejabberd_s2s:has_key({LTo, LFrom}, Key) of
true -> <<"valid">>;
_ -> <<"invalid">>
Type = case ejabberd_s2s:make_key({LTo, LFrom}, Id) of
Key -> <<"valid">>;
_ -> <<"invalid">>
end,
send_element(StateData,
#xmlel{name = <<"db:verify">>,

View File

@ -56,6 +56,7 @@
-record(state,
{socket :: ejabberd_socket:socket_state(),
streamid = <<"">> :: binary(),
remote_streamid = <<"">> :: binary(),
use_v10 = true :: boolean(),
tls = false :: boolean(),
tls_required = false :: boolean(),
@ -69,7 +70,7 @@
server = <<"">> :: binary(),
queue = queue:new() :: ?TQUEUE,
delay_to_retry = undefined_delay :: undefined_delay | non_neg_integer(),
new = false :: false | binary(),
new = false :: boolean(),
verify = false :: false | {pid(), binary(), binary()},
bridge :: {atom(), atom()},
timer = make_ref() :: reference()}).
@ -196,7 +197,7 @@ init([From, Server, Type]) ->
true -> TLSOpts4
end,
{New, Verify} = case Type of
{new, Key} -> {Key, false};
new -> {true, false};
{verify, Pid, Key, SID} ->
start_connection(self()), {false, {Pid, Key, SID}}
end,
@ -310,7 +311,7 @@ open_socket2(Type, Addr, Port) ->
wait_for_stream({xmlstreamstart, _Name, Attrs},
StateData) ->
{CertCheckRes, CertCheckMsg, NewStateData} =
{CertCheckRes, CertCheckMsg, StateData0} =
if StateData#state.tls_certverify, StateData#state.tls_enabled ->
{Res, Msg} =
ejabberd_s2s:check_peer_certificate(ejabberd_socket,
@ -322,6 +323,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
true ->
{no_verify, <<"Not verified">>, StateData}
end,
RemoteStreamID = xml:get_attr_s(<<"id">>, Attrs),
NewStateData = StateData0#state{remote_streamid = RemoteStreamID},
case {xml:get_attr_s(<<"xmlns">>, Attrs),
xml:get_attr_s(<<"xmlns:db">>, Attrs),
xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
@ -958,10 +961,10 @@ terminate(Reason, StateName, StateData) ->
?DEBUG("terminated: ~p", [{Reason, StateName}]),
case StateData#state.new of
false -> ok;
Key ->
true ->
ejabberd_s2s:remove_connection({StateData#state.myname,
StateData#state.server},
self(), Key)
self())
end,
bounce_queue(StateData#state.queue,
?ERR_REMOTE_SERVER_NOT_FOUND),
@ -1029,19 +1032,18 @@ bounce_messages(Error) ->
send_db_request(StateData) ->
Server = StateData#state.server,
New = case StateData#state.new of
false ->
case ejabberd_s2s:try_register({StateData#state.myname,
Server})
of
{key, Key} -> Key;
false -> false
end;
Key -> Key
false ->
ejabberd_s2s:try_register({StateData#state.myname, Server});
true ->
true
end,
NewStateData = StateData#state{new = New},
try case New of
false -> ok;
Key1 ->
false -> ok;
true ->
Key1 = ejabberd_s2s:make_key(
{StateData#state.myname, Server},
StateData#state.remote_streamid),
send_element(StateData,
#xmlel{name = <<"db:result">>,
attrs =