]> granicus.if.org Git - ejabberd/commitdiff
* src/ejabberd_auth.erl: Improve anonymous authentication to not
authorBadlop <badlop@process-one.net>
Tue, 22 Apr 2008 17:41:30 +0000 (17:41 +0000)
committerBadlop <badlop@process-one.net>
Tue, 22 Apr 2008 17:41:30 +0000 (17:41 +0000)
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

ChangeLog
src/cyrsasl_anonymous.erl
src/cyrsasl_digest.erl
src/cyrsasl_plain.erl
src/ejabberd_auth.erl
src/ejabberd_auth_anonymous.erl
src/ejabberd_c2s.erl

index 6e0151260f6921d781f5ceb5428ddcc700206b3c..23bf00dccb472c646d68fbdf2c0b080bed85e20f 100644 (file)
--- 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
index 7393685d81088ba3005bc43642e69561181b830b..b9cf7449c1ce4f8a7f275ea74d495c1bbcbeaa56 100644 (file)
@@ -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.
index 6d87ae1990809696da259d51c8548051a31fd36e..5395205d7942f511e4d7c120d5f28dc082397d20 100644 (file)
@@ -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"}.
index eaeb1be47c2204a7f47d5c87ede19c27b0454b03..1b9cddb491d30fe41e5977c9bcf4429771e93162 100644 (file)
@@ -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;
index 5201900d4a7f5ea7220a805b0707d59a1580d059..d6a7e52287dc83f20f102b2b75d22dbd7bf064dd 100644 (file)
@@ -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) ->
index be04fc0a822f1daf2db114c290649ab88fa3482b..3c5c58fed08b5e62b3193a0f626b8af94e801549 100644 (file)
@@ -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}, _) ->
index 4974c0acfa5adcb5eb9bb51962f6aba22a6bbfb3..53bb41cafddcd9fe4d3d40ea59175b9328453e79 100644 (file)
@@ -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),