mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-30 16:36:29 +01:00
* 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
This commit is contained in:
parent
ee6aae8211
commit
96f0c001d9
14
ChangeLog
14
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>
|
2008-04-18 Badlop <badlop@process-one.net>
|
||||||
|
|
||||||
* src/ejabberd_s2s_out.erl: Fix long timeout when reconnecting s2s
|
* src/ejabberd_s2s_out.erl: Fix long timeout when reconnecting s2s
|
||||||
|
@ -51,5 +51,6 @@ mech_step(State, _ClientIn) ->
|
|||||||
%% Checks that the username is available
|
%% Checks that the username is available
|
||||||
case ejabberd_auth:is_user_exists(User, Server) of
|
case ejabberd_auth:is_user_exists(User, Server) of
|
||||||
true -> {error, "not-authorized"};
|
true -> {error, "not-authorized"};
|
||||||
false -> {ok, [{username, User}]}
|
false -> {ok, [{username, User},
|
||||||
|
{auth_module, ejabberd_auth_anonymous}]}
|
||||||
end.
|
end.
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
-behaviour(cyrsasl).
|
-behaviour(cyrsasl).
|
||||||
|
|
||||||
-record(state, {step, nonce, username, authzid, get_password}).
|
-record(state, {step, nonce, username, authzid, get_password, auth_module}).
|
||||||
|
|
||||||
start(_Opts) ->
|
start(_Opts) ->
|
||||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
|
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),
|
UserName = xml:get_attr_s("username", KeyVals),
|
||||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||||
case (State#state.get_password)(UserName) of
|
case (State#state.get_password)(UserName) of
|
||||||
false ->
|
{false, _} ->
|
||||||
{error, "not-authorized", UserName};
|
{error, "not-authorized", UserName};
|
||||||
Passwd ->
|
{Passwd, AuthModule} ->
|
||||||
Response = response(KeyVals, UserName, Passwd,
|
Response = response(KeyVals, UserName, Passwd,
|
||||||
Nonce, AuthzId, "AUTHENTICATE"),
|
Nonce, AuthzId, "AUTHENTICATE"),
|
||||||
case xml:get_attr_s("response", KeyVals) of
|
case xml:get_attr_s("response", KeyVals) of
|
||||||
@ -57,6 +57,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
|||||||
{continue,
|
{continue,
|
||||||
"rspauth=" ++ RspAuth,
|
"rspauth=" ++ RspAuth,
|
||||||
State#state{step = 5,
|
State#state{step = 5,
|
||||||
|
auth_module = AuthModule,
|
||||||
username = UserName,
|
username = UserName,
|
||||||
authzid = AuthzId}};
|
authzid = AuthzId}};
|
||||||
_ ->
|
_ ->
|
||||||
@ -65,9 +66,11 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
|||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
mech_step(#state{step = 5,
|
mech_step(#state{step = 5,
|
||||||
|
auth_module = AuthModule,
|
||||||
username = UserName,
|
username = UserName,
|
||||||
authzid = AuthzId}, "") ->
|
authzid = AuthzId}, "") ->
|
||||||
{ok, [{username, UserName}, {authzid, AuthzId}]};
|
{ok, [{username, UserName}, {authzid, AuthzId},
|
||||||
|
{auth_module, AuthModule}]};
|
||||||
mech_step(A, B) ->
|
mech_step(A, B) ->
|
||||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||||
{error, "bad-protocol"}.
|
{error, "bad-protocol"}.
|
||||||
|
@ -47,8 +47,9 @@ mech_step(State, ClientIn) ->
|
|||||||
case parse(ClientIn) of
|
case parse(ClientIn) of
|
||||||
[AuthzId, User, Password] ->
|
[AuthzId, User, Password] ->
|
||||||
case (State#state.check_password)(User, Password) of
|
case (State#state.check_password)(User, Password) of
|
||||||
true ->
|
{true, AuthModule} ->
|
||||||
{ok, [{username, User}, {authzid, AuthzId}]};
|
{ok, [{username, User}, {authzid, AuthzId},
|
||||||
|
{auth_module, AuthModule}]};
|
||||||
_ ->
|
_ ->
|
||||||
{error, "not-authorized", User}
|
{error, "not-authorized", User}
|
||||||
end;
|
end;
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
set_password/3,
|
set_password/3,
|
||||||
check_password/3,
|
check_password/3,
|
||||||
check_password/5,
|
check_password/5,
|
||||||
|
check_password_with_authmodule/3,
|
||||||
|
check_password_with_authmodule/5,
|
||||||
try_register/3,
|
try_register/3,
|
||||||
dirty_get_registered_users/0,
|
dirty_get_registered_users/0,
|
||||||
get_vh_registered_users/1,
|
get_vh_registered_users/1,
|
||||||
@ -42,6 +44,7 @@
|
|||||||
get_vh_registered_users_number/2,
|
get_vh_registered_users_number/2,
|
||||||
get_password/2,
|
get_password/2,
|
||||||
get_password_s/2,
|
get_password_s/2,
|
||||||
|
get_password_with_authmodule/2,
|
||||||
is_user_exists/2,
|
is_user_exists/2,
|
||||||
is_user_exists_in_other_modules/3,
|
is_user_exists_in_other_modules/3,
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
@ -73,18 +76,57 @@ plain_password_required(Server) ->
|
|||||||
M:plain_password_required()
|
M:plain_password_required()
|
||||||
end, auth_modules(Server)).
|
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) ->
|
check_password(User, Server, Password) ->
|
||||||
lists:any(
|
lists:any(
|
||||||
fun(M) ->
|
fun(M) ->
|
||||||
M:check_password(User, Server, Password)
|
M:check_password(User, Server, Password)
|
||||||
end, auth_modules(Server)).
|
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) ->
|
check_password(User, Server, Password, StreamID, Digest) ->
|
||||||
lists:any(
|
lists:any(
|
||||||
fun(M) ->
|
fun(M) ->
|
||||||
M:check_password(User, Server, Password, StreamID, Digest)
|
M:check_password(User, Server, Password, StreamID, Digest)
|
||||||
end, auth_modules(Server)).
|
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:
|
%% We do not allow empty password:
|
||||||
set_password(_User, _Server, "") ->
|
set_password(_User, _Server, "") ->
|
||||||
{error, not_allowed};
|
{error, not_allowed};
|
||||||
@ -163,6 +205,8 @@ get_vh_registered_users_number(Server, Opts) ->
|
|||||||
end
|
end
|
||||||
end, auth_modules(Server))).
|
end, auth_modules(Server))).
|
||||||
|
|
||||||
|
%% @doc Get the password of the user.
|
||||||
|
%% @spec (User::string(), Server::string()) -> Password::string()
|
||||||
get_password(User, Server) ->
|
get_password(User, Server) ->
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(M, false) ->
|
fun(M, false) ->
|
||||||
@ -179,6 +223,17 @@ get_password_s(User, Server) ->
|
|||||||
Password
|
Password
|
||||||
end.
|
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
|
%% Returns true if the user exists in the DB or if an anonymous user is logged
|
||||||
%% under the given name
|
%% under the given name
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
|
@ -141,11 +141,17 @@ remove_connection(SID, LUser, LServer) ->
|
|||||||
mnesia:transaction(F).
|
mnesia:transaction(F).
|
||||||
|
|
||||||
%% Register connection
|
%% Register connection
|
||||||
register_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
|
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},
|
US = {LUser, LServer},
|
||||||
mnesia:sync_dirty(
|
mnesia:sync_dirty(
|
||||||
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
||||||
end).
|
end);
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
%% Remove an anonymous user from the anonymous users table
|
%% Remove an anonymous user from the anonymous users table
|
||||||
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
|
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
pres_invis = false,
|
pres_invis = false,
|
||||||
privacy_list = #userlist{},
|
privacy_list = #userlist{},
|
||||||
conn = unknown,
|
conn = unknown,
|
||||||
|
auth_module = unknown,
|
||||||
ip,
|
ip,
|
||||||
lang}).
|
lang}).
|
||||||
|
|
||||||
@ -238,11 +239,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
|||||||
cyrsasl:server_new(
|
cyrsasl:server_new(
|
||||||
"jabber", Server, "", [],
|
"jabber", Server, "", [],
|
||||||
fun(U) ->
|
fun(U) ->
|
||||||
ejabberd_auth:get_password(
|
ejabberd_auth:get_password_with_authmodule(
|
||||||
U, Server)
|
U, Server)
|
||||||
end,
|
end,
|
||||||
fun(U, P) ->
|
fun(U, P) ->
|
||||||
ejabberd_auth:check_password(
|
ejabberd_auth:check_password_with_authmodule(
|
||||||
U, Server, P)
|
U, Server, P)
|
||||||
end),
|
end),
|
||||||
Mechs = lists:map(
|
Mechs = lists:map(
|
||||||
@ -430,17 +431,18 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
|||||||
(acl:match_rule(StateData#state.server,
|
(acl:match_rule(StateData#state.server,
|
||||||
StateData#state.access, JID) == allow) of
|
StateData#state.access, JID) == allow) of
|
||||||
true ->
|
true ->
|
||||||
case ejabberd_auth:check_password(
|
case ejabberd_auth:check_password_with_authmodule(
|
||||||
U, StateData#state.server, P,
|
U, StateData#state.server, P,
|
||||||
StateData#state.streamid, D) of
|
StateData#state.streamid, D) of
|
||||||
true ->
|
{true, AuthModule} ->
|
||||||
?INFO_MSG(
|
?INFO_MSG(
|
||||||
"(~w) Accepted legacy authentication for ~s",
|
"(~w) Accepted legacy authentication for ~s",
|
||||||
[StateData#state.socket,
|
[StateData#state.socket,
|
||||||
jlib:jid_to_string(JID)]),
|
jlib:jid_to_string(JID)]),
|
||||||
SID = {now(), self()},
|
SID = {now(), self()},
|
||||||
Conn = get_conn_type(StateData),
|
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(
|
ejabberd_sm:open_session(
|
||||||
SID, U, StateData#state.server, R, Info),
|
SID, U, StateData#state.server, R, Info),
|
||||||
Res1 = jlib:make_result_iq_reply(El),
|
Res1 = jlib:make_result_iq_reply(El),
|
||||||
@ -468,6 +470,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
|||||||
jid = JID,
|
jid = JID,
|
||||||
sid = SID,
|
sid = SID,
|
||||||
conn = Conn,
|
conn = Conn,
|
||||||
|
auth_module = AuthModule,
|
||||||
pres_f = ?SETS:from_list(Fs1),
|
pres_f = ?SETS:from_list(Fs1),
|
||||||
pres_t = ?SETS:from_list(Ts1),
|
pres_t = ?SETS:from_list(Ts1),
|
||||||
privacy_list = PrivList});
|
privacy_list = PrivList});
|
||||||
@ -674,12 +677,14 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
|||||||
{xmlelement, "success",
|
{xmlelement, "success",
|
||||||
[{"xmlns", ?NS_SASL}], []}),
|
[{"xmlns", ?NS_SASL}], []}),
|
||||||
U = xml:get_attr_s(username, Props),
|
U = xml:get_attr_s(username, Props),
|
||||||
|
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||||
?INFO_MSG("(~w) Accepted authentication for ~s",
|
?INFO_MSG("(~w) Accepted authentication for ~s",
|
||||||
[StateData#state.socket, U]),
|
[StateData#state.socket, U]),
|
||||||
fsm_next_state(wait_for_stream,
|
fsm_next_state(wait_for_stream,
|
||||||
StateData#state{
|
StateData#state{
|
||||||
streamid = new_id(),
|
streamid = new_id(),
|
||||||
authenticated = true,
|
authenticated = true,
|
||||||
|
auth_module = AuthModule,
|
||||||
user = U});
|
user = U});
|
||||||
{continue, ServerOut, NewSASLState} ->
|
{continue, ServerOut, NewSASLState} ->
|
||||||
send_element(StateData,
|
send_element(StateData,
|
||||||
@ -790,7 +795,8 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
|||||||
jlib:jid_to_string(JID)]),
|
jlib:jid_to_string(JID)]),
|
||||||
SID = {now(), self()},
|
SID = {now(), self()},
|
||||||
Conn = get_conn_type(StateData),
|
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(
|
ejabberd_sm:open_session(
|
||||||
SID, U, StateData#state.server, R, Info),
|
SID, U, StateData#state.server, R, Info),
|
||||||
Res = jlib:make_result_iq_reply(El),
|
Res = jlib:make_result_iq_reply(El),
|
||||||
|
Loading…
Reference in New Issue
Block a user