Skip to content

Commit 89617b2

Browse files
authored
Merge pull request #4386 from esl/MIM-2315-fail_if_no_peer_cert
MIM-2315 Always set `fail_if_no_peer_cert` for just_tls
2 parents 6fdd662 + 7f0299a commit 89617b2

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

big_tests/tests/connect_SUITE.erl

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ groups() ->
7575
{tls, [parallel], auth_bind_pipelined_cases() ++
7676
protocol_test_cases() ++
7777
cipher_test_cases()},
78-
{just_tls, tls_groups()},
78+
{verify_peer, [], [verify_peer_disconnects_when_client_has_no_cert,
79+
verify_peer_ignores_when_client_has_no_cert]},
80+
{just_tls, [{group, verify_peer} | tls_groups()]},
7981
{fast_tls, tls_groups()},
8082
{session_replacement, [], [same_resource_replaces_session,
8183
clean_close_of_replaced_session,
@@ -207,6 +209,10 @@ end_per_testcase(replaced_session_cannot_terminate_different_nodes = CaseName, C
207209
distributed_helper:remove_node_from_cluster(mim2(), Config),
208210
mongoose_helper:restore_config(Config),
209211
escalus:end_per_testcase(CaseName, Config);
212+
end_per_testcase(verify_peer_disconnects_when_client_has_no_cert, Config) ->
213+
mongoose_helper:restore_config(Config),
214+
catch escalus_event:stop(Config),
215+
catch escalus_cleaner:stop(Config);
210216
end_per_testcase(CaseName, Config) ->
211217
mongoose_helper:restore_config(Config),
212218
escalus:end_per_testcase(CaseName, Config).
@@ -215,6 +221,46 @@ end_per_testcase(CaseName, Config) ->
215221
%% Tests
216222
%%--------------------------------------------------------------------
217223

224+
verify_peer_disconnects_when_client_has_no_cert(Config) ->
225+
%% Server disconnects only when `disconnect_on_failure` is set to `true`.
226+
%% It is true by default, so we make sure `disconnect_on_failure` is not in config.
227+
%% `verify_mode` needs to be set to `peer`.
228+
ServerTLSOpts0 = tls_opts(starttls_required, Config),
229+
ServerTLSOpts = maps:remove(disconnect_on_failure, ServerTLSOpts0#{verify_mode => peer}),
230+
configure_c2s_listener(Config, #{tls => ServerTLSOpts}),
231+
process_flag(trap_exit, true),
232+
UserSpec0 = escalus_users:get_userspec(Config, ?SECURE_USER),
233+
UserSpec = [{ssl_opts, [{verify, verify_none}]}|UserSpec0],
234+
try
235+
escalus_connection:start(UserSpec) of
236+
{error, {connection_step_failed, {{escalus_session, maybe_use_ssl}, _, _}, _}} ->
237+
ok;
238+
_Result ->
239+
error({client_connected, Config})
240+
catch
241+
C:E ->
242+
error({C, E, Config})
243+
end.
244+
245+
verify_peer_ignores_when_client_has_no_cert(Config) ->
246+
%% Server bypasses TLS client cert verification when `disconnect_on_failure` is set to `false`.
247+
ServerTLSOpts0 = tls_opts(starttls_required, Config),
248+
ServerTLSOpts = ServerTLSOpts0#{disconnect_on_failure => false},
249+
configure_c2s_listener(Config, #{tls => ServerTLSOpts}),
250+
process_flag(trap_exit, true),
251+
UserSpec0 = escalus_users:get_userspec(Config, ?SECURE_USER),
252+
UserSpec = [{ssl_opts, [{verify, verify_none}]}|UserSpec0],
253+
try
254+
escalus_connection:start(UserSpec) of
255+
{ok, _, _} ->
256+
ok;
257+
Other ->
258+
error({client_disconnected, Config, Other})
259+
catch
260+
C:E ->
261+
error({C, E, Config})
262+
end.
263+
218264
bad_xml(Config) ->
219265
%% given
220266
Spec = escalus_users:get_userspec(Config, alice),
@@ -709,7 +755,8 @@ configure_c2s_listener(Config, ExtraC2SOpts, RemovedC2SKeys) ->
709755
mongoose_helper:restart_listener(mim(), NewC2SListener).
710756

711757
tls_opts(Mode, Config) ->
712-
ExtraOpts = #{mode => Mode, cacertfile => ?CACERT_FILE, certfile => ?CERT_FILE, dhfile => ?DH_FILE},
758+
ExtraOpts = #{mode => Mode, verify_mode => none,
759+
cacertfile => ?CACERT_FILE, certfile => ?CERT_FILE, dhfile => ?DH_FILE},
713760
Module = proplists:get_value(tls_module, Config, fast_tls),
714761
maps:merge(default_c2s_tls(Module), ExtraOpts).
715762

big_tests/tests/sasl_external_SUITE.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ init_per_group(ca_signed, Config) ->
111111
{verify_mode, "\n tls.verify_mode = \"peer\""} | Config];
112112
init_per_group(self_signed, Config) ->
113113
[{signed, self},
114+
{ssl_options, "\n tls.disconnect_on_failure = false"},
114115
{verify_mode, "\n tls.verify_mode = \"selfsigned_peer\""} | Config];
115116
init_per_group(standard, Config) ->
116117
modify_config_and_restart("\"standard\"", Config),

src/just_tls.erl

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,20 @@
4141
{ok, mongoose_tls:tls_socket()} | {error, any()}.
4242
tcp_to_tls(TCPSocket, Options) ->
4343
inet:setopts(TCPSocket, [{active, false}]),
44-
{Ref, SSLOpts} = format_opts_with_ref(Options, false),
45-
Ret = case Options of
46-
#{connect := true} ->
47-
% Currently unused as ejabberd_s2s_out uses fast_tls,
48-
% and outgoing pools use Erlang SSL directly
49-
ssl:connect(TCPSocket, SSLOpts);
50-
#{} ->
51-
ssl:handshake(TCPSocket, SSLOpts, 5000)
52-
end,
53-
VerifyResults = receive_verify_results(Ref),
44+
{Ref1, Ret} = case Options of
45+
#{connect := true} ->
46+
% Currently unused as ejabberd_s2s_out uses fast_tls,
47+
% and outgoing pools use Erlang SSL directly
48+
% Do not set `fail_if_no_peer_cert_opt` for SSL client
49+
% as it is a server only option.
50+
{Ref, SSLOpts} = format_opts_with_ref(Options, false),
51+
{Ref, ssl:connect(TCPSocket, SSLOpts)};
52+
#{} ->
53+
FailIfNoPeerCert = fail_if_no_peer_cert_opt(Options),
54+
{Ref, SSLOpts} = format_opts_with_ref(Options, FailIfNoPeerCert),
55+
{Ref, ssl:handshake(TCPSocket, SSLOpts, 5000)}
56+
end,
57+
VerifyResults = receive_verify_results(Ref1),
5458
case Ret of
5559
{ok, SSLSocket} ->
5660
{ok, #tls_socket{ssl_socket = SSLSocket, verify_results = VerifyResults}};
@@ -106,13 +110,15 @@ get_peer_certificate(#tls_socket{verify_results = [Err | _]}) ->
106110
%% -callback close(tls_socket()) -> ok.
107111
close(#tls_socket{ssl_socket = SSLSocket}) -> ssl:close(SSLSocket).
108112

109-
%% @doc Prepare SSL options for direct use of ssl:connect/2 or ssl:handshake/2
110-
%% The `disconnect_on_failure' option is not supported
113+
%% @doc Prepare SSL options for direct use of ssl:connect/2 (client side)
114+
%% The `disconnect_on_failure' option is expected to be unset or true
111115
-spec make_ssl_opts(mongoose_tls:options()) -> [ssl:tls_option()].
112116
make_ssl_opts(Opts) ->
113117
{dummy_ref, SSLOpts} = format_opts_with_ref(Opts, false),
114118
SSLOpts.
115119

120+
%% @doc Prepare SSL options for direct use of ssl:handshake/2 (server side)
121+
%% The `disconnect_on_failure' option is expected to be unset or true
116122
-spec make_cowboy_ssl_opts(mongoose_tls:options()) -> [ssl:tls_option()].
117123
make_cowboy_ssl_opts(Opts) ->
118124
FailIfNoPeerCert = fail_if_no_peer_cert_opt(Opts),
@@ -156,6 +162,8 @@ error_to_list(_Error) ->
156162
verify_opt(#{verify_mode := none}) -> verify_none;
157163
verify_opt(#{}) -> verify_peer.
158164

165+
%% accept empty peer certificate if explicitly requested not to fail
166+
fail_if_no_peer_cert_opt(#{disconnect_on_failure := false}) -> false;
159167
fail_if_no_peer_cert_opt(#{verify_mode := peer}) -> true;
160168
fail_if_no_peer_cert_opt(#{verify_mode := selfsigned_peer}) -> true;
161169
fail_if_no_peer_cert_opt(#{}) -> false.

0 commit comments

Comments
 (0)