Description: Fix parsing SCRAM optional parameters 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. . This patch is a backport of changes introduced by the commit 9e9b0eae802ee0508db6780426954efd048e7976 in the upstream Git repository to the ejabberd code base as of version 2.1.10. Author: Stephen Röttger Forwarded: not-needed Bug: https://support.process-one.net/browse/EJAB-1632 Last-Update: 2013-03-25 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- 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 @@ {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 @@ 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"}