summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2008-04-22 17:41:30 +0000
committerBadlop <badlop@process-one.net>2008-04-22 17:41:30 +0000
commit96f0c001d9b11f98c842f463537d0056d2ce5acf (patch)
tree4f5c2908f828cc38ad68044e353a93f7c3dd9ca2
parent* src/ejabberd_s2s_out.erl: Fix long timeout when reconnecting s2s (diff)
* src/ejabberd_auth.erl: Improve anonymous authentication to not
remove rosters accidentally (EJAB-549). New functions in ejabberd_auth to get/check password and know which module accepted the authentication. New element 'auth_module' in ejabberd_c2s record 'statedata'. Cyrsasl provides a new property in the response: {auth_module, AuthModule}. * src/ejabberd_auth_anonymous.erl: Likewise * src/ejabberd_c2s.erl: Likewise * src/cyrsasl_anonymous.erl: Likewise * src/cyrsasl_digest.erl: Likewise * src/cyrsasl_plain.erl: Likewise SVN Revision: 1297
-rw-r--r--ChangeLog14
-rw-r--r--src/cyrsasl_anonymous.erl3
-rw-r--r--src/cyrsasl_digest.erl11
-rw-r--r--src/cyrsasl_plain.erl5
-rw-r--r--src/ejabberd_auth.erl55
-rw-r--r--src/ejabberd_auth_anonymous.erl16
-rw-r--r--src/ejabberd_c2s.erl18
7 files changed, 104 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 6e015126..23bf00dc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2008-04-22 Badlop <badlop@process-one.net>
+
+ * src/ejabberd_auth.erl: Improve anonymous authentication to not
+ remove rosters accidentally (EJAB-549). New functions in
+ ejabberd_auth to get/check password and know which module accepted
+ the authentication. New element 'auth_module' in ejabberd_c2s
+ record 'statedata'. Cyrsasl provides a new property in the
+ response: {auth_module, AuthModule}.
+ * src/ejabberd_auth_anonymous.erl: Likewise
+ * src/ejabberd_c2s.erl: Likewise
+ * src/cyrsasl_anonymous.erl: Likewise
+ * src/cyrsasl_digest.erl: Likewise
+ * src/cyrsasl_plain.erl: Likewise
+
2008-04-18 Badlop <badlop@process-one.net>
* src/ejabberd_s2s_out.erl: Fix long timeout when reconnecting s2s
diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl
index 7393685d..b9cf7449 100644
--- a/src/cyrsasl_anonymous.erl
+++ b/src/cyrsasl_anonymous.erl
@@ -51,5 +51,6 @@ mech_step(State, _ClientIn) ->
%% Checks that the username is available
case ejabberd_auth:is_user_exists(User, Server) of
true -> {error, "not-authorized"};
- false -> {ok, [{username, User}]}
+ false -> {ok, [{username, User},
+ {auth_module, ejabberd_auth_anonymous}]}
end.
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
index 6d87ae19..5395205d 100644
--- a/src/cyrsasl_digest.erl
+++ b/src/cyrsasl_digest.erl
@@ -18,7 +18,7 @@
-behaviour(cyrsasl).
--record(state, {step, nonce, username, authzid, get_password}).
+-record(state, {step, nonce, username, authzid, get_password, auth_module}).
start(_Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
@@ -44,9 +44,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
UserName = xml:get_attr_s("username", KeyVals),
AuthzId = xml:get_attr_s("authzid", KeyVals),
case (State#state.get_password)(UserName) of
- false ->
+ {false, _} ->
{error, "not-authorized", UserName};
- Passwd ->
+ {Passwd, AuthModule} ->
Response = response(KeyVals, UserName, Passwd,
Nonce, AuthzId, "AUTHENTICATE"),
case xml:get_attr_s("response", KeyVals) of
@@ -57,6 +57,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
{continue,
"rspauth=" ++ RspAuth,
State#state{step = 5,
+ auth_module = AuthModule,
username = UserName,
authzid = AuthzId}};
_ ->
@@ -65,9 +66,11 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
end
end;
mech_step(#state{step = 5,
+ auth_module = AuthModule,
username = UserName,
authzid = AuthzId}, "") ->
- {ok, [{username, UserName}, {authzid, AuthzId}]};
+ {ok, [{username, UserName}, {authzid, AuthzId},
+ {auth_module, AuthModule}]};
mech_step(A, B) ->
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
{error, "bad-protocol"}.
diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
index eaeb1be4..1b9cddb4 100644
--- a/src/cyrsasl_plain.erl
+++ b/src/cyrsasl_plain.erl
@@ -47,8 +47,9 @@ mech_step(State, ClientIn) ->
case parse(ClientIn) of
[AuthzId, User, Password] ->
case (State#state.check_password)(User, Password) of
- true ->
- {ok, [{username, User}, {authzid, AuthzId}]};
+ {true, AuthModule} ->
+ {ok, [{username, User}, {authzid, AuthzId},
+ {auth_module, AuthModule}]};
_ ->
{error, "not-authorized", User}
end;
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 5201900d..d6a7e522 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -34,6 +34,8 @@
set_password/3,
check_password/3,
check_password/5,
+ check_password_with_authmodule/3,
+ check_password_with_authmodule/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
@@ -42,6 +44,7 @@
get_vh_registered_users_number/2,
get_password/2,
get_password_s/2,
+ get_password_with_authmodule/2,
is_user_exists/2,
is_user_exists_in_other_modules/3,
remove_user/2,
@@ -73,18 +76,57 @@ plain_password_required(Server) ->
M:plain_password_required()
end, auth_modules(Server)).
+%% @doc Check if the user and password can login in server.
+%% @spec (User::string(), Server::string(), Password::string()) ->
+%% true | false
check_password(User, Server, Password) ->
lists:any(
fun(M) ->
M:check_password(User, Server, Password)
end, auth_modules(Server)).
+%% @doc Check if the user and password can login in server.
+%% @spec (User::string(), Server::string(), Password::string(),
+%% StreamID::string(), Digest::string()) ->
+%% true | false
check_password(User, Server, Password, StreamID, Digest) ->
lists:any(
fun(M) ->
M:check_password(User, Server, Password, StreamID, Digest)
end, auth_modules(Server)).
+%% @doc Check if the user and password can login in server.
+%% The user can login if at least an authentication method accepts the user
+%% and the password.
+%% The first authentication method that accepts the credentials is returned.
+%% @spec (User::string(), Server::string(), Password::string()) ->
+%% {true, AuthModule} | false
+%% where
+%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
+%% | ejabberd_auth_internal | ejabberd_auth_ldap
+%% | ejabberd_auth_odbc | ejabberd_auth_pam
+check_password_with_authmodule(User, Server, Password) ->
+ Res = lists:dropwhile(
+ fun(M) ->
+ not apply(M, check_password,
+ [User, Server, Password])
+ end, auth_modules(Server)),
+ case Res of
+ [] -> false;
+ [AuthMod | _] -> {true, AuthMod}
+ end.
+
+check_password_with_authmodule(User, Server, Password, StreamID, Digest) ->
+ Res = lists:dropwhile(
+ fun(M) ->
+ not apply(M, check_password,
+ [User, Server, Password, StreamID, Digest])
+ end, auth_modules(Server)),
+ case Res of
+ [] -> false;
+ [AuthMod | _] -> {true, AuthMod}
+ end.
+
%% We do not allow empty password:
set_password(_User, _Server, "") ->
{error, not_allowed};
@@ -163,6 +205,8 @@ get_vh_registered_users_number(Server, Opts) ->
end
end, auth_modules(Server))).
+%% @doc Get the password of the user.
+%% @spec (User::string(), Server::string()) -> Password::string()
get_password(User, Server) ->
lists:foldl(
fun(M, false) ->
@@ -179,6 +223,17 @@ get_password_s(User, Server) ->
Password
end.
+%% @doc Get the password of the user and the auth module.
+%% @spec (User::string(), Server::string()) ->
+%% {Password::string(), AuthModule::atom()} | {false, none}
+get_password_with_authmodule(User, Server) ->
+ lists:foldl(
+ fun(M, {false, _}) ->
+ {M:get_password(User, Server), M};
+ (_M, {Password, AuthModule}) ->
+ {Password, AuthModule}
+ end, {false, none}, auth_modules(Server)).
+
%% 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) ->
diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl
index be04fc0a..3c5c58fe 100644
--- a/src/ejabberd_auth_anonymous.erl
+++ b/src/ejabberd_auth_anonymous.erl
@@ -141,11 +141,17 @@ remove_connection(SID, LUser, LServer) ->
mnesia:transaction(F).
%% Register connection
-register_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
- US = {LUser, LServer},
- mnesia:sync_dirty(
- fun() -> mnesia:write(#anonymous{us = US, sid=SID})
- end).
+register_connection(SID, #jid{luser = LUser, lserver = LServer}, Info) ->
+ AuthModule = xml:get_attr_s(auth_module, Info),
+ case AuthModule == ?MODULE of
+ true ->
+ US = {LUser, LServer},
+ mnesia:sync_dirty(
+ fun() -> mnesia:write(#anonymous{us = US, sid=SID})
+ end);
+ false ->
+ ok
+ end.
%% Remove an anonymous user from the anonymous users table
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 4974c0ac..53bb41ca 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -87,6 +87,7 @@
pres_invis = false,
privacy_list = #userlist{},
conn = unknown,
+ auth_module = unknown,
ip,
lang}).
@@ -238,11 +239,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
cyrsasl:server_new(
"jabber", Server, "", [],
fun(U) ->
- ejabberd_auth:get_password(
+ ejabberd_auth:get_password_with_authmodule(
U, Server)
end,
fun(U, P) ->
- ejabberd_auth:check_password(
+ ejabberd_auth:check_password_with_authmodule(
U, Server, P)
end),
Mechs = lists:map(
@@ -430,17 +431,18 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
(acl:match_rule(StateData#state.server,
StateData#state.access, JID) == allow) of
true ->
- case ejabberd_auth:check_password(
+ case ejabberd_auth:check_password_with_authmodule(
U, StateData#state.server, P,
StateData#state.streamid, D) of
- true ->
+ {true, AuthModule} ->
?INFO_MSG(
"(~w) Accepted legacy authentication for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
SID = {now(), self()},
Conn = get_conn_type(StateData),
- Info = [{ip, StateData#state.ip}, {conn, Conn}],
+ Info = [{ip, StateData#state.ip}, {conn, Conn},
+ {auth_module, AuthModule}],
ejabberd_sm:open_session(
SID, U, StateData#state.server, R, Info),
Res1 = jlib:make_result_iq_reply(El),
@@ -468,6 +470,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
jid = JID,
sid = SID,
conn = Conn,
+ auth_module = AuthModule,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList});
@@ -674,12 +677,14 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
U = xml:get_attr_s(username, Props),
+ AuthModule = xml:get_attr_s(auth_module, Props),
?INFO_MSG("(~w) Accepted authentication for ~s",
[StateData#state.socket, U]),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
+ auth_module = AuthModule,
user = U});
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
@@ -790,7 +795,8 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
jlib:jid_to_string(JID)]),
SID = {now(), self()},
Conn = get_conn_type(StateData),
- Info = [{ip, StateData#state.ip}, {conn, Conn}],
+ Info = [{ip, StateData#state.ip}, {conn, Conn},
+ {auth_module, StateData#state.auth_module}],
ejabberd_sm:open_session(
SID, U, StateData#state.server, R, Info),
Res = jlib:make_result_iq_reply(El),