From 9a44fdffab463adb4cc5d9463d23a35a78867b10 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 7 Apr 2006 00:39:24 +0000 Subject: [PATCH] * src/ejabberd_sm.erl: SASL Anonymous + Anonymous login support (thanks to Mickael Remond and Magnus Henoch) * src/ejabberd_c2s.erl: Likewise * src/ejabberd_auth.erl: Likewise * src/ejabberd_auth_anonymous.erl: Likewise * src/cyrsasl.erl: Likewise * src/cyrsasl_anonymous.erl: Likewise * src/ejabberd.cfg.example: Likewise SVN Revision: 527 --- ChangeLog | 11 +++++ src/cyrsasl.erl | 42 +++++++++------- src/cyrsasl_digest.erl | 4 +- src/cyrsasl_plain.erl | 4 +- src/ejabberd.cfg.example | 22 +++++++-- src/ejabberd_auth.erl | 98 ++++++++++++++++++++++++++++++-------- src/ejabberd_auth_odbc.erl | 4 +- src/ejabberd_c2s.erl | 5 +- src/ejabberd_sm.erl | 16 +++++-- 9 files changed, 154 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index a788dca0b..9606d4d62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-04-07 Alexey Shchepin + + * src/ejabberd_sm.erl: SASL Anonymous + Anonymous login support + (thanks to Mickael Remond and Magnus Henoch) + * src/ejabberd_c2s.erl: Likewise + * src/ejabberd_auth.erl: Likewise + * src/ejabberd_auth_anonymous.erl: Likewise + * src/cyrsasl.erl: Likewise + * src/cyrsasl_anonymous.erl: Likewise + * src/ejabberd.cfg.example: Likewise + 2006-04-06 Alexey Shchepin * src/expat_erl.c: Use binaries for CDATA diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl index 1b2937b41..4b040bee2 100644 --- a/src/cyrsasl.erl +++ b/src/cyrsasl.erl @@ -25,8 +25,7 @@ -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{mech_new, 2}, - {mech_step, 2}]; + [{mech_new, 3}, {mech_step, 2}]; behaviour_info(Other) -> undefined. @@ -36,6 +35,7 @@ start() -> {keypos, #sasl_mechanism.mechanism}]), cyrsasl_plain:start([]), cyrsasl_digest:start([]), + cyrsasl_anonymous:start([]), ok. register_mechanism(Mechanism, Module, RequirePlainPassword) -> @@ -81,18 +81,19 @@ check_credentials(State, Props) -> listmech(Host) -> RequirePlainPassword = ejabberd_auth:plain_password_required(Host), - ets:select(sasl_mechanism, - [{#sasl_mechanism{mechanism = '$1', - require_plain_password = '$2', - _ = '_'}, - if - RequirePlainPassword -> - [{'==', '$2', false}]; - true -> - [] - end, - ['$1']}]). - + + 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) -> @@ -105,8 +106,10 @@ server_new(Service, ServerFQDN, UserRealm, SecFlags, server_start(State, Mech, ClientIn) -> case ets:lookup(sasl_mechanism, Mech) of [#sasl_mechanism{module = Module}] -> - {ok, MechState} = Module:mech_new(State#sasl_state.get_password, - State#sasl_state.check_password), + {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); @@ -132,3 +135,10 @@ server_step(State, ClientIn) -> {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. diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index fbd543312..cc44b1a95 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -12,7 +12,7 @@ -export([start/1, stop/0, - mech_new/2, + mech_new/3, mech_step/2]). -behaviour(cyrsasl). @@ -25,7 +25,7 @@ start(_Opts) -> stop() -> ok. -mech_new(GetPassword, _CheckPassword) -> +mech_new(_Host, GetPassword, _CheckPassword) -> {ok, #state{step = 1, nonce = randoms:get_string(), get_password = GetPassword}}. diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl index 48472bfa5..e58e1cc22 100644 --- a/src/cyrsasl_plain.erl +++ b/src/cyrsasl_plain.erl @@ -10,7 +10,7 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start/1, stop/0, mech_new/2, mech_step/2, parse/1]). +-export([start/1, stop/0, mech_new/3, mech_step/2, parse/1]). -behaviour(cyrsasl). @@ -23,7 +23,7 @@ start(_Opts) -> stop() -> ok. -mech_new(_GetPassword, CheckPassword) -> +mech_new(_Host, _GetPassword, CheckPassword) -> {ok, #state{check_password = CheckPassword}}. mech_step(State, ClientIn) -> diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index 749ba5276..3d94d73df 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -73,11 +73,11 @@ {access, local, [{allow, local}]}. -% Authentification method. If you want to use internal user base, then use +% Authentication method. If you want to use internal user base, then use % this line: {auth_method, internal}. -% For LDAP authentification use these lines instead of above one: +% For LDAP authentication use these lines instead of above one: %{auth_method, ldap}. %{ldap_servers, ["localhost"]}. % List of LDAP servers %{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID @@ -85,11 +85,11 @@ %{ldap_rootdn, "dc=example,dc=com"}. % LDAP manager %{ldap_password, "******"}. % Password to LDAP manager -% For authentification via external script use the following: +% For authentication via external script use the following: %{auth_method, external}. -%{extauth_program, "/path/to/authentification/script"}. +%{extauth_program, "/path/to/authentication/script"}. -% For authentification via ODBC use the following: +% For authentication via ODBC use the following: %{auth_method, odbc}. %{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. @@ -98,6 +98,18 @@ {hosts, ["localhost"]}. +%% Anonymous login support: +%% anonymous_protocol: sasl_anon|login_anon|both +%% allow_multiple_connections: true|false +%% anon_digest_password: "anonymous" (this is the default password that should +%% be use for digest login). +%%{host_config, "public.example.org", [{auth_method, anonymous}, +%% {allow_multiple_connections, false}, +%% {anonymous_protocol, sasl_anon}, +%% {anon_digest_password, "anonymous"}]}. +%% To use both anonymous and internal authentication: +%%{host_config, "public.example.org", [{auth_method, [anonymous, internal]}]}. + % Default language for server messages {language, "en"}. diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 045bf2726..ea4518b9b 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -3,9 +3,13 @@ %%% Author : Alexey Shchepin %%% Purpose : Authentification %%% Created : 23 Nov 2002 by Alexey Shchepin +%%% Updated : 23 Feb 2006 by Mickael Remond +%%% for anonymous login support %%% Id : $Id$ %%%---------------------------------------------------------------------- +%% TODO: Use the functions in ejabberd auth to add and remove users. + -module(ejabberd_auth). -author('alexey@sevcom.net'). -vsn('$Revision$ '). @@ -27,6 +31,8 @@ ctl_process_get_registered/3 ]). +-export([auth_modules/1]). + -include("ejabberd.hrl"). -include("ejabberd_ctl.hrl"). @@ -34,50 +40,102 @@ %%% API %%%---------------------------------------------------------------------- start() -> - lists:foreach(fun(Host) -> - (auth_module(Host)):start(Host) - end, ?MYHOSTS). + lists:foreach( + fun(Host) -> + lists:foreach( + fun(M) -> + M:start(Host) + end, auth_modules(Host)) + end, ?MYHOSTS). plain_password_required(Server) -> - (auth_module(Server)):plain_password_required(). + lists:any( + fun(M) -> + M:plain_password_required() + end, auth_modules(Server)). check_password(User, Server, Password) -> - (auth_module(Server)):check_password(User, Server, Password). + lists:any( + fun(M) -> + M:check_password(User, Server, Password) + end, auth_modules(Server)). check_password(User, Server, Password, StreamID, Digest) -> - (auth_module(Server)):check_password(User, Server, Password, StreamID, Digest). + lists:any( + fun(M) -> + M:check_password(User, Server, Password, StreamID, Digest) + end, auth_modules(Server)). set_password(User, Server, Password) -> - (auth_module(Server)):set_password(User, Server, Password). + lists:foldl( + fun(M, {error, _}) -> + M:set_password(User, Server, Password); + (_M, Res) -> + Res + end, {error, not_allowed}, auth_modules(Server)). try_register(User, Server, Password) -> case lists:member(jlib:nameprep(Server), ?MYHOSTS) of true -> - (auth_module(Server)):try_register(User, Server, Password); + lists:foldl( + fun(_M, {atomic, ok} = Res) -> + Res; + (M, _) -> + M:try_register(User, Server, Password) + end, {error, not_allowed}, auth_modules(Server)); false -> {error, not_allowed} end. +%% Registered users list do not include anonymous users logged dirty_get_registered_users() -> - (auth_module(?MYNAME)):dirty_get_registered_users(). + lists:flatmap( + fun(M) -> + M:dirty_get_registered_users() + end, auth_modules(?MYNAME)). +%% Registered users list do not include anonymous users logged get_vh_registered_users(Server) -> - (auth_module(Server)):get_vh_registered_users(Server). + lists:flatmap( + fun(M) -> + M:get_vh_registered_users(Server) + end, auth_modules(Server)). get_password(User, Server) -> - (auth_module(Server)):get_password(User, Server). + lists:foldl( + fun(M, false) -> + M:get_password(User, Server); + (_M, Password) -> + Password + end, false, auth_modules(Server)). get_password_s(User, Server) -> - (auth_module(Server)):get_password_s(User, Server). + case get_password(User, Server) of + false -> + ""; + Password -> + Password + end. +%% Returns true if the user exists in the DB or if an anonymous user is logged +%% under the given name is_user_exists(User, Server) -> - (auth_module(Server)):is_user_exists(User, Server). + lists:any( + fun(M) -> + M:is_user_exists(User, Server) + end, auth_modules(Server)). remove_user(User, Server) -> - (auth_module(Server)):remove_user(User, Server). + lists:foreach( + fun(M) -> + M:remove_user(User, Server) + end, auth_modules(Server)). remove_user(User, Server, Password) -> - (auth_module(Server)):remove_user(User, Server, Password). + lists:foreach( + fun(M) -> + M:remove_user(User, Server, Password) + end, auth_modules(Server)). ctl_process_get_registered(_Val, Host, ["registered-users"]) -> @@ -93,9 +151,11 @@ ctl_process_get_registered(Val, _Host, _Args) -> %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- - -auth_module(Server) -> +auth_modules(Server) -> LServer = jlib:nameprep(Server), Method = ejabberd_config:get_local_option({auth_method, LServer}), - list_to_atom("ejabberd_auth_" ++ atom_to_list(Method)). - + Methods = if + is_list(Method) -> Method; + is_atom(Method) -> [Method] + end, + [list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods]. diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl index 7b7459451..cf5a96131 100644 --- a/src/ejabberd_auth_odbc.erl +++ b/src/ejabberd_auth_odbc.erl @@ -217,8 +217,8 @@ remove_user(User, Server, Password) -> LServer = jlib:nameprep(Server), F = fun() -> Result = ejabberd_odbc:sql_query_t( - ["select password from users where username='", - Username, "';"]), + ["select password from users where username='", + Username, "';"]), ejabberd_odbc:sql_query_t(["delete from users " "where username='", Username, "' and password='", Pass, "';"]), diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index ccf9e22cd..e99d90600 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1158,7 +1158,10 @@ terminate(_Reason, StateName, StateData) -> pres_a = EmptySet, pres_i = EmptySet, pres_invis = false} -> - ejabberd_sm:close_session(StateData#state.sid); + ejabberd_sm:close_session(StateData#state.sid, + StateData#state.user, + StateData#state.server, + StateData#state.resource); _ -> From = StateData#state.jid, Packet = {xmlelement, "presence", diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index cd4bc6c6d..1127ddd09 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -15,7 +15,7 @@ %% API -export([start_link/0, route/3, - open_session/4, close_session/1, + open_session/4, close_session/4, bounce_offline_message/3, disconnect_removed_user/2, get_user_resources/2, @@ -62,13 +62,19 @@ route(From, To, Packet) -> end. open_session(SID, User, Server, Resource) -> - set_session(SID, User, Server, Resource, undefined). + set_session(SID, User, Server, Resource, undefined), + JID = jlib:make_jid(User, Server, Resource), + ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver, + [SID, JID]). -close_session(SID) -> +close_session(SID, User, Server, Resource) -> F = fun() -> mnesia:delete({session, SID}) end, - mnesia:sync_dirty(F). + mnesia:sync_dirty(F), + JID = jlib:make_jid(User, Server, Resource), + ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver, + [SID, JID]). bounce_offline_message(From, To, Packet) -> Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), @@ -101,7 +107,7 @@ unset_presence(SID, User, Server, Resource, Status) -> [User, Server, Resource, Status]). close_session_unset_presence(SID, User, Server, Resource, Status) -> - close_session(SID), + close_session(SID, User, Server, Resource), ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), [User, Server, Resource, Status]).