Skip to content

Commit 8c1920d

Browse files
committed
Improve handling of c=y sasl flag when plus variants are disabled
Previously we aborted connection with that flag unless client used SASL2 and allow_unencrypted_sasl2 was enabled. Now in addition to this option, we will also accept connection if admin disabled all plus mechanisms, or in SASL2 case, if filtering out unavailable methods based on stored user passwords we removed all -PLUS mechanisms.
1 parent c3aa211 commit 8c1920d

8 files changed

Lines changed: 51 additions & 37 deletions

src/xmpp_sasl.erl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
-module(xmpp_sasl).
1919
-author('alexey@process-one.net').
2020

21-
-export([server_new/5, server_start/6, server_step/2,
21+
-export([server_new/5, server_start/7, server_step/2,
2222
listmech/0, format_error/2]).
2323

2424
%% TODO: write correct types for these callbacks
@@ -60,7 +60,7 @@
6060
-export_type([mechanism/0, error_reason/0,
6161
sasl_state/0, sasl_return/0, sasl_property/0]).
6262

63-
-callback mech_new(binary(), channel_bindings(), list(binary()),
63+
-callback mech_new(binary(), channel_bindings(), boolean(), list(binary()),
6464
binary(), binary(), map()) -> mech_state().
6565
-callback mech_step(mech_state(), binary()) -> sasl_return().
6666

@@ -105,14 +105,14 @@ server_new(ServerHost, GetPassword, CheckPassword, CheckPasswordDigest, GetFastT
105105
check_password => CheckPassword,
106106
check_password_digest => CheckPasswordDigest}}.
107107

108-
-spec server_start(sasl_state(), mechanism(), binary(), channel_bindings(),
108+
-spec server_start(sasl_state(), mechanism(), binary(), channel_bindings(), boolean(),
109109
list(binary()) | undefined | not_available, binary() | undefined) -> sasl_return().
110-
server_start(State, Mech, ClientIn, ChannelBindings, Mechs, UAId) ->
110+
server_start(State, Mech, ClientIn, ChannelBindings, PlusDisabled, Mechs, UAId) ->
111111
case get_mod(Mech) of
112112
undefined ->
113113
{error, unsupported_mechanism, <<"">>};
114114
Module ->
115-
MechState = Module:mech_new(Mech, ChannelBindings, Mechs, UAId,
115+
MechState = Module:mech_new(Mech, ChannelBindings, PlusDisabled, Mechs, UAId,
116116
State#sasl_state.server_host,
117117
State#sasl_state.callbacks),
118118
State1 = State#sasl_state{mech_name = Mech,

src/xmpp_sasl_anonymous.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
-behaviour(xmpp_sasl).
2121
-protocol({xep, 175, '1.2'}).
2222

23-
-export([mech_new/6, mech_step/2]).
23+
-export([mech_new/7, mech_step/2]).
2424

2525
-record(state, {server = <<"">> :: binary()}).
2626

27-
mech_new(_Mech, _CB, _Mechs, _UAId, Host, _Callbacks) ->
27+
mech_new(_Mech, _CB, _PD, _Mechs, _UAId, Host, _Callbacks) ->
2828
#state{server = Host}.
2929

3030
mech_step(#state{}, _ClientIn) ->

src/xmpp_sasl_digest.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
-author('alexey@sevcom.net').
2121
-dialyzer({no_match, [get_local_fqdn/1]}).
2222

23-
-export([mech_new/6, mech_step/2, format_error/1]).
23+
-export([mech_new/7, mech_step/2, format_error/1]).
2424
%% For tests
2525
-export([parse/1]).
2626

@@ -58,7 +58,7 @@ format_error(not_authorized) ->
5858
format_error(unexpected_response) ->
5959
{'not-authorized', <<"Unexpected response">>}.
6060

61-
mech_new(_Mech, _CB, _Mechs, _UAId, Host, #{get_password := GetPassword,
61+
mech_new(_Mech, _CB, _PD, _Mechs, _UAId, Host, #{get_password := GetPassword,
6262
check_password_digest := CheckPasswordDigest}) ->
6363
#state{step = 1, nonce = p1_rand:get_string(),
6464
host = Host, hostfqdn = get_local_fqdn(Host),

src/xmpp_sasl_fast.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
-behaviour(xmpp_sasl).
2020
-author('alexey@process-one.net').
2121

22-
-export([mech_new/6, mech_step/2, format_error/1]).
22+
-export([mech_new/7, mech_step/2, format_error/1]).
2323
%% For tests
2424
-export([parse/1]).
2525

@@ -34,15 +34,15 @@ format_error({Condition, Text}) ->
3434
format_error(incompatible_mechs) ->
3535
{'not-authorized', <<"Incompatible SCRAM methods">>};
3636
format_error(missing_ua) ->
37-
{'malformed-request', <<"Missing user-agent">>};
37+
{'malformed', <<"Missing user-agent">>};
3838
format_error(bad_channel_binding) ->
3939
{'not-authorized', <<"Invalid channel binding">>};
4040
format_error(parser_failed) ->
4141
{'not-authorized', <<"Response decoding failed">>};
4242
format_error(not_authorized) ->
4343
{'not-authorized', <<"Invalid username or password">>}.
4444

45-
mech_new(Mech, CB, _Mechs, UAId, _Host, #{get_fast_tokens := GetFastTokens}) ->
45+
mech_new(Mech, CB, _PD, _Mechs, UAId, _Host, #{get_fast_tokens := GetFastTokens}) ->
4646
#state{mech = Mech, cb = CB, ua = UAId, get_fast_tokens = GetFastTokens}.
4747

4848

src/xmpp_sasl_oauth.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
-behaviour(xmpp_sasl).
2020
-author('alexey@process-one.net').
2121

22-
-export([mech_new/6, mech_step/2, format_error/1]).
22+
-export([mech_new/7, mech_step/2, format_error/1]).
2323
%% For tests
2424
-export([parse/1]).
2525

@@ -34,7 +34,7 @@ format_error(parser_failed) ->
3434
format_error(not_authorized) ->
3535
{'not-authorized', <<"Invalid token">>}.
3636

37-
mech_new(_Mech, _CB, _Mechs, _UAId, Host, #{check_password := CheckPassword}) ->
37+
mech_new(_Mech, _CB, _PD, _Mechs, _UAId, Host, #{check_password := CheckPassword}) ->
3838
#state{host = Host, check_password = CheckPassword}.
3939

4040
mech_step(State, ClientIn) ->

src/xmpp_sasl_plain.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
-behaviour(xmpp_sasl).
2020
-author('alexey@process-one.net').
2121

22-
-export([mech_new/6, mech_step/2, format_error/1]).
22+
-export([mech_new/7, mech_step/2, format_error/1]).
2323
%% For tests
2424
-export([parse/1]).
2525

@@ -35,7 +35,7 @@ format_error(parser_failed) ->
3535
format_error(not_authorized) ->
3636
{'not-authorized', <<"Invalid username or password">>}.
3737

38-
mech_new(_Mech, _CB, _Mechs, _UAId, _Host, #{check_password := CheckPassword}) ->
38+
mech_new(_Mech, _CB, _PD, _Mechs, _UAId, _Host, #{check_password := CheckPassword}) ->
3939
#state{check_password = CheckPassword}.
4040

4141
mech_step(State, ClientIn) ->

src/xmpp_sasl_scram.erl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
-protocol({rfc, 5802}).
2323
-protocol({xep, 474, '0.4.0'}).
2424

25-
-export([mech_new/6, mech_step/2, format_error/1]).
25+
-export([mech_new/7, mech_step/2, format_error/1]).
2626

2727
-include("scram.hrl").
2828

@@ -33,6 +33,7 @@
3333
{step = 2 :: 2 | 4,
3434
algo = sha :: sha | sha256 | sha512,
3535
channel_bindings = none :: none | not_available | #{atom() => binary()},
36+
plus_disabled = false :: boolean(),
3637
ssdp :: undefined | binary(),
3738
stored_key = <<"">> :: binary(),
3839
server_key = <<"">> :: binary(),
@@ -76,7 +77,7 @@ format_error(bad_channel_binding) ->
7677
format_error(incompatible_mechs) ->
7778
{'not-authorized', <<"Incompatible SCRAM methods">>}.
7879

79-
mech_new(Mech, ChannelBindings, Mechs, _UAId, _Host, #{get_password := GetPassword}) ->
80+
mech_new(Mech, ChannelBindings, PlusDisabled, Mechs, _UAId, _Host, #{get_password := GetPassword}) ->
8081
NCB = case ChannelBindings of
8182
#{} -> none;
8283
_ -> ChannelBindings
@@ -103,7 +104,7 @@ mech_new(Mech, ChannelBindings, Mechs, _UAId, _Host, #{get_password := GetPasswo
103104
end]))
104105
end,
105106
#state{step = 2, get_password = GetPassword, algo = Algo,
106-
channel_bindings = CB, ssdp = Ssdp}.
107+
channel_bindings = CB, plus_disabled = PlusDisabled, ssdp = Ssdp}.
107108

108109
mech_step(_State, ClientIn) when not is_binary(ClientIn) ->
109110
{error, parser_failed};
@@ -281,6 +282,8 @@ cbind_check(#state{channel_bindings = #{}}, _) ->
281282
false;
282283
cbind_check(#state{channel_bindings = not_available}, <<"y", _/binary>>) ->
283284
true;
285+
cbind_check(#state{plus_disabled = true}, <<"y", _/binary>>) ->
286+
true;
284287
cbind_check(_, <<"y", _/binary>>) ->
285288
false;
286289
cbind_check(_, <<"n", _/binary>>) ->

src/xmpp_stream_in.erl

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ init_channel_bindings(#{socket := Socket} = State) ->
965965
process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn},
966966
#{lserver := LServer} = State) ->
967967
State1 = State#{sasl_mech => Mech},
968-
Mechs = get_sasl_mechanisms(State1, sasl),
968+
{Mechs, PlusDisabled} = get_sasl_mechanisms(State1, sasl),
969969
case lists:member(Mech, Mechs) of
970970
true when Mech == <<"EXTERNAL">> ->
971971
Res = case xmpp_stream_pkix:authenticate(State1, ClientIn) of
@@ -989,7 +989,7 @@ process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn},
989989
end,
990990
SASLState = xmpp_sasl:server_new(LServer, GetPW, CheckPW, CheckPWDigest, undefined),
991991
CB = maps:get(sasl_channel_bindings, State1, none),
992-
Res = xmpp_sasl:server_start(SASLState, Mech, ClientIn, CB, Mechs2, undefined),
992+
Res = xmpp_sasl:server_start(SASLState, Mech, ClientIn, CB, PlusDisabled, Mechs2, undefined),
993993
process_sasl_result(Res, disable_sasl2(State1#{sasl_state => SASLState}));
994994
false ->
995995
process_sasl_result({error, unsupported_mechanism, <<"">>}, disable_sasl2(State1))
@@ -1078,7 +1078,7 @@ process_sasl2_request(#sasl2_authenticate{mechanism = Mech, initial_response = C
10781078
FastMechs = try callback(fast_mechanisms, State)
10791079
catch _:{?MODULE, undef} -> []
10801080
end,
1081-
Mechs = get_sasl_mechanisms(State1, sasl2),
1081+
{Mechs, PlusDisabled} = get_sasl_mechanisms(State1, sasl2),
10821082
MechsAll = Mechs ++ FastMechs,
10831083
UAId = case UA of
10841084
#sasl2_user_agent{id = ID} when ID /= <<>> ->
@@ -1116,7 +1116,7 @@ process_sasl2_request(#sasl2_authenticate{mechanism = Mech, initial_response = C
11161116
SASLState = xmpp_sasl:server_new(LServer, GetPW, CheckPW,
11171117
CheckPWDigest, GetFastTokens),
11181118
CB = maps:get(sasl_channel_bindings, State1, none),
1119-
Res = xmpp_sasl:server_start(SASLState, Mech, ClientIn, CB, Mechs2, UAId),
1119+
Res = xmpp_sasl:server_start(SASLState, Mech, ClientIn, CB, PlusDisabled, Mechs2, UAId),
11201120
process_sasl2_result(Res, State1#{sasl_state => SASLState,
11211121
sasl2_inline_els => SaslInline,
11221122
sasl2_ua_id => UAId});
@@ -1414,7 +1414,7 @@ get_fast_tokens_fun(Mech, State) ->
14141414
catch _:{?MODULE, undef} -> fun(_, _) -> [] end
14151415
end.
14161416

1417-
-spec get_sasl_mechanisms(state(), sasl | sasl2) -> [xmpp_sasl:mechanism()].
1417+
-spec get_sasl_mechanisms(state(), sasl | sasl2) -> {[xmpp_sasl:mechanism()], boolean()}.
14181418
get_sasl_mechanisms(#{stream_encrypted := Encrypted,
14191419
xmlns := NS} = State, Type) ->
14201420
Mechs = if NS == ?NS_CLIENT -> xmpp_sasl:listmech();
@@ -1423,15 +1423,18 @@ get_sasl_mechanisms(#{stream_encrypted := Encrypted,
14231423
Mechs1 = if Encrypted -> [<<"EXTERNAL">> | Mechs];
14241424
true -> Mechs
14251425
end,
1426-
Mechs2 = try callback(sasl_mechanisms, Mechs1, State) of
1427-
{Sasl1, _} when Type == sasl -> Sasl1;
1428-
{_, Sasl2} when Type == sasl2 -> Sasl2;
1429-
Common -> Common
1430-
catch _:{?MODULE, undef} -> Mechs1
1431-
end,
1426+
{Mechs2, PlusDisabled} =
1427+
try callback(sasl_mechanisms, Mechs1, State) of
1428+
{Sasl1, _} when Type == sasl -> {Sasl1, false};
1429+
{_, Sasl2} when Type == sasl2 -> {Sasl2, false};
1430+
{Sasl1, _, Disabled} when Type == sasl -> {Sasl1, Disabled};
1431+
{_, Sasl2, Disabled} when Type == sasl2 -> {Sasl2, Disabled};
1432+
Common -> {Common, false}
1433+
catch _:{?MODULE, undef} -> {Mechs1, false}
1434+
end,
14321435
if Type == sasl2 ->
1433-
filter_sasl2_user_mechs(Mechs2, State);
1434-
true -> Mechs2
1436+
filter_sasl2_user_mechs(Mechs2, PlusDisabled, State);
1437+
true -> {Mechs2, PlusDisabled}
14351438
end.
14361439

14371440
pass_to_mech(_, all) -> all;
@@ -1444,16 +1447,24 @@ pass_to_mech(#scram{hash = sha512}, Acc) -> [<<"SCRAM-SHA-512">>, <<"SCRAM-SHA-5
14441447
pass_to_mech(List, Acc) when is_list(List) ->
14451448
lists:foldl(fun pass_to_mech/2, Acc, List).
14461449

1447-
filter_sasl2_user_mechs(Mechs, State) ->
1450+
filter_sasl2_user_mechs(Mechs, PlusDisabled, State) ->
14481451
{Pass, _} = (get_password_fun_sasl2(<<>>, State))(<<>>),
14491452
case pass_to_mech(Pass, []) of
1450-
all -> Mechs;
1453+
all -> {Mechs, PlusDisabled};
14511454
M ->
14521455
OtherMechs = Mechs -- [<<"SCRAM-SHA-1">>, <<"SCRAM-SHA-1-PLUS">>,
14531456
<<"SCRAM-SHA-256">>, <<"SCRAM-SHA-256-PLUS">>,
14541457
<<"SCRAM-SHA-512">>, <<"SCRAM-SHA-512-PLUS">>],
14551458
ScramMechs = Mechs -- OtherMechs,
1456-
OtherMechs ++ (ScramMechs -- (ScramMechs -- M))
1459+
Mechs2 = OtherMechs ++ (ScramMechs -- (ScramMechs -- M)),
1460+
case PlusDisabled of
1461+
true -> {Mechs2, true};
1462+
_ ->
1463+
PlusMechs = [<<"SCRAM-SHA-1-PLUS">>, <<"SCRAM-SHA-256-PLUS">>, <<"SCRAM-SHA-512-PLUS">>],
1464+
HadPlusMechs = ScramMechs -- PlusMechs /= ScramMechs,
1465+
HavePlusMechs = PlusMechs -- Mechs2 == PlusMechs,
1466+
{Mechs2, HadPlusMechs andalso not HavePlusMechs}
1467+
end
14571468
end.
14581469

14591470

@@ -1463,7 +1474,7 @@ get_sasl_feature(#{stream_authenticated := false,
14631474
TLSRequired = is_starttls_required(State),
14641475
if
14651476
Encrypted or not TLSRequired ->
1466-
Mechs = get_sasl_mechanisms(State, sasl),
1477+
{Mechs, _} = get_sasl_mechanisms(State, sasl),
14671478
[#sasl_mechanisms{list = Mechs}] ++
14681479
case maps:get(sasl_channel_bindings, State, none) of
14691480
none -> [];
@@ -1478,7 +1489,7 @@ get_sasl_feature(_) ->
14781489

14791490
-spec get_sasl2_feature(state()) -> [sasl2_authenticaton() | sasl_channel_binding()].
14801491
get_sasl2_feature(#{stream_authenticated := false} = State) ->
1481-
Mechs = get_sasl_mechanisms(State, sasl2),
1492+
{Mechs, _} = get_sasl_mechanisms(State, sasl2),
14821493

14831494
{SASL2Features, Bind2Features, ExtraFeatures} =
14841495
try callback(inline_stream_features, State)

0 commit comments

Comments
 (0)