Sign 'Record-Route' in order to proxy unauthorized ACKs
This commit is contained in:
parent
b75b5ebeb2
commit
f446e7fc0b
|
@ -80,7 +80,9 @@ response(_Resp, _SIPSock) ->
|
||||||
request(#sip{method = <<"ACK">>} = Req, SIPSock) ->
|
request(#sip{method = <<"ACK">>} = Req, SIPSock) ->
|
||||||
case action(Req, SIPSock) of
|
case action(Req, SIPSock) of
|
||||||
{relay, LServer} ->
|
{relay, LServer} ->
|
||||||
mod_sip_proxy:route(Req, LServer, []);
|
mod_sip_proxy:route(Req, LServer, [{authenticated, true}]);
|
||||||
|
{proxy_auth, LServer} ->
|
||||||
|
mod_sip_proxy:route(Req, LServer, [{authenticated, false}]);
|
||||||
_ ->
|
_ ->
|
||||||
error
|
error
|
||||||
end;
|
end;
|
||||||
|
@ -112,20 +114,20 @@ request(Req, SIPSock, TrID, Action) ->
|
||||||
?INFO_MSG("failed to proxy request ~p: ~p", [Req, Err]),
|
?INFO_MSG("failed to proxy request ~p: ~p", [Req, Err]),
|
||||||
Err
|
Err
|
||||||
end;
|
end;
|
||||||
{proxy_auth, Host} ->
|
{proxy_auth, LServer} ->
|
||||||
make_response(
|
make_response(
|
||||||
Req,
|
Req,
|
||||||
#sip{status = 407,
|
#sip{status = 407,
|
||||||
type = response,
|
type = response,
|
||||||
hdrs = [{'proxy-authenticate',
|
hdrs = [{'proxy-authenticate',
|
||||||
make_auth_hdr(Host)}]});
|
make_auth_hdr(LServer)}]});
|
||||||
{auth, Host} ->
|
{auth, LServer} ->
|
||||||
make_response(
|
make_response(
|
||||||
Req,
|
Req,
|
||||||
#sip{status = 401,
|
#sip{status = 401,
|
||||||
type = response,
|
type = response,
|
||||||
hdrs = [{'www-authenticate',
|
hdrs = [{'www-authenticate',
|
||||||
make_auth_hdr(Host)}]});
|
make_auth_hdr(LServer)}]});
|
||||||
deny ->
|
deny ->
|
||||||
make_response(Req, #sip{status = 403,
|
make_response(Req, #sip{status = 403,
|
||||||
type = response});
|
type = response});
|
||||||
|
@ -169,7 +171,7 @@ action(#sip{method = <<"REGISTER">>, type = request, hdrs = Hdrs,
|
||||||
true ->
|
true ->
|
||||||
register;
|
register;
|
||||||
false ->
|
false ->
|
||||||
{auth, ToURI#uri.host}
|
{auth, jlib:nameprep(ToURI#uri.host)}
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
deny
|
deny
|
||||||
|
@ -259,8 +261,7 @@ process(Req, _) ->
|
||||||
hdrs = [{'allow', allow()}]}).
|
hdrs = [{'allow', allow()}]}).
|
||||||
|
|
||||||
make_auth_hdr(LServer) ->
|
make_auth_hdr(LServer) ->
|
||||||
Realm = jlib:nameprep(LServer),
|
{<<"Digest">>, [{<<"realm">>, esip:quote(LServer)},
|
||||||
{<<"Digest">>, [{<<"realm">>, esip:quote(Realm)},
|
|
||||||
{<<"qop">>, esip:quote(<<"auth">>)},
|
{<<"qop">>, esip:quote(<<"auth">>)},
|
||||||
{<<"nonce">>, esip:quote(esip:make_hexstr(20))}]}.
|
{<<"nonce">>, esip:quote(esip:make_hexstr(20))}]}.
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("esip.hrl").
|
-include("esip.hrl").
|
||||||
|
|
||||||
|
-define(SIGN_LIFETIME, 300). %% in seconds.
|
||||||
|
|
||||||
-record(state, {host = <<"">> :: binary(),
|
-record(state, {host = <<"">> :: binary(),
|
||||||
opts = [] :: [{certfile, binary()}],
|
opts = [] :: [{certfile, binary()}],
|
||||||
orig_trid,
|
orig_trid,
|
||||||
|
@ -42,7 +44,33 @@ start_link(LServer, Opts) ->
|
||||||
route(SIPMsg, _SIPSock, TrID, Pid) ->
|
route(SIPMsg, _SIPSock, TrID, Pid) ->
|
||||||
?GEN_FSM:send_event(Pid, {SIPMsg, TrID}).
|
?GEN_FSM:send_event(Pid, {SIPMsg, TrID}).
|
||||||
|
|
||||||
route(Req, LServer, Opts) ->
|
route(#sip{hdrs = Hdrs} = Req, LServer, Opts) ->
|
||||||
|
case proplists:get_bool(authenticated, Opts) of
|
||||||
|
true ->
|
||||||
|
route_statelessly(Req, LServer, Opts);
|
||||||
|
false ->
|
||||||
|
ConfiguredRoute = get_configured_route(LServer),
|
||||||
|
ConfiguredBareRoute = ConfiguredRoute#uri{user = <<"">>},
|
||||||
|
case esip:get_hdrs('route', Hdrs) of
|
||||||
|
[{_, URI, _}|_] ->
|
||||||
|
BareURI = URI#uri{user = <<"">>},
|
||||||
|
case cmp_uri(BareURI, ConfiguredBareRoute) of
|
||||||
|
true ->
|
||||||
|
case is_signed_by_me(URI#uri.user, Hdrs) of
|
||||||
|
true ->
|
||||||
|
route_statelessly(Req, LServer, Opts);
|
||||||
|
false ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
[] ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
route_statelessly(Req, LServer, Opts) ->
|
||||||
Req1 = prepare_request(LServer, Req),
|
Req1 = prepare_request(LServer, Req),
|
||||||
case connect(Req1, add_certfile(LServer, Opts)) of
|
case connect(Req1, add_certfile(LServer, Opts)) of
|
||||||
{ok, SIPSocketsWithURIs} ->
|
{ok, SIPSocketsWithURIs} ->
|
||||||
|
@ -253,7 +281,11 @@ add_record_route_and_set_uri(URI, LServer, #sip{hdrs = Hdrs} = Req) ->
|
||||||
case is_request_within_dialog(Req) of
|
case is_request_within_dialog(Req) of
|
||||||
false ->
|
false ->
|
||||||
RR_URI = get_configured_route(LServer),
|
RR_URI = get_configured_route(LServer),
|
||||||
Hdrs1 = [{'record-route', [{<<>>, RR_URI, []}]}|Hdrs],
|
{MSecs, Secs, _} = now(),
|
||||||
|
TS = list_to_binary(integer_to_list(MSecs*1000000 + Secs)),
|
||||||
|
Sign = make_sign(TS, Hdrs),
|
||||||
|
NewRR_URI = RR_URI#uri{user = <<TS/binary, $-, Sign/binary>>},
|
||||||
|
Hdrs1 = [{'record-route', [{<<>>, NewRR_URI, []}]}|Hdrs],
|
||||||
Req#sip{uri = URI, hdrs = Hdrs1};
|
Req#sip{uri = URI, hdrs = Hdrs1};
|
||||||
true ->
|
true ->
|
||||||
Req
|
Req
|
||||||
|
@ -263,6 +295,31 @@ is_request_within_dialog(#sip{hdrs = Hdrs}) ->
|
||||||
{_, _, Params} = esip:get_hdr('to', Hdrs),
|
{_, _, Params} = esip:get_hdr('to', Hdrs),
|
||||||
esip:has_param(<<"tag">>, Params).
|
esip:has_param(<<"tag">>, Params).
|
||||||
|
|
||||||
|
make_sign(TS, Hdrs) ->
|
||||||
|
{_, #uri{user = FUser, host = FServer}, FParams} = esip:get_hdr('from', Hdrs),
|
||||||
|
{_, #uri{user = TUser, host = TServer}, _} = esip:get_hdr('to', Hdrs),
|
||||||
|
LFUser = jlib:nodeprep(FUser),
|
||||||
|
LTUser = jlib:nodeprep(TUser),
|
||||||
|
LFServer = jlib:nameprep(FServer),
|
||||||
|
LTServer = jlib:nameprep(TServer),
|
||||||
|
FromTag = esip:get_param(<<"tag">>, FParams),
|
||||||
|
CallID = esip:get_hdr('call-id', Hdrs),
|
||||||
|
SharedKey = ejabberd_config:get_option(shared_key, fun(V) -> V end),
|
||||||
|
p1_sha:sha([SharedKey, LFUser, LFServer, LTUser, LTServer,
|
||||||
|
FromTag, CallID, TS]).
|
||||||
|
|
||||||
|
is_signed_by_me(TS_Sign, Hdrs) ->
|
||||||
|
try
|
||||||
|
[TSBin, Sign] = str:tokens(TS_Sign, <<"-">>),
|
||||||
|
TS = list_to_integer(binary_to_list(TSBin)),
|
||||||
|
{MSecs, Secs, _} = now(),
|
||||||
|
NowTS = MSecs*1000000 + Secs,
|
||||||
|
true = (NowTS - TS) =< ?SIGN_LIFETIME,
|
||||||
|
Sign == make_sign(TSBin, Hdrs)
|
||||||
|
catch _:_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
get_configured_vias(LServer) ->
|
get_configured_vias(LServer) ->
|
||||||
gen_mod:get_module_opt(
|
gen_mod:get_module_opt(
|
||||||
LServer, mod_sip, via,
|
LServer, mod_sip, via,
|
||||||
|
@ -323,12 +380,14 @@ cmp_uri(_, _) ->
|
||||||
|
|
||||||
prepare_request(LServer, #sip{hdrs = Hdrs} = Req) ->
|
prepare_request(LServer, #sip{hdrs = Hdrs} = Req) ->
|
||||||
ConfiguredRoute = get_configured_route(LServer),
|
ConfiguredRoute = get_configured_route(LServer),
|
||||||
|
ConfiguredBareRoute = ConfiguredRoute#uri{user = <<"">>},
|
||||||
Hdrs1 = lists:flatmap(
|
Hdrs1 = lists:flatmap(
|
||||||
fun({Hdr, HdrList}) when Hdr == 'route';
|
fun({Hdr, HdrList}) when Hdr == 'route';
|
||||||
Hdr == 'record-route' ->
|
Hdr == 'record-route' ->
|
||||||
case lists:filter(
|
case lists:filter(
|
||||||
fun({_, URI, _}) ->
|
fun({_, URI, _}) ->
|
||||||
not cmp_uri(URI, ConfiguredRoute)
|
BareURI = URI#uri{user = <<"">>},
|
||||||
|
not cmp_uri(BareURI, ConfiguredBareRoute)
|
||||||
end, HdrList) of
|
end, HdrList) of
|
||||||
[] ->
|
[] ->
|
||||||
[];
|
[];
|
||||||
|
|
Loading…
Reference in New Issue