1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2018. 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_session_cache_SUITE). 24 25%% Note: This directive should only be used in test suites. 26-compile(export_all). 27 28-include_lib("common_test/include/ct.hrl"). 29 30-define(DELAY, 500). 31-define(SLEEP, 500). 32-define(TIMEOUT, 60000). 33-define(LONG_TIMEOUT, 600000). 34-define(MAX_TABLE_SIZE, 5). 35 36-behaviour(ssl_session_cache_api). 37 38%% For the session cache tests 39-export([init/1, terminate/1, lookup/2, update/3, 40 delete/2, foldl/3, select_session/2]). 41 42%%-------------------------------------------------------------------- 43%% Common Test interface functions ----------------------------------- 44%%-------------------------------------------------------------------- 45 46all() -> 47 [session_cleanup, 48 session_cache_process_list, 49 session_cache_process_mnesia, 50 client_unique_session, 51 max_table_size, 52 save_specific_session 53 ]. 54 55groups() -> 56 []. 57 58init_per_suite(Config0) -> 59 catch crypto:stop(), 60 try crypto:start() of 61 ok -> 62 ssl_test_lib:clean_start(), 63 %% make rsa certs using 64 ssl_test_lib:make_rsa_cert(Config0) 65 catch _:_ -> 66 {skip, "Crypto did not start"} 67 end. 68 69end_per_suite(_Config) -> 70 ssl:stop(), 71 application:stop(crypto). 72 73init_per_group(_GroupName, Config) -> 74 Config. 75 76end_per_group(_GroupName, Config) -> 77 Config. 78 79init_per_testcase(session_cache_process_list, Config) -> 80 init_customized_session_cache(list, Config); 81 82init_per_testcase(session_cache_process_mnesia, Config) -> 83 mnesia:start(), 84 init_customized_session_cache(mnesia, Config); 85 86init_per_testcase(session_cleanup, Config) -> 87 ssl:stop(), 88 application:load(ssl), 89 application:set_env(ssl, session_lifetime, 5), 90 application:set_env(ssl, session_delay_cleanup_time, ?DELAY), 91 ssl:start(), 92 ct:timetrap({seconds, 20}), 93 Config; 94 95init_per_testcase(client_unique_session, Config) -> 96 ct:timetrap({seconds, 40}), 97 Config; 98init_per_testcase(save_specific_session, Config) -> 99 ssl_test_lib:clean_start(), 100 ct:timetrap({seconds, 5}), 101 Config; 102init_per_testcase(max_table_size, Config) -> 103 ssl:stop(), 104 application:load(ssl), 105 application:set_env(ssl, session_cache_server_max, ?MAX_TABLE_SIZE), 106 application:set_env(ssl, session_cache_client_max, ?MAX_TABLE_SIZE), 107 application:set_env(ssl, session_delay_cleanup_time, ?DELAY), 108 ssl:start(), 109 ct:timetrap({seconds, 40}), 110 Config. 111 112init_customized_session_cache(Type, Config) -> 113 ssl:stop(), 114 application:load(ssl), 115 application:set_env(ssl, session_cb, ?MODULE), 116 application:set_env(ssl, session_cb_init_args, [{type, Type}]), 117 ssl:start(), 118 catch (end_per_testcase(list_to_atom("session_cache_process" ++ atom_to_list(Type)), 119 Config)), 120 ets:new(ssl_test, [named_table, public, set]), 121 ets:insert(ssl_test, {type, Type}), 122 ct:timetrap({seconds, 20}), 123 Config. 124 125end_per_testcase(session_cache_process_list, Config) -> 126 application:unset_env(ssl, session_cb), 127 end_per_testcase(default_action, Config); 128end_per_testcase(session_cache_process_mnesia, Config) -> 129 application:unset_env(ssl, session_cb), 130 application:unset_env(ssl, session_cb_init_args), 131 mnesia:kill(), 132 ssl:stop(), 133 ssl:start(), 134 end_per_testcase(default_action, Config); 135end_per_testcase(session_cleanup, Config) -> 136 application:unset_env(ssl, session_delay_cleanup_time), 137 application:unset_env(ssl, session_lifetime), 138 end_per_testcase(default_action, Config); 139end_per_testcase(max_table_size, Config) -> 140 application:unset_env(ssl, session_cach_server_max), 141 application:unset_env(ssl, session_cach_client_max), 142 end_per_testcase(default_action, Config); 143end_per_testcase(Case, Config) when Case == session_cache_process_list; 144 Case == session_cache_process_mnesia -> 145 catch ets:delete(ssl_test), 146 Config; 147end_per_testcase(_, Config) -> 148 Config. 149 150%%-------------------------------------------------------------------- 151%% Test Cases -------------------------------------------------------- 152%%-------------------------------------------------------------------- 153client_unique_session() -> 154 [{doc, "Test session table does not grow when client " 155 "sets up many connections"}]. 156client_unique_session(Config) when is_list(Config) -> 157 process_flag(trap_exit, true), 158 ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), 159 ServerOpts = proplists:get_value(server_rsa_opts, Config), 160 {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), 161 Server = 162 ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, 163 {from, self()}, 164 {mfa, {ssl_test_lib, no_result, []}}, 165 {tcp_options, [{active, false}]}, 166 {options, ServerOpts}]), 167 Port = ssl_test_lib:inet_port(Server), 168 LastClient = clients_start(Server, ClientNode, Hostname, Port, ClientOpts, 20), 169 receive 170 {LastClient, {ok, _}} -> 171 ok 172 end, 173 {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), 174 [_, _,_, _, Prop] = StatusInfo, 175 State = ssl_test_lib:state(Prop), 176 ClientCache = element(2, State), 177 178 1 = ssl_session_cache:size(ClientCache), 179 180 ssl_test_lib:close(Server, 500), 181 ssl_test_lib:close(LastClient). 182 183session_cleanup() -> 184 [{doc, "Test that sessions are cleand up eventually, so that the session table " 185 "does not grow and grow ..."}]. 186session_cleanup(Config) when is_list(Config) -> 187 process_flag(trap_exit, true), 188 ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), 189 ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), 190 {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), 191 192 Server = 193 ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, 194 {from, self()}, 195 {mfa, {ssl_test_lib, session_info_result, []}}, 196 {options, ServerOpts}]), 197 Port = ssl_test_lib:inet_port(Server), 198 Client = 199 ssl_test_lib:start_client([{node, ClientNode}, 200 {port, Port}, {host, Hostname}, 201 {mfa, {ssl_test_lib, no_result, []}}, 202 {from, self()}, {options, ClientOpts}]), 203 SessionInfo = 204 receive 205 {Server, Info} -> 206 Info 207 end, 208 209 %% Make sure session is registered 210 ct:sleep(?SLEEP), 211 212 {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), 213 [_, _,_, _, Prop] = StatusInfo, 214 State = ssl_test_lib:state(Prop), 215 ClientCache = element(2, State), 216 ServerCache = element(3, State), 217 SessionTimer = element(7, State), 218 219 Id = proplists:get_value(session_id, SessionInfo), 220 CSession = ssl_session_cache:lookup(ClientCache, {{Hostname, Port}, Id}), 221 SSession = ssl_session_cache:lookup(ServerCache, {Port, Id}), 222 223 true = CSession =/= undefined, 224 true = SSession =/= undefined, 225 226 %% Make sure session has expired and been cleaned up 227 check_timer(SessionTimer), 228 ct:sleep(?DELAY *2), %% Delay time + some extra time 229 230 {ServerDelayTimer, ClientDelayTimer} = get_delay_timers(), 231 232 check_timer(ServerDelayTimer), 233 check_timer(ClientDelayTimer), 234 235 ct:sleep(?SLEEP), %% Make sure clean has had time to run 236 237 undefined = ssl_session_cache:lookup(ClientCache, {{Hostname, Port}, Id}), 238 undefined = ssl_session_cache:lookup(ServerCache, {Port, Id}), 239 240 process_flag(trap_exit, false), 241 ssl_test_lib:close(Server), 242 ssl_test_lib:close(Client). 243 244 245%%-------------------------------------------------------------------- 246session_cache_process_list() -> 247 [{doc,"Test reuse of sessions (short handshake)"}]. 248session_cache_process_list(Config) when is_list(Config) -> 249 session_cache_process(list,Config). 250%%-------------------------------------------------------------------- 251session_cache_process_mnesia() -> 252 [{doc,"Test reuse of sessions (short handshake)"}]. 253session_cache_process_mnesia(Config) when is_list(Config) -> 254 session_cache_process(mnesia,Config). 255 256%%-------------------------------------------------------------------- 257save_specific_session() -> 258 [{doc, "Test that we can save a specific client session" 259 }]. 260save_specific_session(Config) when is_list(Config) -> 261 process_flag(trap_exit, true), 262 ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), 263 ServerOpts = proplists:get_value(server_rsa_opts, Config), 264 {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), 265 Server = 266 ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, 267 {from, self()}, 268 {mfa, {ssl_test_lib, no_result, []}}, 269 {tcp_options, [{active, false}]}, 270 {options, ServerOpts}]), 271 Port = ssl_test_lib:inet_port(Server), 272 273 Client1 = ssl_test_lib:start_client([{node, ClientNode}, 274 {port, Port}, {host, Hostname}, 275 {mfa, {ssl_test_lib, session_id, []}}, 276 {from, self()}, {options, ClientOpts}]), 277 Server ! listen, 278 279 Client2 = ssl_test_lib:start_client([{node, ClientNode}, 280 {port, Port}, {host, Hostname}, 281 {mfa, {ssl_test_lib, session_id, []}}, 282 {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), 283 SessionID1 = 284 receive 285 {Client1, S1} -> 286 S1 287 end, 288 289 SessionID2 = 290 receive 291 {Client2, S2} -> 292 S2 293 end, 294 295 true = SessionID1 =/= SessionID2, 296 297 {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), 298 [_, _,_, _, Prop] = StatusInfo, 299 State = ssl_test_lib:state(Prop), 300 ClientCache = element(2, State), 301 2 = ssl_session_cache:size(ClientCache), 302 303 Server ! listen, 304 305 Client3 = ssl_test_lib:start_client([{node, ClientNode}, 306 {port, Port}, {host, Hostname}, 307 {mfa, {ssl_test_lib, session_id, []}}, 308 {from, self()}, {options, [{reuse_session, SessionID2} | ClientOpts]}]), 309 receive 310 {Client3, SessionID2} -> 311 ok; 312 {Client3, SessionID3}-> 313 ct:fail({got, SessionID3, expected, SessionID2}); 314 Other -> 315 ct:fail({got,Other}) 316 end. 317 318%%-------------------------------------------------------------------- 319 320max_table_size() -> 321 [{doc,"Test max limit on session table"}]. 322max_table_size(Config) when is_list(Config) -> 323 process_flag(trap_exit, true), 324 ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), 325 ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), 326 {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), 327 Server = 328 ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, 329 {from, self()}, 330 {mfa, {ssl_test_lib, no_result, []}}, 331 {tcp_options, [{active, false}]}, 332 {options, ServerOpts}]), 333 Port = ssl_test_lib:inet_port(Server), 334 LastClient = clients_start(Server, 335 ClientNode, Hostname, Port, ClientOpts, 20), 336 receive 337 {LastClient, {ok, _}} -> 338 ok 339 end, 340 ct:sleep(1000), 341 {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), 342 [_, _,_, _, Prop] = StatusInfo, 343 State = ssl_test_lib:state(Prop), 344 ClientCache = element(2, State), 345 ServerCache = element(3, State), 346 N = ssl_session_cache:size(ServerCache), 347 M = ssl_session_cache:size(ClientCache), 348 ct:pal("~p",[{N, M}]), 349 ssl_test_lib:close(Server, 500), 350 ssl_test_lib:close(LastClient), 351 true = N =< ?MAX_TABLE_SIZE, 352 true = M =< ?MAX_TABLE_SIZE. 353 354%%-------------------------------------------------------------------- 355%%% Session cache API callbacks 356%%-------------------------------------------------------------------- 357 358init(Opts) -> 359 case proplists:get_value(type, Opts) of 360 list -> 361 spawn(fun() -> session_loop([]) end); 362 mnesia -> 363 mnesia:start(), 364 Name = atom_to_list(proplists:get_value(role, Opts)), 365 TabName = list_to_atom(Name ++ "sess_cache"), 366 {atomic,ok} = mnesia:create_table(TabName, []), 367 TabName 368 end. 369 370session_cb() -> 371 [{type, Type}] = ets:lookup(ssl_test, type), 372 Type. 373 374terminate(Cache) -> 375 case session_cb() of 376 list -> 377 Cache ! terminate; 378 mnesia -> 379 catch {atomic,ok} = 380 mnesia:delete_table(Cache) 381 end. 382 383lookup(Cache, Key) -> 384 case session_cb() of 385 list -> 386 Cache ! {self(), lookup, Key}, 387 receive {Cache, Res} -> Res end; 388 mnesia -> 389 case mnesia:transaction(fun() -> 390 mnesia:read(Cache, 391 Key, read) 392 end) of 393 {atomic, [{Cache, Key, Value}]} -> 394 Value; 395 _ -> 396 undefined 397 end 398 end. 399 400update(Cache, Key, Value) -> 401 case session_cb() of 402 list -> 403 Cache ! {update, Key, Value}; 404 mnesia -> 405 {atomic, ok} = 406 mnesia:transaction(fun() -> 407 mnesia:write(Cache, 408 {Cache, Key, Value}, write) 409 end) 410 end. 411 412delete(Cache, Key) -> 413 case session_cb() of 414 list -> 415 Cache ! {delete, Key}; 416 mnesia -> 417 {atomic, ok} = 418 mnesia:transaction(fun() -> 419 mnesia:delete(Cache, Key) 420 end) 421 end. 422 423foldl(Fun, Acc, Cache) -> 424 case session_cb() of 425 list -> 426 Cache ! {self(),foldl,Fun,Acc}, 427 receive {Cache, Res} -> Res end; 428 mnesia -> 429 Foldl = fun() -> 430 mnesia:foldl(Fun, Acc, Cache) 431 end, 432 {atomic, Res} = mnesia:transaction(Foldl), 433 Res 434 end. 435 436select_session(Cache, PartialKey) -> 437 case session_cb() of 438 list -> 439 Cache ! {self(),select_session, PartialKey}, 440 receive 441 {Cache, Res} -> 442 Res 443 end; 444 mnesia -> 445 Sel = fun() -> 446 mnesia:select(Cache, 447 [{{Cache,{PartialKey,'_'}, '$1'}, 448 [],['$1']}]) 449 end, 450 {atomic, Res} = mnesia:transaction(Sel), 451 Res 452 end. 453 454session_loop(Sess) -> 455 receive 456 terminate -> 457 ok; 458 {Pid, lookup, Key} -> 459 case lists:keysearch(Key,1,Sess) of 460 {value, {Key,Value}} -> 461 Pid ! {self(), Value}; 462 _ -> 463 Pid ! {self(), undefined} 464 end, 465 session_loop(Sess); 466 {update, Key, Value} -> 467 NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)], 468 session_loop(NewSess); 469 {delete, Key} -> 470 session_loop(lists:keydelete(Key,1,Sess)); 471 {Pid,foldl,Fun,Acc} -> 472 Res = lists:foldl(Fun, Acc,Sess), 473 Pid ! {self(), Res}, 474 session_loop(Sess); 475 {Pid,select_session,PKey} -> 476 Sel = fun({{PKey0, _Id},Session}, Acc) when PKey == PKey0 -> 477 [Session | Acc]; 478 (_,Acc) -> 479 Acc 480 end, 481 Sessions = lists:foldl(Sel, [], Sess), 482 Pid ! {self(), Sessions}, 483 session_loop(Sess) 484 end. 485 486%%-------------------------------------------------------------------- 487%%% Internal functions 488%%-------------------------------------------------------------------- 489 490session_cache_process(_Type,Config) when is_list(Config) -> 491 ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), 492 ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), 493 ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). 494 495 496clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) -> 497 %% Make sure session is registered 498 ct:sleep(?SLEEP * 2), 499 ssl_test_lib:start_client([{node, ClientNode}, 500 {port, Port}, {host, Hostname}, 501 {mfa, {?MODULE, connection_info_result, []}}, 502 {from, self()}, {options, ClientOpts}]); 503clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) -> 504 spawn_link(ssl_test_lib, start_client, 505 [[{node, ClientNode}, 506 {port, Port}, {host, Hostname}, 507 {mfa, {ssl_test_lib, no_result, []}}, 508 {from, self()}, {options, ClientOpts}]]), 509 Server ! listen, 510 wait_for_server(), 511 clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1). 512 513connection_info_result(Socket) -> 514 ssl:connection_information(Socket, [protocol, cipher_suite]). 515 516check_timer(Timer) -> 517 case erlang:read_timer(Timer) of 518 false -> 519 {status, _, _, _} = sys:get_status(whereis(ssl_manager)), 520 timer:sleep(?SLEEP), 521 {status, _, _, _} = sys:get_status(whereis(ssl_manager)), 522 ok; 523 Int -> 524 ct:sleep(Int), 525 check_timer(Timer) 526 end. 527 528get_delay_timers() -> 529 {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), 530 [_, _,_, _, Prop] = StatusInfo, 531 State = ssl_test_lib:state(Prop), 532 case element(8, State) of 533 {undefined, undefined} -> 534 ct:sleep(?SLEEP), 535 get_delay_timers(); 536 {undefined, _} -> 537 ct:sleep(?SLEEP), 538 get_delay_timers(); 539 {_, undefined} -> 540 ct:sleep(?SLEEP), 541 get_delay_timers(); 542 DelayTimers -> 543 DelayTimers 544 end. 545 546wait_for_server() -> 547 ct:sleep(100). 548