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.
This commit is contained in:
Stephen Röttger 2013-04-17 12:34:53 +02:00 committed by Badlop
parent c0afb1f282
commit 9fa415e557
1 changed files with 43 additions and 36 deletions

View File

@ -32,6 +32,8 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl").
-behaviour(cyrsasl). -behaviour(cyrsasl).
-record(state, -record(state,
@ -60,8 +62,11 @@ mech_new(_Host, GetPassword, _CheckPassword,
{ok, #state{step = 2, get_password = GetPassword}}. {ok, #state{step = 2, get_password = GetPassword}}.
mech_step(#state{step = 2} = State, ClientIn) -> mech_step(#state{step = 2} = State, ClientIn) ->
case str:tokens(ClientIn, <<",">>) of case re:split(ClientIn, <<",">>, [{return, binary}]) of
[CBind, UserNameAttribute, ClientNonceAttribute] [_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _]
when ExtensionAttribute /= [] ->
{error, <<"protocol-error-extension-not-supported">>};
[CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _]
when (CBind == <<"y">>) or (CBind == <<"n">>) -> when (CBind == <<"y">>) or (CBind == <<"n">>) ->
case parse_attribute(UserNameAttribute) of case parse_attribute(UserNameAttribute) of
{error, Reason} -> {error, Reason}; {error, Reason} -> {error, Reason};
@ -123,42 +128,44 @@ mech_step(#state{step = 4} = State, ClientIn) ->
[GS2ChannelBindingAttribute, NonceAttribute, [GS2ChannelBindingAttribute, NonceAttribute,
ClientProofAttribute] -> ClientProofAttribute] ->
case parse_attribute(GS2ChannelBindingAttribute) of case parse_attribute(GS2ChannelBindingAttribute) of
{$c, CVal} when (CVal == <<"biws">>) or (CVal == <<"eSws">>) -> {$c, CVal} ->
%% biws is base64 for n,, => channelbinding not supported ChannelBindingSupport = binary:at(jlib:decode_base64(CVal), 0),
%% eSws is base64 for y,, => channelbinding supported by client only if (ChannelBindingSupport == $n)
Nonce = <<(State#state.client_nonce)/binary, or (ChannelBindingSupport == $y) ->
(State#state.server_nonce)/binary>>, Nonce = <<(State#state.client_nonce)/binary,
case parse_attribute(NonceAttribute) of (State#state.server_nonce)/binary>>,
{$r, CompareNonce} when CompareNonce == Nonce -> case parse_attribute(NonceAttribute) of
case parse_attribute(ClientProofAttribute) of {$r, CompareNonce} when CompareNonce == Nonce ->
{$p, ClientProofB64} -> case parse_attribute(ClientProofAttribute) of
ClientProof = jlib:decode_base64(ClientProofB64), {$p, ClientProofB64} ->
AuthMessage = ClientProof = jlib:decode_base64(ClientProofB64),
iolist_to_binary( AuthMessage = iolist_to_binary(
[State#state.auth_message, [State#state.auth_message,
",", ",",
str:substr(ClientIn, 1, str:substr(ClientIn, 1,
str:str(ClientIn, <<",p=">>) str:str(ClientIn, <<",p=">>)
- 1)]), - 1)]),
ClientSignature = ClientSignature =
scram:client_signature(State#state.stored_key, scram:client_signature(State#state.stored_key,
AuthMessage), AuthMessage),
ClientKey = scram:client_key(ClientProof, ClientKey = scram:client_key(ClientProof,
ClientSignature), ClientSignature),
CompareStoredKey = scram:stored_key(ClientKey), CompareStoredKey = scram:stored_key(ClientKey),
if CompareStoredKey == State#state.stored_key -> if CompareStoredKey == State#state.stored_key ->
ServerSignature = ServerSignature =
scram:server_signature(State#state.server_key, scram:server_signature(State#state.server_key,
AuthMessage), AuthMessage),
{ok, [{username, State#state.username}], {ok, [{username, State#state.username}],
<<"v=", <<"v=",
(jlib:encode_base64(ServerSignature))/binary>>}; (jlib:encode_base64(ServerSignature))/binary>>};
true -> {error, <<"bad-auth">>} true -> {error, <<"bad-auth">>}
end;
_Else -> {error, <<"bad-protocol">>}
end; end;
{$r, _} -> {error, <<"bad-nonce">>};
_Else -> {error, <<"bad-protocol">>} _Else -> {error, <<"bad-protocol">>}
end; end;
{$r, _} -> {error, <<"bad-nonce">>}; true -> {error, <<"bad-channel-binding">>}
_Else -> {error, <<"bad-protocol">>}
end; end;
_Else -> {error, <<"bad-protocol">>} _Else -> {error, <<"bad-protocol">>}
end; end;