1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2019-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22
23-module(ssl_renegotiate_SUITE).
24
25-behaviour(ct_suite).
26
27-include_lib("common_test/include/ct.hrl").
28-include_lib("public_key/include/public_key.hrl").
29
30%% Common test
31-export([all/0,
32         groups/0,
33         init_per_suite/1,
34         init_per_group/2,
35         init_per_testcase/2,
36         end_per_suite/1,
37         end_per_group/2,
38         end_per_testcase/2
39        ]).
40
41%% Test cases
42-export([client_renegotiate/0,
43         client_renegotiate/1,
44         server_renegotiate/0,
45         server_renegotiate/1,
46         client_secure_renegotiate/0,
47         client_secure_renegotiate/1,
48         client_secure_renegotiate_fallback/0,
49         client_secure_renegotiate_fallback/1,
50         client_renegotiate_reused_session/0,
51         client_renegotiate_reused_session/1,
52         server_renegotiate_reused_session/0,
53         server_renegotiate_reused_session/1,
54         client_no_wrap_sequence_number/0,
55         client_no_wrap_sequence_number/1,
56         server_no_wrap_sequence_number/0,
57         server_no_wrap_sequence_number/1,
58         renegotiate_dos_mitigate_active/0,
59         renegotiate_dos_mitigate_active/1,
60         renegotiate_dos_mitigate_passive/0,
61         renegotiate_dos_mitigate_passive/1,
62         renegotiate_dos_mitigate_absolute/0,
63         renegotiate_dos_mitigate_absolute/1
64        ]).
65
66%% Apply export
67-export([renegotiate/2,
68         renegotiate_reuse_session/2,
69         renegotiate_immediately/1,
70         renegotiate_rejected/1,
71         erlang_ssl_receive/2]).
72
73-define(SLEEP, 500).
74-define(RENEGOTIATION_DISABLE_TIME, 12000).
75
76%%--------------------------------------------------------------------
77%% Common Test interface functions -----------------------------------
78%%--------------------------------------------------------------------
79all() ->
80    [
81     {group, 'tlsv1.2'},
82     {group, 'tlsv1.1'},
83     {group, 'tlsv1'},
84     {group, 'dtlsv1.2'},
85     {group, 'dtlsv1'}
86    ].
87
88groups() ->
89    [{'dtlsv1.2', [], renegotiate_tests()},
90     {'dtlsv1', [], renegotiate_tests()},
91     {'tlsv1.3', [], renegotiate_tests()},
92     {'tlsv1.2', [], renegotiate_tests()},
93     {'tlsv1.1', [], renegotiate_tests()},
94     {'tlsv1', [], renegotiate_tests()}
95    ].
96
97renegotiate_tests() ->
98    [client_renegotiate,
99     server_renegotiate,
100     client_secure_renegotiate,
101     client_secure_renegotiate_fallback,
102     client_renegotiate_reused_session,
103     server_renegotiate_reused_session,
104     client_no_wrap_sequence_number,
105     server_no_wrap_sequence_number,
106     renegotiate_dos_mitigate_active,
107     renegotiate_dos_mitigate_passive,
108     renegotiate_dos_mitigate_absolute].
109
110ssl3_renegotiate_tests() ->
111    [client_renegotiate,
112     server_renegotiate,
113     client_renegotiate_reused_session,
114     server_renegotiate_reused_session,
115     client_no_wrap_sequence_number,
116     server_no_wrap_sequence_number,
117     renegotiate_dos_mitigate_active,
118     renegotiate_dos_mitigate_passive,
119     renegotiate_dos_mitigate_absolute].
120
121init_per_suite(Config) ->
122    catch crypto:stop(),
123    try crypto:start() of
124	ok ->
125	    ssl_test_lib:clean_start(),
126            ssl_test_lib:make_rsa_cert(Config)
127    catch _:_ ->
128	    {skip, "Crypto did not start"}
129    end.
130
131end_per_suite(_Config) ->
132    ssl:stop(),
133    application:stop(crypto).
134
135init_per_group(GroupName, Config) ->
136    ssl_test_lib:init_per_group(GroupName, Config).
137
138end_per_group(GroupName, Config) ->
139  ssl_test_lib:end_per_group(GroupName, Config).
140
141init_per_testcase(TestCase, Config)  when TestCase == renegotiate_dos_mitigate_active;
142                                          TestCase == renegotiate_dos_mitigate_passive;
143                                          TestCase == renegotiate_dos_mitigate_absolute ->
144    ct:timetrap({seconds, 160}),
145    Config;
146init_per_testcase(_, Config) ->
147    ct:timetrap({seconds, 15}),
148    Config.
149
150end_per_testcase(_, Config) ->
151    Config.
152
153%%--------------------------------------------------------------------
154%% Test Cases --------------------------------------------------------
155%%--------------------------------------------------------------------
156client_renegotiate() ->
157    [{doc,"Test ssl:renegotiate/1 on client."}].
158client_renegotiate(Config) when is_list(Config) ->
159    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
160    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
161
162    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
163
164    Data = "From erlang to erlang",
165
166    Server =
167	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
168				   {from, self()},
169				   {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
170				   {options, ServerOpts}]),
171    Port = ssl_test_lib:inet_port(Server),
172
173    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
174					{host, Hostname},
175					{from, self()},
176					{mfa, {?MODULE,
177					       renegotiate, [Data]}},
178					{options, [{reuse_sessions, false} | ClientOpts]}]),
179
180    ssl_test_lib:check_result(Client, ok, Server, ok),
181    ssl_test_lib:close(Server),
182    ssl_test_lib:close(Client).
183
184%%--------------------------------------------------------------------
185client_secure_renegotiate() ->
186    [{doc,"Test ssl:renegotiate/1 on client."}].
187client_secure_renegotiate(Config) when is_list(Config) ->
188    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
189    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
190
191    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
192
193    Data = "From erlang to erlang",
194
195    Server =
196	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
197				   {from, self()},
198				   {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
199				   {options, [{secure_renegotiate, true} | ServerOpts]}]),
200    Port = ssl_test_lib:inet_port(Server),
201
202    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
203					{host, Hostname},
204					{from, self()},
205					{mfa, {?MODULE,
206					       renegotiate, [Data]}},
207					{options, [{reuse_sessions, false},
208						   {secure_renegotiate, true}| ClientOpts]}]),
209
210    ssl_test_lib:check_result(Client, ok, Server, ok),
211    ssl_test_lib:close(Server),
212    ssl_test_lib:close(Client).
213
214%%--------------------------------------------------------------------
215client_secure_renegotiate_fallback() ->
216    [{doc,"Test that we can set secure_renegotiate to false that is "
217      "fallback option, we however do not have a insecure server to test against!"}].
218client_secure_renegotiate_fallback(Config) when is_list(Config) ->
219    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
220    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
221
222    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
223
224    Data = "From erlang to erlang",
225
226    Server =
227	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
228				   {from, self()},
229				   {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
230				   {options, [{secure_renegotiate, false} | ServerOpts]}]),
231    Port = ssl_test_lib:inet_port(Server),
232
233    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
234					{host, Hostname},
235					{from, self()},
236					{mfa, {?MODULE,
237					       renegotiate, [Data]}},
238					{options, [{reuse_sessions, false},
239						   {secure_renegotiate, false}| ClientOpts]}]),
240
241    ssl_test_lib:check_result(Client, ok, Server, ok),
242    ssl_test_lib:close(Server),
243    ssl_test_lib:close(Client).
244
245%%--------------------------------------------------------------------
246server_renegotiate() ->
247    [{doc,"Test ssl:renegotiate/1 on server."}].
248server_renegotiate(Config) when is_list(Config) ->
249    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
250    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
251
252    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
253
254    Data = "From erlang to erlang",
255
256    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
257					{from, self()},
258					{mfa, {?MODULE,
259					       renegotiate, [Data]}},
260					{options, ServerOpts}]),
261    Port = ssl_test_lib:inet_port(Server),
262
263    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
264					{host, Hostname},
265					{from, self()},
266					{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
267					{options, [{reuse_sessions, false} | ClientOpts]}]),
268
269    ssl_test_lib:check_result(Server, ok, Client, ok),
270    ssl_test_lib:close(Server),
271    ssl_test_lib:close(Client).
272
273%%--------------------------------------------------------------------
274client_renegotiate_reused_session() ->
275    [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}].
276client_renegotiate_reused_session(Config) when is_list(Config) ->
277    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
278    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
279
280    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
281
282    Data = "From erlang to erlang",
283
284    Server =
285	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
286				   {from, self()},
287				   {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
288				   {options, ServerOpts}]),
289    Port = ssl_test_lib:inet_port(Server),
290
291    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
292					{host, Hostname},
293					{from, self()},
294					{mfa, {?MODULE,
295					       renegotiate_reuse_session, [Data]}},
296					{options, [{reuse_sessions, true} | ClientOpts]}]),
297
298    ssl_test_lib:check_result(Client, ok, Server, ok),
299    ssl_test_lib:close(Server),
300    ssl_test_lib:close(Client).
301%%--------------------------------------------------------------------
302server_renegotiate_reused_session() ->
303    [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}].
304server_renegotiate_reused_session(Config) when is_list(Config) ->
305    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
306    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
307
308    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
309
310    Data = "From erlang to erlang",
311
312    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
313					{from, self()},
314					{mfa, {?MODULE,
315					       renegotiate_reuse_session, [Data]}},
316					{options, ServerOpts}]),
317    Port = ssl_test_lib:inet_port(Server),
318
319    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
320					{host, Hostname},
321					{from, self()},
322					{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
323					{options, [{reuse_sessions, true} | ClientOpts]}]),
324
325    ssl_test_lib:check_result(Server, ok, Client, ok),
326    ssl_test_lib:close(Server),
327    ssl_test_lib:close(Client).
328%%--------------------------------------------------------------------
329client_no_wrap_sequence_number() ->
330    [{doc,"Test that erlang client will renegotiate session when",
331     "max sequence number celing is about to be reached. Although"
332     "in the testcase we use the test option renegotiate_at"
333     " to lower treashold substantially."}].
334
335client_no_wrap_sequence_number(Config) when is_list(Config) ->
336    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
337    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
338
339    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
340
341    ErlData = "From erlang to erlang",
342    N = 12,
343
344    Server =
345	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
346				   {from, self()},
347				   {mfa, {ssl_test_lib, no_result, []}},
348				   {options, ServerOpts}]),
349    Port = ssl_test_lib:inet_port(Server),
350
351    Version = ssl_test_lib:protocol_version(Config, tuple),
352
353    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
354					{host, Hostname},
355					{from, self()},
356					{mfa, {ssl_test_lib,
357					       trigger_renegotiate, [[ErlData, treashold(N, Version)]]}},
358					{options, [{reuse_sessions, false},
359						   {renegotiate_at, N} | ClientOpts]}]),
360
361    ssl_test_lib:check_result(Client, ok),
362    ssl_test_lib:close(Server),
363    ssl_test_lib:close(Client).
364
365%%--------------------------------------------------------------------
366server_no_wrap_sequence_number() ->
367    [{doc, "Test that erlang server will renegotiate session when",
368     "max sequence number celing is about to be reached. Although"
369     "in the testcase we use the test option renegotiate_at"
370     " to lower treashold substantially."}].
371
372server_no_wrap_sequence_number(Config) when is_list(Config) ->
373    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
374    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
375
376    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
377
378    Data = "From erlang to erlang",
379    N = 12,
380
381    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
382					{from, self()},
383					{mfa, {ssl_test_lib,
384					       trigger_renegotiate, [[Data, N+2]]}},
385					{options, [{renegotiate_at, N} | ServerOpts]}]),
386    Port = ssl_test_lib:inet_port(Server),
387
388    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
389					{host, Hostname},
390					{from, self()},
391					{mfa, {ssl_test_lib, no_result, []}},
392					{options, [{reuse_sessions, false} | ClientOpts]}]),
393
394    ssl_test_lib:check_result(Server, ok),
395    ssl_test_lib:close(Server),
396    ssl_test_lib:close(Client).
397renegotiate_dos_mitigate_active() ->
398    [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
399      "immediately after each other"}].
400renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
401    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
402    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
403
404    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
405
406    Server =
407	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
408				   {from, self()},
409				   {mfa, {ssl_test_lib, send_recv_result_active, []}},
410				   {options, ServerOpts}]),
411    Port = ssl_test_lib:inet_port(Server),
412
413    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
414					{host, Hostname},
415					{from, self()},
416					{mfa, {?MODULE,
417					       renegotiate_immediately, []}},
418					{options, ClientOpts}]),
419
420    ssl_test_lib:check_result(Client, ok, Server, ok),
421    ssl_test_lib:close(Server),
422    ssl_test_lib:close(Client).
423
424%%--------------------------------------------------------------------
425renegotiate_dos_mitigate_passive() ->
426    [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
427      "immediately after each other"}].
428renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
429    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
430    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
431
432    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
433
434    Server =
435	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
436				   {from, self()},
437				   {mfa, {ssl_test_lib, send_recv_result, []}},
438				   {options, [{active, false} | ServerOpts]}]),
439    Port = ssl_test_lib:inet_port(Server),
440
441    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
442					{host, Hostname},
443					{from, self()},
444					{mfa, {?MODULE,
445					       renegotiate_immediately, []}},
446					{options, ClientOpts}]),
447
448    ssl_test_lib:check_result(Client, ok, Server, ok),
449    ssl_test_lib:close(Server),
450    ssl_test_lib:close(Client).
451
452%%--------------------------------------------------------------------
453renegotiate_dos_mitigate_absolute() ->
454    [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}].
455renegotiate_dos_mitigate_absolute(Config) when is_list(Config) ->
456    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
457    ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
458
459    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
460
461    Server =
462	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
463				   {from, self()},
464				   {mfa, {ssl_test_lib, send_recv_result_active, []}},
465				   {options, [{client_renegotiation, false} | ServerOpts]}]),
466    Port = ssl_test_lib:inet_port(Server),
467
468    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
469					{host, Hostname},
470					{from, self()},
471					{mfa, {?MODULE,
472					       renegotiate_rejected,
473					       []}},
474					{options, ClientOpts}]),
475
476    ssl_test_lib:check_result(Client, ok, Server, ok),
477    ssl_test_lib:close(Server),
478    ssl_test_lib:close(Client).
479
480%%--------------------------------------------------------------------
481%% Internal functions ------------------------------------------------
482%%--------------------------------------------------------------------
483renegotiate(Socket, Data) ->
484    ct:log("Renegotiating ~n", []),
485    Result = ssl:renegotiate(Socket),
486    ct:log("Result ~p~n", [Result]),
487    ssl:send(Socket, Data),
488    case Result of
489	ok ->
490	    ok;
491	Other ->
492	    Other
493    end.
494
495renegotiate_reuse_session(Socket, Data) ->
496    %% Make sure session is registered
497    ct:sleep(?SLEEP),
498    renegotiate(Socket, Data).
499
500renegotiate_immediately(Socket) ->
501    _ = ssl_test_lib:active_recv(Socket, 11),
502    ok = ssl:renegotiate(Socket),
503    {error, renegotiation_rejected} = ssl:renegotiate(Socket),
504    ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP),
505    ok = ssl:renegotiate(Socket),
506    ct:log("Renegotiated again"),
507    ssl:send(Socket, "Hello world"),
508    ok.
509
510renegotiate_rejected(Socket) ->
511    _ = ssl_test_lib:active_recv(Socket, 11),
512    {error, renegotiation_rejected} = ssl:renegotiate(Socket),
513    {error, renegotiation_rejected} = ssl:renegotiate(Socket),
514    ct:sleep(?RENEGOTIATION_DISABLE_TIME +1),
515    {error, renegotiation_rejected} = ssl:renegotiate(Socket),
516    ct:log("Failed to renegotiate again"),
517    ssl:send(Socket, "Hello world"),
518    ok.
519
520%% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
521treashold(N, {3,0}) ->
522    (N div 2) + 1;
523treashold(N, {3,1}) ->
524    (N div 2) + 1;
525treashold(N, _) ->
526    N + 1.
527
528erlang_ssl_receive(Socket, Data) ->
529    case ssl_test_lib:active_recv(Socket, length(Data)) of
530        Data ->
531            ok;
532        Other ->
533            ct:fail({{expected, Data}, {got, Other}})
534    end.
535