]> granicus.if.org Git - ejabberd/commitdiff
New ip_access option restricts which IPs can register (thanks to Alexey Shchepin...
authorBadlop <badlop@process-one.net>
Fri, 5 Nov 2010 18:00:16 +0000 (19:00 +0100)
committerBadlop <badlop@process-one.net>
Fri, 5 Nov 2010 18:33:52 +0000 (19:33 +0100)
doc/guide.tex
src/ejabberd.cfg.example
src/mod_register.erl

index 4fdb43b22ade343c3d471021b160b65917d2d370..28bc05253f912459fca8afe5ba0ae582c3e81ef6 100644 (file)
@@ -3779,6 +3779,12 @@ change it by defining access rule in this option. Use with care: allowing regist
 from s2s leads to uncontrolled massive accounts creation by rogue users.
 \titem{\{captcha\_protected, false|true\}} \ind{options!captcha\_protected}
 Protect registrations with CAPTCHA (see section \ref{captcha}). The default is \term{false}.
+\titem{\{ip\_access, [ \{allow|deny, IPaddress\}, ...]\}} \ind{options!ip\_access}
+  Define rules to allow or deny account registration depending
+  in the IP address of the XMPP client.
+  If there is no matching IP mask, the default rule is ``allow''.
+  IPv6 addresses are supported, but not tested.
+  The default option value is an empty list: \term{[]}.
 \titem{\{password\_strength, Entropy\}} \ind{options!password\_strength}
 This option sets the minimum informational entropy for passwords. The value \term{Entropy}
 is a number of bits of entropy. The recommended minimum is 32 bits.
@@ -3806,7 +3812,8 @@ Default value: 600 seconds.
 
 Examples:
 \begin{itemize}
-\item Next example prohibits the registration of too short account names:
+\item Next example prohibits the registration of too short account names,
+and allows to create accounts only to clients of the local network:
 \begin{verbatim}
 {acl, shortname, {user_glob, "?"}}.
 {acl, shortname, {user_glob, "??"}}.
@@ -3819,7 +3826,10 @@ Examples:
 {modules,
  [
   ...
-  {mod_register, [{access, register}]},
+  {mod_register, [{access, register},
+                  {ip_access, [{allow, "127.0.0.0/8"},
+                               {deny, "0.0.0.0/0"}]}
+  ]},
   ...
  ]}.
 \end{verbatim}
index 25605667ee2d8d5842611d6c3ee538f1042e2cf4..b5dd4c2802abc485a6a342d9b46682483b57bd89 100644 (file)
                  %%
                  %%{registration_watchers, ["admin1@example.org"]},
 
+                 {ip_access, [{allow, "127.0.0.0/8"},
+                              {deny, "0.0.0.0/0"}]},
+
                  {access, register}
                 ]},
   %%{mod_register_web, [
index 14588fa59b489472dd5adb329526fc81d22e955f..2c9f9c50fcb74990120117f8d911fa2633827345 100644 (file)
@@ -88,7 +88,7 @@ unauthenticated_iq_register(Acc, _Server, _IQ, _IP) ->
     Acc.
 
 process_iq(From, To, IQ) ->
-    process_iq(From, To, IQ, jlib:jid_tolower(jlib:jid_remove_resource(From))).
+    process_iq(From, To, IQ, jlib:jid_tolower(From)).
 
 process_iq(From, To,
           #iq{type = Type, lang = Lang, sub_el = SubEl, id = ID} = IQ,
@@ -311,17 +311,22 @@ try_set_password(User, Server, Password, IQ, SubEl, Lang) ->
                  sub_el = [SubEl, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)]}
     end.
 
-try_register(User, Server, Password, Source, Lang) ->
+try_register(User, Server, Password, SourceRaw, Lang) ->
     case jlib:is_nodename(User) of
        false ->
            {error, ?ERR_BAD_REQUEST};
        _ ->
            JID = jlib:make_jid(User, Server, ""),
            Access = gen_mod:get_module_opt(Server, ?MODULE, access, all),
-           case acl:match_rule(Server, Access, JID) of
-               deny ->
+           IPAccess = get_ip_access(Server),
+           case {acl:match_rule(Server, Access, JID),
+                 check_ip_access(SourceRaw, IPAccess)} of
+               {deny, _} ->
                    {error, ?ERR_FORBIDDEN};
-               allow ->
+               {_, deny} ->
+                   {error, ?ERR_FORBIDDEN};
+               {allow, allow} ->
+                   Source = may_remove_resource(SourceRaw),
                    case check_timeout(Source) of
                        true ->
                            case is_strong_password(Server, Password) of
@@ -536,3 +541,95 @@ is_strong_password(Server, Password) ->
                         [Wrong]),
            true
     end.
+
+%%%
+%%% ip_access management
+%%%
+
+may_remove_resource({_, _, _} = From) ->
+    jlib:jid_remove_resource(From);
+may_remove_resource(From) ->
+    From.
+
+get_ip_access(Host) ->
+    IPAccess = gen_mod:get_module_opt(Host, ?MODULE, ip_access, []),
+    lists:flatmap(
+      fun({Access, S}) ->
+             case parse_ip_netmask(S) of
+                 {ok, IP, Mask} ->
+                     [{Access, IP, Mask}];
+                 error ->
+                     ?ERROR_MSG("mod_register: invalid "
+                                "network specification: ~p",
+                                [S]),
+                     []
+             end
+      end, IPAccess).
+
+parse_ip_netmask(S) ->
+    case string:tokens(S, "/") of
+       [IPStr] ->
+           case inet_parse:address(IPStr) of
+               {ok, {_, _, _, _} = IP} ->
+                   {ok, IP, 32};
+               {ok, {_, _, _, _, _, _, _, _} = IP} ->
+                   {ok, IP, 128};
+               _ ->
+                   error
+           end;
+       [IPStr, MaskStr] ->
+           case catch list_to_integer(MaskStr) of
+               Mask when is_integer(Mask),
+                         Mask >= 0 ->
+                   case inet_parse:address(IPStr) of
+                       {ok, {_, _, _, _} = IP} when Mask =< 32 ->
+                           {ok, IP, Mask};
+                       {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
+                           {ok, IP, Mask};
+                       _ ->
+                           error
+                   end;
+               _ ->
+                   error
+           end;
+       _ ->
+           error
+    end.
+
+check_ip_access(_Source, []) ->
+    allow;
+check_ip_access({User, Server, Resource}, IPAccess) ->
+    case ejabberd_sm:get_user_ip(User, Server, Resource) of
+       {IPAddress, _PortNumber} -> check_ip_access(IPAddress, IPAccess);
+       _ -> true
+    end;
+check_ip_access({_, _, _, _} = IP,
+               [{Access, {_, _, _, _} = Net, Mask} | IPAccess]) ->
+    IPInt = ip_to_integer(IP),
+    NetInt = ip_to_integer(Net),
+    M = bnot ((1 bsl (32 - Mask)) - 1),
+    if
+       IPInt band M =:= NetInt band M ->
+           Access;
+       true ->
+           check_ip_access(IP, IPAccess)
+    end;
+check_ip_access({_, _, _, _, _, _, _, _} = IP,
+               [{Access, {_, _, _, _, _, _, _, _} = Net, Mask} | IPAccess]) ->
+    IPInt = ip_to_integer(IP),
+    NetInt = ip_to_integer(Net),
+    M = bnot ((1 bsl (128 - Mask)) - 1),
+    if
+       IPInt band M =:= NetInt band M ->
+           Access;
+       true ->
+           check_ip_access(IP, IPAccess)
+    end;
+check_ip_access(IP, [_ | IPAccess]) ->
+    check_ip_access(IP, IPAccess).
+
+ip_to_integer({IP1, IP2, IP3, IP4}) ->
+    (((((IP1 bsl 8) bor IP2) bsl 8) bor IP3) bsl 8) bor IP4;
+ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8}) ->
+    (((((((((((((IP1 bsl 16) bor IP2) bsl 16) bor IP3) bsl 16) bor IP4)
+          bsl 16) bor IP5) bsl 16) bor IP6) bsl 16) bor IP7) bsl 16) bor IP8.