%%%---------------------------------------------------------------------- %%% File : cyrsasl.erl %%% Author : Alexey Shchepin %%% Purpose : Cyrus SASL-like library %%% Created : 8 Mar 2003 by Alexey Shchepin %%% Id : $Id$ %%%---------------------------------------------------------------------- -module(cyrsasl). -author('alexey@sevcom.net'). -vsn('$Revision$ '). -export([start/0, register_mechanism/3, listmech/1, server_new/6, server_start/3, server_step/2]). -record(sasl_mechanism, {mechanism, module, require_plain_password}). -record(sasl_state, {service, myname, realm, get_password, check_password, mech_mod, mech_state}). -export([behaviour_info/1]). behaviour_info(callbacks) -> [{mech_new, 3}, {mech_step, 2}]; behaviour_info(Other) -> undefined. start() -> ets:new(sasl_mechanism, [named_table, public, {keypos, #sasl_mechanism.mechanism}]), cyrsasl_plain:start([]), cyrsasl_digest:start([]), cyrsasl_anonymous:start([]), ok. register_mechanism(Mechanism, Module, RequirePlainPassword) -> ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism, module = Module, require_plain_password = RequirePlainPassword}). % TODO: use callbacks -include("ejabberd.hrl"). -include("jlib.hrl"). check_authzid(State, Props) -> AuthzId = xml:get_attr_s(authzid, Props), case jlib:string_to_jid(AuthzId) of error -> {error, "invalid-authzid"}; JID -> LUser = jlib:nodeprep(xml:get_attr_s(username, Props)), {U, S, R} = jlib:jid_tolower(JID), case R of "" -> {error, "invalid-authzid"}; _ -> case {LUser, ?MYNAME} of {U, S} -> ok; _ -> {error, "invalid-authzid"} end end end. check_credentials(State, Props) -> User = xml:get_attr_s(username, Props), case jlib:nodeprep(User) of error -> {error, "not-authorized"}; "" -> {error, "not-authorized"}; LUser -> ok end. listmech(Host) -> RequirePlainPassword = ejabberd_auth:plain_password_required(Host), Mechs = ets:select(sasl_mechanism, [{#sasl_mechanism{mechanism = '$1', require_plain_password = '$2', _ = '_'}, if RequirePlainPassword -> [{'==', '$2', false}]; true -> [] end, ['$1']}]), filter_anonymous(Host, Mechs). server_new(Service, ServerFQDN, UserRealm, SecFlags, GetPassword, CheckPassword) -> #sasl_state{service = Service, myname = ServerFQDN, realm = UserRealm, get_password = GetPassword, check_password = CheckPassword}. server_start(State, Mech, ClientIn) -> case lists:member(Mech, listmech(State#sasl_state.myname)) of true -> case ets:lookup(sasl_mechanism, Mech) of [#sasl_mechanism{module = Module}] -> {ok, MechState} = Module:mech_new( State#sasl_state.myname, State#sasl_state.get_password, State#sasl_state.check_password), server_step(State#sasl_state{mech_mod = Module, mech_state = MechState}, ClientIn); _ -> {error, "no-mechanism"} end; false -> {error, "no-mechanism"} end. server_step(State, ClientIn) -> Module = State#sasl_state.mech_mod, MechState = State#sasl_state.mech_state, case Module:mech_step(MechState, ClientIn) of {ok, Props} -> case check_credentials(State, Props) of ok -> {ok, Props}; {error, Error} -> {error, Error} end; {continue, ServerOut, NewMechState} -> {continue, ServerOut, State#sasl_state{mech_state = NewMechState}}; {error, Error} -> {error, Error} end. %% Remove the anonymous mechanism from the list if not enabled for the given %% host filter_anonymous(Host, Mechs) -> case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of true -> Mechs; false -> Mechs -- ["ANONYMOUS"] end.