]> granicus.if.org Git - ejabberd/commitdiff
fixing 1324 commit issue on tagged versions
authorChristophe Romain <christophe.romain@process-one.net>
Fri, 16 May 2008 14:23:37 +0000 (14:23 +0000)
committerChristophe Romain <christophe.romain@process-one.net>
Fri, 16 May 2008 14:23:37 +0000 (14:23 +0000)
SVN Revision: 1326

ChangeLog
src/ejabberd_c2s.erl
src/ejabberd_socket.erl
src/jlib.erl
src/mod_ip_blacklist.erl [new file with mode: 0644]

index 22fc6162f171bb597dafd402f958ad5ee52a1a9f..0700d05e2a1ecd14516fb561f115c252937b0a96 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-05-16  Mickael Remond  <mremond@process-one.net>
+
+       * src/ejabberd_c2s.erl: Added C2S blacklist support (EJAB-625).
+       * src/mod_ip_blacklist.erl: Likewise.
+       * src/jlib.erl: Added IP format tuple to string function.
+       * src/ejabberd_socket.erl: Properly handled c2s start failure (happen
+       for blacklisted IP).
+
 2008-05-16  Christophe Romain <christophe.romain@process-one.net>
 
        * src/ejabberd_receiver.erl: Don't activate a socket untill its
index 567cd6b6e2b71ee170759f05a0fbde002445e35c..f2b7f7e70637cec8caadd04afd63b95ea3c1f905 100644 (file)
@@ -56,7 +56,6 @@
 
 -include("ejabberd.hrl").
 -include("jlib.hrl").
--include("mod_privacy.hrl").
 
 -define(SETS, gb_sets).
 -define(DICT, dict).
@@ -85,9 +84,8 @@
                pres_last, pres_pri,
                pres_timestamp,
                pres_invis = false,
-               privacy_list = #userlist{},
+               privacy_list = none,
                conn = unknown,
-               auth_module = unknown,
                ip,
                lang}).
 
@@ -176,26 +174,35 @@ init([{SockMod, Socket}, Opts]) ->
                              (_) -> false
                           end, Opts),
     IP = peerip(SockMod, Socket),
-    Socket1 =
-       if
-           TLSEnabled ->
-               SockMod:starttls(Socket, TLSOpts);
-           true ->
-               Socket
-       end,
-    SocketMonitor = SockMod:monitor(Socket1),
-    {ok, wait_for_stream, #state{socket         = Socket1,
-                                sockmod        = SockMod,
-                                socket_monitor = SocketMonitor,
-                                zlib           = Zlib,
-                                tls            = TLS,
-                                tls_required   = StartTLSRequired,
-                                tls_enabled    = TLSEnabled,
-                                tls_options    = TLSOpts,
-                                streamid       = new_id(),
-                                access         = Access,
-                                shaper         = Shaper,
-                                ip             = IP}, ?C2S_OPEN_TIMEOUT}.
+    %% Check if IP is blacklisted:
+    case is_ip_blacklisted(IP) of
+       true ->
+           ?INFO_MSG("Connection attempt from blacklisted IP: ~s",
+                     [jlib:ip_to_list(IP)]),
+           {stop, normal};
+       false ->
+           Socket1 =
+               if
+                   TLSEnabled ->
+                       SockMod:starttls(Socket, TLSOpts);
+                   true ->
+                       Socket
+               end,
+           SocketMonitor = SockMod:monitor(Socket1),
+           {ok, wait_for_stream, #state{socket         = Socket1,
+                                        sockmod        = SockMod,
+                                        socket_monitor = SocketMonitor,
+                                        zlib           = Zlib,
+                                        tls            = TLS,
+                                        tls_required   = StartTLSRequired,
+                                        tls_enabled    = TLSEnabled,
+                                        tls_options    = TLSOpts,
+                                        streamid       = new_id(),
+                                        access         = Access,
+                                        shaper         = Shaper,
+                                        ip             = IP},
+            ?C2S_OPEN_TIMEOUT}
+    end.
 
 %% Return list of all available resources of contacts,
 %% in form [{JID, Caps}].
@@ -239,11 +246,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
                                        cyrsasl:server_new(
                                          "jabber", Server, "", [],
                                          fun(U) ->
-                                                 ejabberd_auth:get_password_with_authmodule(
+                                                 ejabberd_auth:get_password(
                                                    U, Server)
                                          end,
                                          fun(U, P) ->
-                                                 ejabberd_auth:check_password_with_authmodule(
+                                                 ejabberd_auth:check_password(
                                                    U, Server, P)
                                          end),
                                    Mechs = lists:map(
@@ -344,9 +351,9 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
                                true ->
                                    send_text(StateData, Header),
                                    fsm_next_state(wait_for_auth,
-                                                  StateData#state{
-                                                    server = Server,
-                                                    lang = Lang})
+                                              StateData#state{
+                                                server = Server,
+                                                lang = Lang})
                            end
                    end;
                _ ->
@@ -431,18 +438,17 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
                (acl:match_rule(StateData#state.server,
                                StateData#state.access, JID) == allow) of
                true ->
-                   case ejabberd_auth:check_password_with_authmodule(
+                   case ejabberd_auth:check_password(
                           U, StateData#state.server, P,
                           StateData#state.streamid, D) of
-                       {true, AuthModule} ->
+                       true ->
                            ?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},
-                                   {auth_module, AuthModule}],
+                           Info = [{ip, StateData#state.ip}, {conn, Conn}],
                            ejabberd_sm:open_session(
                              SID, U, StateData#state.server, R, Info),
                            Res1 = jlib:make_result_iq_reply(El),
@@ -461,7 +467,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
                            PrivList =
                                ejabberd_hooks:run_fold(
                                  privacy_get_user_list, StateData#state.server,
-                                 #userlist{},
+                                 none,
                                  [U, StateData#state.server]),
                            fsm_next_state(session_established,
                                           StateData#state{
@@ -470,7 +476,6 @@ 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});
@@ -677,14 +682,12 @@ 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,
@@ -795,8 +798,7 @@ 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},
-                           {auth_module, StateData#state.auth_module}],
+                   Info = [{ip, StateData#state.ip}, {conn, Conn}],
                    ejabberd_sm:open_session(
                      SID, U, StateData#state.server, R, Info),
                    Res = jlib:make_result_iq_reply(El),
@@ -813,7 +815,7 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
                    PrivList =
                        ejabberd_hooks:run_fold(
                          privacy_get_user_list, StateData#state.server,
-                         #userlist{},
+                         none,
                          [U, StateData#state.server]),
                    fsm_next_state(session_established,
                                   StateData#state{
@@ -849,8 +851,6 @@ wait_for_session(closed, StateData) ->
     {stop, normal, StateData}.
 
 
-
-
 session_established({xmlstreamelement, El}, StateData) ->
     {xmlelement, Name, Attrs, _Els} = El,
     User = StateData#state.user,
@@ -907,18 +907,24 @@ session_established({xmlstreamelement, El}, StateData) ->
                                               StateData)
                        end;
                    "iq" ->
-                       case jlib:iq_query_info(NewEl) of
-                           #iq{xmlns = ?NS_PRIVACY} = IQ ->
-                               process_privacy_iq(
-                                 FromJID, ToJID, IQ, StateData);
-                           _ ->
-                               ejabberd_hooks:run(
-                                 user_send_packet,
-                                 Server,
-                                 [FromJID, ToJID, NewEl]),
-                               ejabberd_router:route(
-                                 FromJID, ToJID, NewEl),
-                               StateData
+                       case StateData#state.privacy_list of
+                           none ->
+                               ejabberd_router:route(FromJID, ToJID, NewEl),
+                               StateData;
+                           _PrivList ->
+                               case jlib:iq_query_info(NewEl) of
+                                   #iq{xmlns = ?NS_PRIVACY} = IQ ->
+                                       process_privacy_iq(
+                                         FromJID, ToJID, IQ, StateData);
+                                   _ ->
+                                       ejabberd_hooks:run(
+                                         user_send_packet,
+                                         Server,
+                                         [FromJID, ToJID, NewEl]),
+                                       ejabberd_router:route(
+                                         FromJID, ToJID, NewEl),
+                                       StateData
+                               end
                        end;
                    "message" ->
                        ejabberd_hooks:run(user_send_packet,
@@ -1420,13 +1426,7 @@ process_presence_probe(From, To, StateData) ->
                        allow ->
                            Pid=element(2, StateData#state.sid),
                            ejabberd_hooks:run(presence_probe_hook, StateData#state.server, [From, To, Pid]),
-                           %% Don't route a presence probe to oneself
-                           case From == To of
-                               false ->
-                                   ejabberd_router:route(To, From, Packet);
-                               true ->
-                                   ok
-                           end
+                           ejabberd_router:route(To, From, Packet)
                    end;
                Cond2 ->
                    ejabberd_router:route(To, From,
@@ -1908,8 +1908,7 @@ process_unauthenticated_stanza(StateData, El) ->
            Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq,
                                          StateData#state.server,
                                          empty,
-                                         [StateData#state.server, IQ,
-                                          StateData#state.ip]),
+                                         [StateData#state.server, IQ]),
            case Res of
                empty ->
                    % The only reasonable IQ's here are auth and register IQ's
@@ -1952,3 +1951,7 @@ fsm_reply(Reply, session_established, StateData) ->
     {reply, Reply, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
 fsm_reply(Reply, StateName, StateData) ->
     {reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
+
+%% Used by c2s blacklist plugins
+is_ip_blacklisted({IP,_Port}) ->
+    ejabberd_hooks:run_fold(check_bl_c2s, false, [IP]).        
index f94a0a0e175c0bc95d29b69a605cd6b5045df7f9..389b3a10656dbc3ff01a5a11d35563998c028ff2 100644 (file)
@@ -65,19 +65,27 @@ start(Module, SockMod, Socket, Opts) ->
            SocketData = #socket_state{sockmod = SockMod,
                                       socket = Socket,
                                       receiver = Receiver},
-           {ok, Pid} = Module:start({?MODULE, SocketData}, Opts),
-           case SockMod:controlling_process(Socket, Receiver) of
-               ok ->
-                   ok;
+           case Module:start({?MODULE, SocketData}, Opts) of
+               {ok, Pid} ->
+                   case SockMod:controlling_process(Socket, Receiver) of
+                       ok ->
+                           ok;
+                       {error, _Reason} ->
+                           SockMod:close(Socket)
+                   end,
+                   ejabberd_receiver:become_controller(Receiver, Pid);
                {error, _Reason} ->
                    SockMod:close(Socket)
-           end,
-           ejabberd_receiver:become_controller(Receiver, Pid);
+           end;
        raw ->
-           {ok, Pid} = Module:start({SockMod, Socket}, Opts),
-           case SockMod:controlling_process(Socket, Pid) of
-               ok ->
-                   ok;
+           case Module:start({SockMod, Socket}, Opts) of
+               {ok, Pid} ->
+                   case SockMod:controlling_process(Socket, Pid) of
+                       ok ->
+                           ok;
+                       {error, _Reason} ->
+                           SockMod:close(Socket)
+                   end;
                {error, _Reason} ->
                    SockMod:close(Socket)
            end
index 1ee2e4ffa0f0c10d7a75df0e66cd6c2f2c2b1908..4fd8975995fd34ed641f80121c21a3e5d062adac 100644 (file)
@@ -59,7 +59,8 @@
         now_to_local_string/1,
         datetime_string_to_timestamp/1,
         decode_base64/1,
-        encode_base64/1]).
+        encode_base64/1,
+        ip_to_list/1]).
 
 -include("jlib.hrl").
 
@@ -676,3 +677,9 @@ e(X) when X>51, X<62 ->     X-4;
 e(62) ->                    $+;
 e(63) ->                    $/;
 e(X) ->                     exit({bad_encode_base64_token, X}).
+
+%% Convert Erlang inet IP to list
+ip_to_list({IP, _Port}) ->
+    ip_to_list(IP);
+ip_to_list({A,B,C,D}) ->
+    lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D])).
diff --git a/src/mod_ip_blacklist.erl b/src/mod_ip_blacklist.erl
new file mode 100644 (file)
index 0000000..095c501
--- /dev/null
@@ -0,0 +1,113 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_ip_blacklist.erl
+%%% Author  : Mickael Remond <mremond@process-one.net>
+%%% Purpose : Download blacklists from ProcessOne
+%%% Created : 5 May 2008 by Mickael Remond <mremond@process-one.net>
+%%% Usage   : Add the following line in modules section of ejabberd.cfg:
+%%%              {mod_ip_blacklist, []}
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2008   Process-one
+%%%
+%%% 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., 59 Temple Place, Suite 330, Boston, MA
+%%% 02111-1307 USA
+%%%
+%%%----------------------------------------------------------------------
+
+-module(mod_ip_blacklist).
+-author('mremond@process-one.net').
+
+-behaviour(gen_mod).
+
+%% API:
+-export([start/2,
+         init/1,
+         stop/1]).
+-export([update_bl_c2s/0]).
+%% Hooks:
+-export([is_ip_in_c2s_blacklist/2]).
+
+-include("ejabberd.hrl").
+
+-define(PROCNAME, ?MODULE).
+-define(BLC2S, "http://xaai.process-one.net/bl_c2s.txt").
+-define(UPDATE_INTERVAL, 6). %% in hours
+
+-record(state, {timer}).
+-record(bl_c2s, {ip}).
+
+%% Start once for all vhost
+start(Host, Opts) ->
+    case whereis(?PROCNAME) of
+        undefined ->
+            ?DEBUG("Starting mod_ip_blacklist ~p  ~p~n", [Host, Opts]),
+              register(?PROCNAME,
+                     spawn(?MODULE, init, [#state{}]));
+        _ ->
+            ok
+    end.
+
+%% TODO:
+stop(_Host) ->
+    ok.
+
+init(State)->
+    inets:start(),
+    ets:new(bl_c2s, [named_table, public, {keypos, #bl_c2s.ip}]),
+    update_bl_c2s(),
+    %% Register hooks for blacklist
+    ejabberd_hooks:add(check_bl_c2s, ?MODULE, is_ip_in_c2s_blacklist, 50),
+    %% Set timer: Download the blacklist file every 6 hours
+    timer:apply_interval(timer:hours(?UPDATE_INTERVAL), ?MODULE, update_bl_c2s, []),
+    loop(State).
+
+%% Remove timer when stop is received.
+loop(_State) ->
+    receive
+       stop ->
+           ok
+    end.
+
+%% Download blacklist file from ProcessOne XAAI
+%% and update the table internal table
+%% TODO: Support comment lines starting by %
+update_bl_c2s() ->
+    ?INFO_MSG("Updating C2S Blacklist", []),
+    {ok, {{_Version, 200, _Reason}, _Headers, Body}} = http:request(?BLC2S),
+    IPs = string:tokens(Body,"\n"),
+    ets:delete_all_objects(bl_c2s),
+    lists:foreach(
+      fun(IP) ->
+             ets:insert(bl_c2s, #bl_c2s{ip=list_to_binary(IP)})
+      end, IPs).
+
+%% Hook is run with:
+%% ejabberd_hooks:run_fold(check_bl_c2s, false, [IP]),
+%% Return: false: IP not blacklisted
+%%         true: IP is blacklisted
+%% IPV4 IP tuple:
+is_ip_in_c2s_blacklist(_Val, IP) ->
+    BinaryIP = list_to_binary(jlib:ip_to_list(IP)),
+    case ets:lookup(bl_c2s, BinaryIP) of
+       [] -> %% Not in blacklist
+           false;
+       [_] -> %% Blacklisted!
+           {stop, true}
+    end.
+
+
+%% TODO:
+%% - For now, we do not kick user already logged on a given IP after
+%%    we update the blacklist.