Packages
ejabberd
26.1.0
26.4.0
26.3.0
26.2.0
26.1.0
25.10.0
25.8.0
25.7.0
25.4.0
25.3.0
24.12.0
24.10.0
24.7.0
24.6.0
24.2.6
23.10.0
23.4.0
23.1.0
22.10.0
22.5.0
21.12.0
21.7.0
21.4.0
21.1.0
20.12.0
20.7.0
20.4.0
20.3.0
20.2.0
20.1.0
19.9.1
19.9.0
19.8.0
19.5.0
19.2.0
18.12.1
18.12.0
18.6.0
18.4.0
18.3.0
18.1.0
17.11.0
17.9.0
17.6.0
17.3.0
17.1.0
16.12.0-beta1
16.9.0
16.8.0
16.6.2
16.6.1
16.6.0
16.4.1
16.4.0
16.3.0
16.2.0
16.1.0-beta1
Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server)
Current section
Files
Jump to
Current section
Files
src/mod_legacy_auth.erl
%%%-------------------------------------------------------------------
%%% Created : 11 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2026 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(mod_legacy_auth).
-behaviour(gen_mod).
-protocol({xep, 78, '2.5', '17.03', "complete", ""}).
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_doc/0]).
%% hooks
-export([c2s_unauthenticated_packet/2, c2s_stream_features/2]).
-include_lib("xmpp/include/xmpp.hrl").
-include("translate.hrl").
-type c2s_state() :: ejabberd_c2s:state().
%%%===================================================================
%%% API
%%%===================================================================
start(_Host, _Opts) ->
{ok, [{hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50},
{hook, c2s_pre_auth_features, c2s_stream_features, 50}]}.
stop(_Host) ->
ok.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
mod_options(_) ->
[].
mod_doc() ->
#{desc =>
[?T("The module implements "
"https://xmpp.org/extensions/xep-0078.html"
"[XEP-0078: Non-SASL Authentication]."), "",
?T("NOTE: This type of authentication was obsoleted in "
"2008 and you unlikely need this module unless "
"you have something like outdated Jabber bots.")]}.
-spec c2s_unauthenticated_packet(c2s_state(), iq()) ->
c2s_state() | {stop, c2s_state()}.
c2s_unauthenticated_packet(State, #iq{type = T, sub_els = [_]} = IQ)
when T == get; T == set ->
try xmpp:try_subtag(IQ, #legacy_auth{}) of
#legacy_auth{} = Auth ->
{stop, authenticate(State, xmpp:set_els(IQ, [Auth]))};
false ->
State
catch _:{xmpp_codec, Why} ->
Txt = xmpp:io_format_error(Why),
Lang = maps:get(lang, State),
Err = xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)),
{stop, ejabberd_c2s:send(State, Err)}
end;
c2s_unauthenticated_packet(State, _) ->
State.
-spec c2s_stream_features([xmpp_element()], binary()) -> [xmpp_element()].
c2s_stream_features(Acc, LServer) ->
case gen_mod:is_loaded(LServer, ?MODULE) of
true ->
[#legacy_auth_feature{}|Acc];
false ->
Acc
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec authenticate(c2s_state(), iq()) -> c2s_state().
authenticate(#{server := Server} = State,
#iq{type = get, sub_els = [#legacy_auth{}]} = IQ) ->
LServer = jid:nameprep(Server),
Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>},
Res = case ejabberd_auth:plain_password_required(LServer) of
false ->
xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>});
true ->
xmpp:make_iq_result(IQ, Auth)
end,
ejabberd_c2s:send(State, Res);
authenticate(State,
#iq{type = set, lang = Lang,
sub_els = [#legacy_auth{username = U,
resource = R}]} = IQ)
when U == undefined; R == undefined; U == <<"">>; R == <<"">> ->
Txt = ?T("Both the username and the resource are required"),
Err = xmpp:make_error(IQ, xmpp:err_not_acceptable(Txt, Lang)),
ejabberd_c2s:send(State, Err);
authenticate(#{stream_id := StreamID, server := Server,
access := Access, ip := IP} = State,
#iq{type = set, lang = Lang,
sub_els = [#legacy_auth{username = U,
password = P0,
digest = D0,
resource = R}]} = IQ) ->
P = if is_binary(P0) -> P0; true -> <<>> end,
D = if is_binary(D0) -> D0; true -> <<>> end,
DGen = fun (PW) -> str:sha(<<StreamID/binary, PW/binary>>) end,
JID = jid:make(U, Server, R),
case JID /= error andalso
acl:match_rule(JID#jid.lserver, Access,
#{usr => jid:split(JID), ip => IP}) == allow of
true ->
case ejabberd_auth:check_password_with_authmodule(
U, U, JID#jid.lserver, P, D, DGen) of
{true, AuthModule} ->
State1 = State#{sasl_mech => <<"legacy">>},
State2 = ejabberd_c2s:handle_auth_success(
U, <<"legacy">>, AuthModule, State1),
State3 = State2#{user := U},
open_session(State3, IQ, R);
_ ->
Err = xmpp:make_error(IQ, xmpp:err_not_authorized()),
process_auth_failure(State, U, Err, 'not-authorized')
end;
false when JID == error ->
Err = xmpp:make_error(IQ, xmpp:err_jid_malformed()),
process_auth_failure(State, U, Err, 'jid-malformed');
false ->
Txt = ?T("Access denied by service policy"),
Err = xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)),
process_auth_failure(State, U, Err, 'forbidden')
end.
-spec open_session(c2s_state(), iq(), binary()) -> c2s_state().
open_session(State, IQ, R) ->
case ejabberd_c2s:bind(R, State) of
{ok, State1} ->
Res = xmpp:make_iq_result(IQ),
ejabberd_c2s:send(State1, Res);
{error, Err, State1} ->
Res = xmpp:make_error(IQ, Err),
ejabberd_c2s:send(State1, Res)
end.
-spec process_auth_failure(c2s_state(), binary(), iq(), atom()) -> c2s_state().
process_auth_failure(State, User, StanzaErr, Reason) ->
State1 = ejabberd_c2s:send(State, StanzaErr),
State2 = State1#{sasl_mech => <<"legacy">>},
Text = format_reason(Reason),
ejabberd_c2s:handle_auth_failure(User, <<"legacy">>, Text, State2).
-spec format_reason(atom()) -> binary().
format_reason('not-authorized') ->
<<"Invalid username or password">>;
format_reason('forbidden') ->
<<"Access denied by service policy">>;
format_reason('jid-malformed') ->
<<"Malformed XMPP address">>.