diff options
Diffstat (limited to 'src/cyrsasl_digest.erl')
-rw-r--r-- | src/cyrsasl_digest.erl | 267 |
1 files changed, 0 insertions, 267 deletions
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl deleted file mode 100644 index 0d408fc48..000000000 --- a/src/cyrsasl_digest.erl +++ /dev/null @@ -1,267 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_digest.erl -%%% Author : Alexey Shchepin <alexey@sevcom.net> -%%% Purpose : DIGEST-MD5 SASL mechanism -%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net> -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(cyrsasl_digest). - --behaviour(ejabberd_config). - --author('alexey@sevcom.net'). - --export([start/1, stop/0, mech_new/4, mech_step/2, - parse/1, opt_type/1]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --behaviour(cyrsasl). - --type get_password_fun() :: fun((binary()) -> {false, any()} | - {binary(), atom()}). - --type check_password_fun() :: fun((binary(), binary(), binary(), - fun((binary()) -> binary())) -> - {boolean(), any()} | - false). - --record(state, {step = 1 :: 1 | 3 | 5, - nonce = <<"">> :: binary(), - username = <<"">> :: binary(), - authzid = <<"">> :: binary(), - get_password = fun(_) -> {false, <<>>} end :: get_password_fun(), - check_password = fun(_, _, _, _, _) -> false end :: check_password_fun(), - auth_module :: atom(), - host = <<"">> :: binary(), - hostfqdn = <<"">> :: binary() | [binary()]}). - -start(_Opts) -> - Fqdn = get_local_fqdn(), - ?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~p", - [Fqdn]), - cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE, - digest). - -stop() -> ok. - -mech_new(Host, GetPassword, _CheckPassword, - CheckPasswordDigest) -> - {ok, - #state{step = 1, nonce = randoms:get_string(), - host = Host, hostfqdn = get_local_fqdn(), - get_password = GetPassword, - check_password = CheckPasswordDigest}}. - -mech_step(#state{step = 1, nonce = Nonce} = State, _) -> - {continue, - <<"nonce=\"", Nonce/binary, - "\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>, - State#state{step = 3}}; -mech_step(#state{step = 3, nonce = Nonce} = State, - ClientIn) -> - case parse(ClientIn) of - bad -> {error, <<"bad-protocol">>}; - KeyVals -> - DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>), - UserName = proplists:get_value(<<"username">>, KeyVals, <<>>), - case is_digesturi_valid(DigestURI, State#state.host, - State#state.hostfqdn) - of - false -> - ?DEBUG("User login not authorized because digest-uri " - "seems invalid: ~p (checking for Host " - "~p, FQDN ~p)", - [DigestURI, State#state.host, State#state.hostfqdn]), - {error, <<"not-authorized">>, UserName}; - true -> - AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>), - case (State#state.get_password)(UserName) of - {false, _} -> {error, <<"not-authorized">>, UserName}; - {Passwd, AuthModule} -> - case (State#state.check_password)(UserName, UserName, <<"">>, - proplists:get_value(<<"response">>, KeyVals, <<>>), - fun (PW) -> - response(KeyVals, - UserName, - PW, - Nonce, - AuthzId, - <<"AUTHENTICATE">>) - end) - of - {true, _} -> - RspAuth = response(KeyVals, UserName, Passwd, Nonce, - AuthzId, <<"">>), - {continue, <<"rspauth=", RspAuth/binary>>, - State#state{step = 5, auth_module = AuthModule, - username = UserName, - authzid = AuthzId}}; - false -> {error, <<"not-authorized">>, UserName}; - {false, _} -> {error, <<"not-authorized">>, UserName} - end - end - end - end; -mech_step(#state{step = 5, auth_module = AuthModule, - username = UserName, authzid = AuthzId}, - <<"">>) -> - {ok, - [{username, UserName}, {authzid, case AuthzId of - <<"">> -> UserName; - _ -> AuthzId - end - }, - {auth_module, AuthModule}]}; -mech_step(A, B) -> - ?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]), - {error, <<"bad-protocol">>}. - -parse(S) -> parse1(binary_to_list(S), "", []). - -parse1([$= | Cs], S, Ts) -> - parse2(Cs, lists:reverse(S), "", Ts); -parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts); -parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts); -parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts); -parse1([], [], T) -> lists:reverse(T); -parse1([], _S, _T) -> bad. - -parse2([$" | Cs], Key, Val, Ts) -> - parse3(Cs, Key, Val, Ts); -parse2([C | Cs], Key, Val, Ts) -> - parse4(Cs, Key, [C | Val], Ts); -parse2([], _, _, _) -> bad. - -parse3([$" | Cs], Key, Val, Ts) -> - parse4(Cs, Key, Val, Ts); -parse3([$\\, C | Cs], Key, Val, Ts) -> - parse3(Cs, Key, [C | Val], Ts); -parse3([C | Cs], Key, Val, Ts) -> - parse3(Cs, Key, [C | Val], Ts); -parse3([], _, _, _) -> bad. - -parse4([$, | Cs], Key, Val, Ts) -> - parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]); -parse4([$\s | Cs], Key, Val, Ts) -> - parse4(Cs, Key, Val, Ts); -parse4([C | Cs], Key, Val, Ts) -> - parse4(Cs, Key, [C | Val], Ts); -parse4([], Key, Val, Ts) -> -%% @doc Check if the digest-uri is valid. -%% RFC-2831 allows to provide the IP address in Host, -%% however ejabberd doesn't allow that. -%% If the service (for example jabber.example.org) -%% is provided by several hosts (being one of them server3.example.org), -%% then acceptable digest-uris would be: -%% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and -%% xmpp/jabber.example.org -%% The last version is not actually allowed by the RFC, but implemented by popular clients - parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]). - -is_digesturi_valid(DigestURICase, JabberDomain, - JabberFQDN) -> - DigestURI = stringprep:tolower(DigestURICase), - case catch str:tokens(DigestURI, <<"/">>) of - [<<"xmpp">>, Host] -> - IsHostFqdn = is_host_fqdn(Host, JabberFQDN), - (Host == JabberDomain) or IsHostFqdn; - [<<"xmpp">>, Host, ServName] -> - IsHostFqdn = is_host_fqdn(Host, JabberFQDN), - (ServName == JabberDomain) and IsHostFqdn; - _ -> - false - end. - -is_host_fqdn(Host, Fqdn) when is_binary(Fqdn) -> - Host == Fqdn; -is_host_fqdn(_Host, []) -> - false; -is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn -> - true; -is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn -> - is_host_fqdn(Host, FqdnTail). - -get_local_fqdn() -> - case catch get_local_fqdn2() of - Str when is_binary(Str) -> Str; - List when is_list(List) -> List; - _ -> - <<"unknown-fqdn, please configure fqdn " - "option in ejabberd.yml!">> - end. - -get_local_fqdn2() -> - case ejabberd_config:get_option( - fqdn, fun(X) -> X end) of - ConfiguredFqdn when is_binary(ConfiguredFqdn) -> - ConfiguredFqdn; - [A | _] = ConfiguredFqdns when is_binary(A) -> - ConfiguredFqdns; - undefined -> - {ok, Hostname} = inet:gethostname(), - {ok, {hostent, Fqdn, _, _, _, _}} = - inet:gethostbyname(Hostname), - list_to_binary(Fqdn) - end. - -hex(S) -> - p1_sha:to_hexlist(S). - -proplists_get_bin_value(Key, Pairs, Default) -> - case proplists:get_value(Key, Pairs, Default) of - L when is_list(L) -> - list_to_binary(L); - L2 -> - L2 - end. - -response(KeyVals, User, Passwd, Nonce, AuthzId, - A2Prefix) -> - Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>), - CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>), - DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>), - NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>), - QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>), - MD5Hash = erlang:md5(<<User/binary, ":", Realm/binary, ":", - Passwd/binary>>), - A1 = case AuthzId of - <<"">> -> - <<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary>>; - _ -> - <<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary, ":", - AuthzId/binary>> - end, - A2 = case QOP of - <<"auth">> -> - <<A2Prefix/binary, ":", DigestURI/binary>>; - _ -> - <<A2Prefix/binary, ":", DigestURI/binary, - ":00000000000000000000000000000000">> - end, - T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary, - ":", NC/binary, ":", CNonce/binary, ":", QOP/binary, - ":", (hex((erlang:md5(A2))))/binary>>, - hex((erlang:md5(T))). - -opt_type(fqdn) -> fun iolist_to_binary/1; -opt_type(_) -> [fqdn]. |