From 983da9c887d6cb64812087cba961dc85f349e1f9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Mar 2012 14:16:17 +0100 Subject: [PATCH] Relax digest-uri handling (thanks to Daniel Willmann)(EJAB-1529) This patch introduces a new config option - fqdn - to set the fully qualified domain name of the host: {fqdn, "foo.example.com"}. This fixes a problem with Pidgin not being able to log in on a server that used SRV records. --- doc/guide.tex | 6 ++++++ src/cyrsasl_digest.erl | 38 ++++++++++++++++++++++++++++---------- src/ejabberd.cfg.example | 3 +++ src/ejabberd_config.erl | 3 +++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/doc/guide.tex b/doc/guide.tex index 1b151ee7c..f0af763dd 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -1231,6 +1231,12 @@ The default value is \term{closeold}. If the client uses old Jabber Non-SASL authentication (\xepref{0078}), then this option is not respected, and the action performed is \term{closeold}. +The option \option{fqdn} allows you to define the Fully Qualified Domain Name +of the machine, in case it isn't detected automatically. +The FQDN is used to authenticate some clients that use the DIGEST-MD5 SASL mechanism. +The option syntax is: +\esyntax{\{fqdn, undefined|FqdnString\}.} + \makesubsubsection{internalauth}{Internal} \ind{internal authentication}\ind{Mnesia} diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index 99c8e2a0e..134a86daf 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -37,9 +37,11 @@ -behaviour(cyrsasl). -record(state, {step, nonce, username, authzid, get_password, check_password, auth_module, - host}). + host, hostfqdn}). start(_Opts) -> + Fqdn = get_local_fqdn(), + ?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~p", [Fqdn]), cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, digest). stop() -> @@ -49,6 +51,7 @@ mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) -> {ok, #state{step = 1, nonce = randoms:get_string(), host = Host, + hostfqdn = get_local_fqdn(), get_password = GetPassword, check_password = CheckPasswordDigest}}. @@ -64,10 +67,11 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) -> KeyVals -> DigestURI = xml:get_attr_s("digest-uri", KeyVals), UserName = xml:get_attr_s("username", KeyVals), - case is_digesturi_valid(DigestURI, State#state.host) of + case is_digesturi_valid(DigestURI, State#state.host, State#state.hostfqdn) of false -> ?DEBUG("User login not authorized because digest-uri " - "seems invalid: ~p", [DigestURI]), + "seems invalid: ~p (checking for Host ~p, FQDN ~p)", [DigestURI, + State#state.host, State#state.hostfqdn]), {error, "not-authorized", UserName}; true -> AuthzId = xml:get_attr_s("authzid", KeyVals), @@ -154,21 +158,35 @@ parse4([], Key, Val, Ts) -> %% however ejabberd doesn't allow that. %% If the service (for example jabber.example.org) %% is provided by several hosts (being one of them server3.example.org), -%% then digest-uri can be like xmpp/server3.example.org/jabber.example.org -%% In that case, ejabberd only checks the service name, not the host. -is_digesturi_valid(DigestURICase, JabberHost) -> +%% then acceptable digest-uris would be: +%% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and +%% xmpp/jabber.example.org +%% The last version is not actually allowed by the RFC, but implemented by popular clients +is_digesturi_valid(DigestURICase, JabberDomain, JabberFQDN) -> DigestURI = stringprep:tolower(DigestURICase), case catch string:tokens(DigestURI, "/") of - ["xmpp", Host] when Host == JabberHost -> + ["xmpp", Host] when (Host == JabberDomain) or (Host == JabberFQDN) -> true; - ["xmpp", _Host, ServName] when ServName == JabberHost -> + ["xmpp", Host, ServName] when (ServName == JabberDomain) and (Host == JabberFQDN) -> true; _ -> false end. - - +get_local_fqdn() -> + case (catch get_local_fqdn2()) of + Str when is_list(Str) -> Str; + _ -> "unknown-fqdn, please configure fqdn option in ejabberd.cfg!" + end. +get_local_fqdn2() -> + case ejabberd_config:get_local_option(fqdn) of + ConfiguredFqdn when is_list(ConfiguredFqdn) -> + ConfiguredFqdn; + _undefined -> + {ok, Hostname} = inet:gethostname(), + {ok, {hostent, Fqdn, _, _, _, _}} = inet:gethostbyname(Hostname), + Fqdn + end. digit_to_xchar(D) when (D >= 0) and (D < 10) -> D + 48; diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index 57eab41bd..a4068ad96 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -222,6 +222,9 @@ %% Store the plain passwords or hashed for SCRAM: %%{auth_password_format, plain}. %%{auth_password_format, scram}. +%% +%% Define the FQDN if ejabberd doesn't detect it: +%%{fqdn, "server3.example.com"}. %% %% Authentication using external script diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index b1b67fee7..503537e76 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -374,6 +374,9 @@ process_term(Term, State) -> State; {hosts, _Hosts} -> State; + {fqdn, HostFQDN} -> + ?DEBUG("FQDN set to: ~p", [HostFQDN]), + add_option(fqdn, HostFQDN, State); {host_config, Host, Terms} -> lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end, State, Terms);