diff --git a/src/mod_sip.erl b/src/mod_sip.erl index 4b733c623..9c50c3241 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -80,7 +80,9 @@ response(_Resp, _SIPSock) -> request(#sip{method = <<"ACK">>} = Req, SIPSock) -> case action(Req, SIPSock) of {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 end; @@ -112,20 +114,20 @@ request(Req, SIPSock, TrID, Action) -> ?INFO_MSG("failed to proxy request ~p: ~p", [Req, Err]), Err end; - {proxy_auth, Host} -> + {proxy_auth, LServer} -> make_response( Req, #sip{status = 407, type = response, hdrs = [{'proxy-authenticate', - make_auth_hdr(Host)}]}); - {auth, Host} -> + make_auth_hdr(LServer)}]}); + {auth, LServer} -> make_response( Req, #sip{status = 401, type = response, hdrs = [{'www-authenticate', - make_auth_hdr(Host)}]}); + make_auth_hdr(LServer)}]}); deny -> make_response(Req, #sip{status = 403, type = response}); @@ -169,7 +171,7 @@ action(#sip{method = <<"REGISTER">>, type = request, hdrs = Hdrs, true -> register; false -> - {auth, ToURI#uri.host} + {auth, jlib:nameprep(ToURI#uri.host)} end; false -> deny @@ -259,8 +261,7 @@ process(Req, _) -> hdrs = [{'allow', allow()}]}). make_auth_hdr(LServer) -> - Realm = jlib:nameprep(LServer), - {<<"Digest">>, [{<<"realm">>, esip:quote(Realm)}, + {<<"Digest">>, [{<<"realm">>, esip:quote(LServer)}, {<<"qop">>, esip:quote(<<"auth">>)}, {<<"nonce">>, esip:quote(esip:make_hexstr(20))}]}. diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl index f25acbd4e..2294ba452 100644 --- a/src/mod_sip_proxy.erl +++ b/src/mod_sip_proxy.erl @@ -23,6 +23,8 @@ -include("logger.hrl"). -include("esip.hrl"). +-define(SIGN_LIFETIME, 300). %% in seconds. + -record(state, {host = <<"">> :: binary(), opts = [] :: [{certfile, binary()}], orig_trid, @@ -42,7 +44,33 @@ start_link(LServer, Opts) -> route(SIPMsg, _SIPSock, TrID, Pid) -> ?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), case connect(Req1, add_certfile(LServer, Opts)) of {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 false -> 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 = <>}, + Hdrs1 = [{'record-route', [{<<>>, NewRR_URI, []}]}|Hdrs], Req#sip{uri = URI, hdrs = Hdrs1}; true -> Req @@ -263,6 +295,31 @@ is_request_within_dialog(#sip{hdrs = Hdrs}) -> {_, _, Params} = esip:get_hdr('to', Hdrs), 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) -> gen_mod:get_module_opt( LServer, mod_sip, via, @@ -323,12 +380,14 @@ cmp_uri(_, _) -> prepare_request(LServer, #sip{hdrs = Hdrs} = Req) -> ConfiguredRoute = get_configured_route(LServer), + ConfiguredBareRoute = ConfiguredRoute#uri{user = <<"">>}, Hdrs1 = lists:flatmap( fun({Hdr, HdrList}) when Hdr == 'route'; Hdr == 'record-route' -> case lists:filter( fun({_, URI, _}) -> - not cmp_uri(URI, ConfiguredRoute) + BareURI = URI#uri{user = <<"">>}, + not cmp_uri(BareURI, ConfiguredBareRoute) end, HdrList) of [] -> [];