fix(tls): fix incompatible tls options and issue with tls version gap

This commit is contained in:
Ivan Dyachkov 2023-06-12 18:56:48 +02:00 committed by Serge Tupchii
parent 267053cc35
commit 6d88cd7a20
1 changed files with 80 additions and 17 deletions

View File

@ -478,7 +478,7 @@ to_server_opts(Type, Opts) ->
Versions = integral_versions(Type, maps:get(versions, Opts, undefined)), Versions = integral_versions(Type, maps:get(versions, Opts, undefined)),
Ciphers = integral_ciphers(Versions, maps:get(ciphers, Opts, undefined)), Ciphers = integral_ciphers(Versions, maps:get(ciphers, Opts, undefined)),
Path = fun(Key) -> resolve_cert_path_for_read_strict(maps:get(Key, Opts, undefined)) end, Path = fun(Key) -> resolve_cert_path_for_read_strict(maps:get(Key, Opts, undefined)) end,
filter( ensure_valid_options(
maps:to_list(Opts#{ maps:to_list(Opts#{
keyfile => Path(keyfile), keyfile => Path(keyfile),
certfile => Path(certfile), certfile => Path(certfile),
@ -511,7 +511,7 @@ to_client_opts(Type, Opts) ->
SNI = ensure_sni(Get(server_name_indication)), SNI = ensure_sni(Get(server_name_indication)),
Versions = integral_versions(Type, Get(versions)), Versions = integral_versions(Type, Get(versions)),
Ciphers = integral_ciphers(Versions, Get(ciphers)), Ciphers = integral_ciphers(Versions, Get(ciphers)),
filter( ensure_valid_options(
[ [
{keyfile, KeyFile}, {keyfile, KeyFile},
{certfile, CertFile}, {certfile, CertFile},
@ -556,33 +556,96 @@ resolve_cert_path_for_read_strict(Path) ->
resolve_cert_path_for_read(Path) -> resolve_cert_path_for_read(Path) ->
emqx_schema:naive_env_interpolation(Path). emqx_schema:naive_env_interpolation(Path).
filter([], _) -> ensure_valid_options(Options, Versions0) ->
[]; Versions = validate_version_gap(Versions0),
filter([{_, undefined} | T], Versions) -> ensure_valid_options(Options, Versions, []).
filter(T, Versions);
filter([{_, ""} | T], Versions) -> %% See also lib/ssl/src/ssl.erl#L2617.
filter(T, Versions); %% Do not allow configuration of TLS 1.3 with a gap where TLS 1.2 is not supported
filter([{K, V} | T], Versions) -> %% as that configuration can trigger the built in version downgrade protection
%% mechanism and the handshake can fail with an Illegal Parameter alert.
validate_version_gap(Versions) ->
case lists:member('tlsv1.3', Versions) of
true when length(Versions) >= 2 ->
case lists:member('tlsv1.2', Versions) of
true ->
Versions;
false ->
NewVersions = ['tlsv1.3'],
?SLOG(warning, #{
msg => "tlsv13_version_gap",
versions => Versions,
new_versions => NewVersions
}),
NewVersions
end;
_ ->
Versions
end.
ensure_valid_options([], _, Acc) ->
lists:reverse(Acc);
ensure_valid_options([{_, undefined} | T], Versions, Acc) ->
ensure_valid_options(T, Versions, Acc);
ensure_valid_options([{_, ""} | T], Versions, Acc) ->
ensure_valid_options(T, Versions, Acc);
ensure_valid_options([{K, V} | T], Versions, Acc) ->
case tls_option_compatible_versions(K) of case tls_option_compatible_versions(K) of
all -> all ->
[{K, V} | filter(T, Versions)]; ensure_valid_options(T, Versions, [{K, V} | Acc]);
CompatibleVersions -> CompatibleVersions ->
case CompatibleVersions -- (CompatibleVersions -- Versions) of Enabled = sets:from_list(Versions),
[] -> Compatible = sets:from_list(CompatibleVersions),
filter(T, Versions); case sets:size(sets:intersection(Enabled, Compatible)) > 0 of
_ -> true ->
[{K, V} | filter(T, Versions)] ensure_valid_options(T, Versions, [{K, V} | Acc]);
false ->
?SLOG(warning, #{
msg => "drop_incompatible_tls_option", option => K, versions => Versions
}),
ensure_valid_options(T, Versions, Acc)
end end
end. end.
%% see lib/ssl/src/ssl.erl, assert_option_dependency/4
tls_option_compatible_versions(beast_mitigation) ->
[dtlsv1, 'tlsv1'];
tls_option_compatible_versions(padding_check) ->
[dtlsv1, 'tlsv1'];
tls_option_compatible_versions(client_renegotiation) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(reuse_session) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(reuse_sessions) -> tls_option_compatible_versions(reuse_sessions) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2']; [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(secure_renegotiate) -> tls_option_compatible_versions(secure_renegotiate) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2']; [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(next_protocol_advertised) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(client_preferred_next_protocols) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(psk_identity) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(srp_identity) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(user_lookup_fun) -> tls_option_compatible_versions(user_lookup_fun) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2']; [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
tls_option_compatible_versions(client_renegotiation) -> tls_option_compatible_versions(early_data) ->
[dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2']; ['tlsv1.3'];
tls_option_compatible_versions(certificate_authorities) ->
['tlsv1.3'];
tls_option_compatible_versions(cookie) ->
['tlsv1.3'];
tls_option_compatible_versions(key_update_at) ->
['tlsv1.3'];
tls_option_compatible_versions(anti_replay) ->
['tlsv1.3'];
tls_option_compatible_versions(session_tickets) ->
['tlsv1.3'];
tls_option_compatible_versions(supported_groups) ->
['tlsv1.3'];
tls_option_compatible_versions(use_ticket) ->
['tlsv1.3'];
tls_option_compatible_versions(_) -> tls_option_compatible_versions(_) ->
all. all.