diff --git a/doc/guide.tex b/doc/guide.tex index 153f6be7f..c2aafb0a0 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -3796,6 +3796,12 @@ Options: 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. @@ -3823,7 +3829,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, "??"}}. @@ -3836,7 +3843,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} diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index 98590949f..53786d615 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -569,6 +569,9 @@ %% %%{registration_watchers, ["admin1@example.org"]}, + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + {access, register} ]}, %%{mod_register_web, [ diff --git a/src/mod_register.erl b/src/mod_register.erl index cde22b365..c4eff1a32 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -324,17 +324,22 @@ try_register_strong(User, Server, Password, Source, _Lang, JID) -> end end. -try_register(User, Server, Password, Source, Lang) -> +try_register(User, Server, Password, SourceRaw, Lang) -> case exmpp_stringprep:is_node(User) of false -> {error, 'bad-request'}; _ -> JID = exmpp_jid:make(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, 'forbidden'}; - allow -> + {_, deny} -> + {error, 'forbidden'}; + {allow, allow} -> + Source = may_remove_resource(SourceRaw), case check_timeout(Source) of true -> case is_strong_password(Server, Password) of @@ -532,3 +537,95 @@ is_strong_password(Server, Password) -> [Wrong]), true end. + +%%% +%%% ip_access management +%%% + +may_remove_resource({U, S, _} = From) -> + {U, S, ""}; +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.