diff --git a/ChangeLog b/ChangeLog index 762ece800..8b2520f03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,14 +15,6 @@ * doc/guide.tex: Describe how to disable registration limitation (EJAB-614) -2008-05-16 Mickael Remond - - * 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 * src/ejabberd_receiver.erl: Don't activate a socket untill its diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index f2b7f7e70..567cd6b6e 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -56,6 +56,7 @@ -include("ejabberd.hrl"). -include("jlib.hrl"). +-include("mod_privacy.hrl"). -define(SETS, gb_sets). -define(DICT, dict). @@ -84,8 +85,9 @@ pres_last, pres_pri, pres_timestamp, pres_invis = false, - privacy_list = none, + privacy_list = #userlist{}, conn = unknown, + auth_module = unknown, ip, lang}). @@ -174,35 +176,26 @@ init([{SockMod, Socket}, Opts]) -> (_) -> false end, Opts), IP = peerip(SockMod, Socket), - %% 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. + 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}. %% Return list of all available resources of contacts, %% in form [{JID, Caps}]. @@ -246,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( @@ -351,9 +344,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; _ -> @@ -438,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), @@ -467,7 +461,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) -> PrivList = ejabberd_hooks:run_fold( privacy_get_user_list, StateData#state.server, - none, + #userlist{}, [U, StateData#state.server]), fsm_next_state(session_established, StateData#state{ @@ -476,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}); @@ -682,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, @@ -798,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), @@ -815,7 +813,7 @@ wait_for_session({xmlstreamelement, El}, StateData) -> PrivList = ejabberd_hooks:run_fold( privacy_get_user_list, StateData#state.server, - none, + #userlist{}, [U, StateData#state.server]), fsm_next_state(session_established, StateData#state{ @@ -851,6 +849,8 @@ wait_for_session(closed, StateData) -> {stop, normal, StateData}. + + session_established({xmlstreamelement, El}, StateData) -> {xmlelement, Name, Attrs, _Els} = El, User = StateData#state.user, @@ -907,24 +907,18 @@ session_established({xmlstreamelement, El}, StateData) -> StateData) end; "iq" -> - 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 + 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; "message" -> ejabberd_hooks:run(user_send_packet, @@ -1426,7 +1420,13 @@ 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]), - ejabberd_router:route(To, From, Packet) + %% Don't route a presence probe to oneself + case From == To of + false -> + ejabberd_router:route(To, From, Packet); + true -> + ok + end end; Cond2 -> ejabberd_router:route(To, From, @@ -1908,7 +1908,8 @@ process_unauthenticated_stanza(StateData, El) -> Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq, StateData#state.server, empty, - [StateData#state.server, IQ]), + [StateData#state.server, IQ, + StateData#state.ip]), case Res of empty -> % The only reasonable IQ's here are auth and register IQ's @@ -1951,7 +1952,3 @@ 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]). diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl index 389b3a106..f94a0a0e1 100644 --- a/src/ejabberd_socket.erl +++ b/src/ejabberd_socket.erl @@ -65,27 +65,19 @@ start(Module, SockMod, Socket, Opts) -> SocketData = #socket_state{sockmod = SockMod, socket = Socket, receiver = Receiver}, - 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); + {ok, Pid} = Module:start({?MODULE, SocketData}, Opts), + case SockMod:controlling_process(Socket, Receiver) of + ok -> + ok; {error, _Reason} -> SockMod:close(Socket) - end; + end, + ejabberd_receiver:become_controller(Receiver, Pid); raw -> - case Module:start({SockMod, Socket}, Opts) of - {ok, Pid} -> - case SockMod:controlling_process(Socket, Pid) of - ok -> - ok; - {error, _Reason} -> - SockMod:close(Socket) - end; + {ok, Pid} = Module:start({SockMod, Socket}, Opts), + case SockMod:controlling_process(Socket, Pid) of + ok -> + ok; {error, _Reason} -> SockMod:close(Socket) end diff --git a/src/jlib.erl b/src/jlib.erl index 4fd897599..1ee2e4ffa 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -59,8 +59,7 @@ now_to_local_string/1, datetime_string_to_timestamp/1, decode_base64/1, - encode_base64/1, - ip_to_list/1]). + encode_base64/1]). -include("jlib.hrl"). @@ -677,9 +676,3 @@ 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 deleted file mode 100644 index 095c501a4..000000000 --- a/src/mod_ip_blacklist.erl +++ /dev/null @@ -1,113 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_ip_blacklist.erl -%%% Author : Mickael Remond -%%% Purpose : Download blacklists from ProcessOne -%%% Created : 5 May 2008 by Mickael Remond -%%% 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.