On 25.04.2013 14:27, Konstantin Khomoutov wrote: > Stephen, could you please help us and look at modifying your patch so > that it applies on 2.1.10? My own Erlang skills are not up to this > task. I'm referring to this Debian bug report [1]. And the release > time is unfortunately very close.
Hi, I backported the patch to the v2.1.10 tag of ejabberd, could you please verify that it works? I just tested it with the haskell testcase from joey hess.
>From 60376459c183543f5767e8cf0a93690cf52d6e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephen=20R=C3=B6ttger?= <stephen.roett...@zero-entropy.de> Date: Thu, 25 Apr 2013 16:34:45 +0200 Subject: [PATCH] SCRAM optional parameter parsing bugfix The server gave an authentication error, if optional parameters were present in the GS2 Header. Specifically, the "a=" parameter, that can be used by admins to login as a different user. Backport of 9e9b0eae802ee0508db6780426954efd048e7976 to 2.1.10 --- src/cyrsasl_scram.erl | 58 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl index 002d6e4..efc63c8 100644 --- a/src/cyrsasl_scram.erl +++ b/src/cyrsasl_scram.erl @@ -34,6 +34,8 @@ -include("ejabberd.hrl"). +-include("jlib.hrl"). + -behaviour(cyrsasl). -record(state, {step, stored_key, server_key, username, get_password, check_password, @@ -52,8 +54,12 @@ mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) -> {ok, #state{step = 2, get_password = GetPassword}}. mech_step(#state{step = 2} = State, ClientIn) -> - case string:tokens(ClientIn, ",") of - [CBind, UserNameAttribute, ClientNonceAttribute] when (CBind == "y") or (CBind == "n") -> + case re:split(ClientIn, ",", [{return, list}]) of + [_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _] + when ExtensionAttribute /= [] -> + {error, <<"protocol-error-extension-not-supported">>}; + [CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _] + when (CBind == "y") or (CBind == "n") -> case parse_attribute(UserNameAttribute) of {error, Reason} -> {error, Reason}; @@ -100,32 +106,36 @@ mech_step(#state{step = 4} = State, ClientIn) -> case string:tokens(ClientIn, ",") of [GS2ChannelBindingAttribute, NonceAttribute, ClientProofAttribute] -> case parse_attribute(GS2ChannelBindingAttribute) of - {$c, CVal} when (CVal == "biws") or (CVal == "eSws") -> - %% biws is base64 for n,, => channelbinding not supported - %% eSws is base64 for y,, => channelbinding supported by client only - Nonce = State#state.client_nonce ++ State#state.server_nonce, - case parse_attribute(NonceAttribute) of - {$r, CompareNonce} when CompareNonce == Nonce -> - case parse_attribute(ClientProofAttribute) of - {$p, ClientProofB64} -> - ClientProof = base64:decode(ClientProofB64), - AuthMessage = State#state.auth_message ++ "," ++ string:substr(ClientIn, 1, string:str(ClientIn, ",p=")-1), - ClientSignature = scram:client_signature(State#state.stored_key, AuthMessage), - ClientKey = scram:client_key(ClientProof, ClientSignature), - CompareStoredKey = scram:stored_key(ClientKey), - if CompareStoredKey == State#state.stored_key -> - ServerSignature = scram:server_signature(State#state.server_key, AuthMessage), - {ok, [{username, State#state.username}], "v=" ++ base64:encode_to_string(ServerSignature)}; - true -> - {error, "bad-auth"} + {$c, CVal} -> + ChannelBindingSupport = string:left(jlib:decode_base64(CVal), 1), + if (ChannelBindingSupport == "n") + or (ChannelBindingSupport == "y") -> + Nonce = State#state.client_nonce ++ State#state.server_nonce, + case parse_attribute(NonceAttribute) of + {$r, CompareNonce} when CompareNonce == Nonce -> + case parse_attribute(ClientProofAttribute) of + {$p, ClientProofB64} -> + ClientProof = base64:decode(ClientProofB64), + AuthMessage = State#state.auth_message ++ "," ++ string:substr(ClientIn, 1, string:str(ClientIn, ",p=")-1), + ClientSignature = scram:client_signature(State#state.stored_key, AuthMessage), + ClientKey = scram:client_key(ClientProof, ClientSignature), + CompareStoredKey = scram:stored_key(ClientKey), + if CompareStoredKey == State#state.stored_key -> + ServerSignature = scram:server_signature(State#state.server_key, AuthMessage), + {ok, [{username, State#state.username}], "v=" ++ base64:encode_to_string(ServerSignature)}; + true -> + {error, "bad-auth"} + end; + _Else -> + {error, "bad-protocol"} end; + {$r, _} -> + {error, "bad-nonce"}; _Else -> {error, "bad-protocol"} end; - {$r, _} -> - {error, "bad-nonce"}; - _Else -> - {error, "bad-protocol"} + true -> + {error, "bad-channel-binding"} end; _Else -> {error, "bad-protocol"} -- 1.8.1.4