aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/test')
-rw-r--r--lib/ssl/test/make_certs.erl15
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl74
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl203
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl10
4 files changed, 285 insertions, 17 deletions
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 5eebf773a7..009bcd81ad 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -172,16 +172,29 @@ revoke(Root, CA, User, C) ->
gencrl(Root, CA, C).
gencrl(Root, CA, C) ->
+ %% By default, the CRL is valid for 24 hours from now.
+ gencrl(Root, CA, C, 24).
+
+gencrl(Root, CA, C, CrlHours) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
CACRLFile = filename:join([Root, CA, "crl.pem"]),
Cmd = [C#config.openssl_cmd, " ca"
" -gencrl ",
- " -crlhours 24",
+ " -crlhours ", integer_to_list(CrlHours),
" -out ", CACRLFile,
" -config ", CACnfFile],
Env = [{"ROOTDIR", filename:absname(Root)}],
cmd(Cmd, Env).
+can_generate_expired_crls(C) ->
+ %% OpenSSL can generate CRLs with an expiration date in the past,
+ %% if we pass a negative number for -crlhours. However, LibreSSL
+ %% rejects this with the error "invalid argument -24: too small".
+ %% Let's check which one we have.
+ Cmd = [C#config.openssl_cmd, " ca -crlhours -24"],
+ Output = os:cmd(Cmd),
+ 0 =:= string:str(Output, "too small").
+
verify(Root, CA, User, C) ->
CAFile = filename:join([Root, User, "cacerts.pem"]),
CACRLFile = filename:join([Root, CA, "crl.pem"]),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 905f0f442d..efa5faa218 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -150,6 +150,7 @@ api_tests() ->
sockname,
versions,
controlling_process,
+ getstat,
close_with_timeout,
hibernate,
hibernate_right_away,
@@ -694,6 +695,75 @@ controlling_process(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+getstat() ->
+ [{doc,"Test API function getstat/2"}].
+
+getstat(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port1 = ssl_test_lib:inet_port(Server1),
+ Server2 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port2 = ssl_test_lib:inet_port(Server2),
+ {ok, ActiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port1,[{active, once}|ClientOpts]]),
+ {ok, PassiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port2,[{active, false}|ClientOpts]]),
+
+ ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n",
+ [self(), self(), Server1, Server2]),
+
+ %% We only check that the values are non-zero initially
+ %% (due to the handshake), and that sending more changes the values.
+
+ %% Passive socket.
+
+ {ok, InitialStats} = ssl:getstat(PassiveC),
+ ct:pal("InitialStats ~p~n", [InitialStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ ok = ssl:send(PassiveC, "Hello world"),
+ wait_for_send(PassiveC),
+ {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]),
+ ct:pal("SStats ~p~n", [SStats]),
+ [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ %% Active socket.
+
+ {ok, InitialAStats} = ssl:getstat(ActiveC),
+ ct:pal("InitialAStats ~p~n", [InitialAStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ _ = receive
+ {ssl, ActiveC, _} ->
+ ok
+ after
+ ?SLEEP ->
+ exit(timeout)
+ end,
+
+ ok = ssl:send(ActiveC, "Hello world"),
+ wait_for_send(ActiveC),
+ {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]),
+ ct:pal("ASStats ~p~n", [ASStats]),
+ [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ ok.
+
+%%--------------------------------------------------------------------
controller_dies() ->
[{doc,"Test that the socket is closed after controlling process dies"}].
controller_dies(Config) when is_list(Config) ->
@@ -4595,4 +4665,6 @@ first_rsa_suite([{rsa, _, _, _} = Suite| _]) ->
first_rsa_suite([_ | Rest]) ->
first_rsa_suite(Rest).
-
+wait_for_send(Socket) ->
+ %% Make sure TLS process processed send message event
+ _ = ssl:connection_information(Socket).
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index aa321407b7..00636e5660 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -41,20 +41,26 @@ groups() ->
[
{check_true, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{check_peer, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{check_best_effort, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{v2_crl, [], basic_tests()},
{v1_crl, [], basic_tests()},
- {idp_crl, [], basic_tests()}].
+ {idp_crl, [], basic_tests()},
+ {crl_hash_dir, [], basic_tests() ++ crl_hash_dir_tests()}].
basic_tests() ->
[crl_verify_valid, crl_verify_revoked, crl_verify_no_crl].
+crl_hash_dir_tests() ->
+ [crl_hash_dir_collision, crl_hash_dir_expired].
init_per_suite(Config) ->
case os:find_executable("openssl") of
@@ -101,7 +107,24 @@ init_per_group(Group, Config0) ->
CertDir = filename:join(proplists:get_value(priv_dir, Config0), Group),
{CertOpts, Config} = init_certs(CertDir, Group, Config0),
{ok, _} = make_certs:all(DataDir, CertDir, CertOpts),
- [{cert_dir, CertDir}, {idp_crl, false} | Config]
+ case Group of
+ crl_hash_dir ->
+ CrlDir = filename:join(CertDir, "crls"),
+ %% Copy CRLs to their hashed filenames.
+ %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
+ populate_crl_hash_dir(CertDir, CrlDir,
+ [{"erlangCA", "d6134ed3"},
+ {"otpCA", "d4c8d7e5"}],
+ replace),
+ CrlCacheOpts = [{crl_cache,
+ {ssl_crl_hash_dir,
+ {internal, [{dir, CrlDir}]}}}];
+ _ ->
+ CrlCacheOpts = []
+ end,
+ [{crl_cache_opts, CrlCacheOpts},
+ {cert_dir, CertDir},
+ {idp_crl, false} | Config]
end.
end_per_group(_GroupName, Config) ->
@@ -164,9 +187,10 @@ crl_verify_valid(Config) when is_list(Config) ->
{crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
{verify, verify_peer}];
false ->
- [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
- {crl_check, Check},
- {verify, verify_peer}]
+ ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
end,
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -196,9 +220,10 @@ crl_verify_revoked(Config) when is_list(Config) ->
{crl_check, Check},
{verify, verify_peer}];
false ->
- [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
- {crl_check, Check},
- {verify, verify_peer}]
+ ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
end,
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
@@ -251,6 +276,134 @@ crl_verify_no_crl(Config) when is_list(Config) ->
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
end.
+crl_hash_dir_collision() ->
+ [{doc,"Verify ssl_crl_hash_dir behaviour with hash collisions"}].
+crl_hash_dir_collision(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+
+ %% Create two CAs whose names hash to the same value
+ CA1 = "hash-collision-0000000000",
+ CA2 = "hash-collision-0258497583",
+ CertsConfig = make_certs:make_config([]),
+ make_certs:intermediateCA(PrivDir, CA1, "erlangCA", CertsConfig),
+ make_certs:intermediateCA(PrivDir, CA2, "erlangCA", CertsConfig),
+
+ make_certs:enduser(PrivDir, CA1, "collision-client-1", CertsConfig),
+ make_certs:enduser(PrivDir, CA2, "collision-client-2", CertsConfig),
+
+ [ServerOpts1, ServerOpts2] =
+ [
+ [{keyfile, filename:join([PrivDir, EndUser, "key.pem"])},
+ {certfile, filename:join([PrivDir, EndUser, "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, EndUser, "cacerts.pem"])}]
+ || EndUser <- ["collision-client-1", "collision-client-2"]],
+
+ %% Add CRLs for our new CAs into the CRL hash directory.
+ %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ ClientOpts = ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "erlangCA", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Neither certificate revoked; both succeed.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts),
+ crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
+
+ make_certs:revoke(PrivDir, CA1, "collision-client-1", CertsConfig),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ %% First certificate revoked; first fails, second succeeds.
+ crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
+ "certificate revoked"),
+ crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
+
+ make_certs:revoke(PrivDir, CA2, "collision-client-2", CertsConfig),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ %% Second certificate revoked; both fail.
+ crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
+ "certificate revoked"),
+ crl_verify_error(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts,
+ "certificate revoked"),
+
+ ok.
+
+crl_hash_dir_expired() ->
+ [{doc,"Verify ssl_crl_hash_dir behaviour with expired CRLs"}].
+crl_hash_dir_expired(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+
+ CA = "CRL-maybe-expired-CA",
+ %% Add "issuing distribution point", to ensure that verification
+ %% fails if there is no valid CRL.
+ CertsConfig = make_certs:make_config([{issuing_distribution_point, true}]),
+ make_certs:can_generate_expired_crls(CertsConfig)
+ orelse throw({skip, "cannot generate CRLs with expiry date in the past"}),
+ make_certs:intermediateCA(PrivDir, CA, "erlangCA", CertsConfig),
+ EndUser = "CRL-maybe-expired",
+ make_certs:enduser(PrivDir, CA, EndUser, CertsConfig),
+
+ ServerOpts = [{keyfile, filename:join([PrivDir, EndUser, "key.pem"])},
+ {certfile, filename:join([PrivDir, EndUser, "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, EndUser, "cacerts.pem"])}],
+ ClientOpts = ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, CA, "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% First make a CRL that expired yesterday.
+ make_certs:gencrl(PrivDir, CA, CertsConfig, -24),
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA, "1627b4b0"}],
+ replace),
+
+ %% Since the CRL has expired, it's treated as missing, and the
+ %% outcome depends on the crl_check setting.
+ case Check of
+ true ->
+ %% The error "revocation status undetermined" gets turned
+ %% into "bad certificate".
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ peer ->
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ best_effort ->
+ %% In "best effort" mode, we consider the certificate not
+ %% to be revoked if we can't find the appropriate CRL.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
+ end,
+
+ %% Now make a CRL that expires tomorrow.
+ make_certs:gencrl(PrivDir, CA, CertsConfig, 24),
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA, "1627b4b0"}],
+ add),
+
+ %% With a valid CRL, verification should always pass.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts),
+
+ ok.
+
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -311,3 +464,31 @@ make_dir_path(PathComponents) ->
rename_crl(Filename) ->
file:rename(Filename, Filename ++ ".notfound").
+
+populate_crl_hash_dir(CertDir, CrlDir, CAsHashes, AddOrReplace) ->
+ ok = filelib:ensure_dir(filename:join(CrlDir, "crls")),
+ case AddOrReplace of
+ replace ->
+ %% Delete existing files, so we can override them.
+ [ok = file:delete(FileToDelete) ||
+ {_CA, Hash} <- CAsHashes,
+ FileToDelete <- filelib:wildcard(
+ filename:join(CrlDir, Hash ++ ".r*"))];
+ add ->
+ ok
+ end,
+ %% Create new files, incrementing suffix if needed to find unique names.
+ [{ok, _} =
+ file:copy(filename:join([CertDir, CA, "crl.pem"]),
+ find_free_name(CrlDir, Hash, 0))
+ || {CA, Hash} <- CAsHashes],
+ ok.
+
+find_free_name(CrlDir, Hash, N) ->
+ Name = filename:join(CrlDir, Hash ++ ".r" ++ integer_to_list(N)),
+ case filelib:is_file(Name) of
+ true ->
+ find_free_name(CrlDir, Hash, N + 1);
+ false ->
+ Name
+ end.
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index f0ce82f4fe..5ebf9bb2de 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -413,7 +413,7 @@ use_interface(Config) when is_list(Config) ->
NH1,
fun() ->
[inet:sockname(P) ||
- P <- erlang:ports(),
+ P <- inet_ports(),
{ok, Port} =:= (catch inet:port(P))]
end),
%% And check that it's actually listening on localhost.
@@ -705,9 +705,11 @@ try_setting_priority(TestFun, Config) ->
get_socket_priorities() ->
[Priority ||
{ok,[{priority,Priority}]} <-
- [inet:getopts(Port, [priority]) ||
- Port <- erlang:ports(),
- element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]].
+ [inet:getopts(Port, [priority]) || Port <- inet_ports()]].
+
+inet_ports() ->
+ [Port || Port <- erlang:ports(),
+ element(2, erlang:port_info(Port, name)) =:= "tcp_inet"].
%%
%% test_server side api