File 1023-ssl-Make-sure-refresh-of-trusted-certs-repopulates-c.patch of Package erlang
From d06aafba3b546157f9a4893d837e74bb69b6728c Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Sat, 18 Jun 2022 11:40:01 +0200
Subject: [PATCH] ssl: Make sure refresh of trusted certs repopulates cache
---
lib/ssl/src/ssl_certificate.erl | 16 +-
lib/ssl/src/ssl_manager.erl | 14 +-
lib/ssl/src/ssl_pkix_db.erl | 66 +++++--
lib/ssl/test/ssl_pem_cache_SUITE.erl | 248 +++++++++++++--------------
4 files changed, 181 insertions(+), 163 deletions(-)
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index fdc5ed1636..2e2b43f564 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -448,7 +448,7 @@ find_alternative_root([Cert | _], CertDbHandle, CertDbRef, InvalidatedList) ->
end.
find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, InvalidatedList) ->
- IsIssuerFun =
+ IsIssuerFun =
fun({_Key, #cert{otp=ErlCertCandidate}}, Acc) ->
case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
true ->
@@ -468,12 +468,15 @@ find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, I
end,
Result = case is_reference(CertsDbRef) of
- true ->
- do_find_issuer(IsIssuerFun, CertDbHandle, ListDb);
- false ->
+ true when ListDb == [] ->
+ CertEntryList = ssl_pkix_db:select_certentries_by_ref(CertsDbRef, CertDbHandle),
+ do_find_issuer(IsIssuerFun, CertDbHandle, CertEntryList);
+ false when ListDb == [] ->
{extracted, CertsData} = CertsDbRef,
- DB = [Entry || {decoded, Entry} <- CertsData],
- do_find_issuer(IsIssuerFun, CertDbHandle, DB)
+ CertEntryList = [Entry || {decoded, Entry} <- CertsData],
+ do_find_issuer(IsIssuerFun, CertDbHandle, CertEntryList);
+ _ ->
+ do_find_issuer(IsIssuerFun, CertDbHandle, ListDb)
end,
case Result of
issuer_not_found ->
@@ -482,6 +485,7 @@ find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, I
Result
end.
+
do_find_issuer(IssuerFun, CertDbHandle, CertDb) ->
try
foldl_db(IssuerFun, CertDbHandle, CertDb)
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index e115fe394c..f4c6dbfc96 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -276,23 +276,23 @@ init([ManagerName, PemCacheName, Opts]) ->
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From,
- #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
+ #state{certificate_db = [CertDb, FileRefDb, PemCache | _] = Db} = State) ->
Ref = make_ref(),
{reply, {ok, #{cert_db_ref => Ref,
cert_db_handle => CertDb,
fileref_db_handle => FileRefDb,
- pem_cache => PemChace,
+ pem_cache => PemCache,
session_cache => session_cache(Role, State),
crl_db_info => {CRLCb, crl_db_info(Db, UserCRLDb)}}}, State};
handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From,
- #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
+ #state{certificate_db = [CertDb, FileRefDb, PemCache | _] = Db} = State) ->
case add_trusted_certs(Pid, Trustedcerts, Db) of
{ok, Ref} ->
{reply, {ok, #{cert_db_ref => Ref,
cert_db_handle => CertDb,
fileref_db_handle => FileRefDb,
- pem_cache => PemChace,
+ pem_cache => PemCache,
session_cache => session_cache(Role, State),
crl_db_info => {CRLCb, crl_db_info(Db, UserCRLDb)}}}, State};
{error, _} = Error ->
@@ -312,10 +312,12 @@ handle_call({{register_session, Host, Port, Session},_}, _, State0) ->
State = client_register_session(Host, Port, Session, State0),
{reply, ok, State};
handle_call({refresh_trusted_db, _}, _, #state{certificate_db = Db} = State) ->
- ssl_pkix_db:refresh_trusted_certs(Db),
+ PemCache = get(ssl_pem_cache),
+ ssl_pkix_db:refresh_trusted_certs(Db, PemCache),
{reply, ok, State};
handle_call({{refresh_trusted_db, File}, _}, _, #state{certificate_db = Db} = State) ->
- ssl_pkix_db:refresh_trusted_certs(File, Db),
+ PemCache = get(ssl_pem_cache),
+ ssl_pkix_db:refresh_trusted_certs(File, Db, PemCache),
{reply, ok, State}.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl
index dbdae5307c..6da1503b8d 100644
--- a/lib/ssl/src/ssl_pkix_db.erl
+++ b/lib/ssl/src/ssl_pkix_db.erl
@@ -31,11 +31,11 @@
-export([create/1, create_pem_cache/1,
add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
- refresh_trusted_certs/1,
refresh_trusted_certs/2,
+ refresh_trusted_certs/3,
extract_trusted_certs/1,
remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
- ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2,
+ ref_count/3, lookup_trusted_cert/4, foldl/3, select_certentries_by_ref/2,
decode_pem_file/1, lookup/2]).
%%====================================================================
@@ -146,18 +146,18 @@ add_trusted_certs(_Pid, File, [ _, {RefDb, FileMapDb} | _] = Db) ->
new_trusted_cert_entry(File, Db)
end.
-refresh_trusted_certs(File, [CertsDb, {_, FileMapDb} | _]) ->
+refresh_trusted_certs(File, [CertsDb, {_, FileMapDb} | _], PemCache) ->
case lookup(File, FileMapDb) of
[Ref] ->
- {ok, Content} = decode_pem_file(File),
- remove_trusted_certs(Ref, CertsDb),
- add_certs_from_pem(Content, Ref, CertsDb);
+ Certs = ssl_certificate:file_to_certificats(File, PemCache),
+ KeyList = select_certentries_by_ref(Ref,CertsDb),
+ update_certs(Ref, Certs, KeyList, CertsDb);
undefined ->
ok
end.
-refresh_trusted_certs([_, {_, FileMapDb} | _] = Db) ->
+refresh_trusted_certs([_, {_, FileMapDb} | _] = Db, PemCache) ->
Refresh = fun({File, _}, Acc) ->
- refresh_trusted_certs(File, Db),
+ refresh_trusted_certs(File, Db, PemCache),
Acc
end,
foldl(Refresh, refresh, FileMapDb).
@@ -243,9 +243,13 @@ lookup(Key, Db) ->
foldl(Fun, Acc0, Cache) ->
ets:foldl(Fun, Acc0, Cache).
-
-select_cert_by_issuer(Cache, Issuer) ->
- ets:select(Cache, [{{{'_','_', Issuer},{'_', '$1'}},[],['$$']}]).
+%%--------------------------------------------------------------------
+-spec select_certentries_by_ref(reference(), db_handle()) -> term().
+%%
+%% Description: Select certs entries originating from same source
+%%--------------------------------------------------------------------
+select_certentries_by_ref(Ref, Cache) ->
+ ets:select(Cache, [{{{Ref,'_', '_'}, '_'},[],['$_']}]).
%%--------------------------------------------------------------------
-spec ref_count(term(), db_handle(), integer()) -> integer().
@@ -297,40 +301,40 @@ remove_certs(Ref, CertsDb) ->
ok.
add_certs_from_der(DerList, Ref, CertsDb) ->
- Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
+ Add = fun(Cert) -> add_cert(Cert, Ref, CertsDb) end,
[Add(Cert) || Cert <- DerList],
ok.
certs_from_der(DerList) ->
Ref = make_ref(),
[Decoded || Cert <- DerList,
- Decoded <- [decode_certs(Ref, Cert)],
+ Decoded <- [decode_cert(Ref, Cert)],
Decoded =/= undefined].
add_certs_from_pem(PemEntries, Ref, CertsDb) ->
- Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
+ Add = fun(Cert) -> add_cert(Cert, Ref, CertsDb) end,
[Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
ok.
-add_certs(Cert, Ref, CertsDb) ->
+add_cert(Cert, Ref, CertsDb) ->
try
- {decoded, {Key, Val}} = decode_certs(Ref, Cert),
+ {decoded, {Key, Val}} = decode_cert(Ref, Cert),
insert(Key, Val, CertsDb)
catch
error:_ ->
ok
end.
-decode_certs(Ref, #cert{otp=ErlCert} = Cert) ->
+decode_cert(Ref, #cert{otp=ErlCert} = Cert) ->
TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
Issuer = public_key:pkix_normalize_name(
TBSCertificate#'OTPTBSCertificate'.issuer),
{decoded, {{Ref, SerialNumber, Issuer}, Cert}};
-decode_certs(Ref, Der) ->
+decode_cert(Ref, Der) ->
try public_key:pkix_decode_cert(Der, otp) of
ErlCert ->
- decode_certs(Ref, #cert{der=Der, otp=ErlCert})
+ decode_cert(Ref, #cert{der=Der, otp=ErlCert})
catch error:_ ->
?LOG_NOTICE("SSL WARNING: Ignoring a CA cert as "
"it could not be correctly decoded.~n"),
@@ -378,3 +382,27 @@ crl_issuer(DerCRL) ->
TBSCRL = CRL#'CertificateList'.tbsCertList,
TBSCRL#'TBSCertList'.issuer.
+update_certs(Ref, CertList, KeyList, CertsDb) ->
+ {Insert, Delete} = insert_delete_lists(Ref, CertList, CertsDb, [], KeyList),
+ insert_cert_entries(Insert, CertsDb),
+ remove_cert_entries(Delete, CertsDb).
+
+insert_delete_lists(_, [], _, Insert, Delete) ->
+ {Insert, Delete};
+insert_delete_lists(Ref, [Cert | Rest], CertsDb, Insert, Delete) ->
+ case decode_cert(Ref, Cert) of
+ {decoded, {Key, Value} = Entry} ->
+ case lookup(Key, CertsDb) of
+ [Value] -> %% Entry already exists and is unchanged
+ insert_delete_lists(Ref, Rest, CertsDb, Insert, lists:keydelete(Key, 1, Delete));
+ _ ->
+ insert_delete_lists(Ref, Rest, CertsDb, [Entry | Insert], lists:keydelete(Key, 1, Delete))
+ end;
+ undefined ->
+ insert_delete_lists(Ref, Rest, CertsDb, Insert, Delete)
+ end.
+
+insert_cert_entries(EntryList, CertsDb) ->
+ ets:insert(CertsDb, EntryList).
+remove_cert_entries(EntryList, CertsDb) ->
+ lists:foreach(fun({Key, Value}) -> remove(Key, Value, CertsDb) end, EntryList).
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index bb68b3f7b8..6647bab898 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -68,7 +68,6 @@
check_cert/3
]).
-
-define(CLEANUP_INTERVAL, 5000).
-define(BIG_CLEANUP_INTERVAL, 600000).
-define(SLEEP_AMOUNT, 1000).
@@ -115,18 +114,23 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(pem_certfile_keyfile_periodical_cleanup = Case, Config) ->
adjust_pem_periodical_cleanup_interval(Case, Config, ?CLEANUP_INTERVAL),
+ ssl:clear_pem_cache(),
Config;
init_per_testcase(pem_cacertfile_periodical_cleanup = Case, Config) ->
adjust_pem_periodical_cleanup_interval(Case, Config, ?CLEANUP_INTERVAL),
+ ssl:clear_pem_cache(),
Config;
init_per_testcase(new_root_pem_periodical_cleanup = Case, Config) ->
adjust_pem_periodical_cleanup_interval(Case, Config, ?CLEANUP_INTERVAL),
+ ssl:clear_pem_cache(),
Config;
init_per_testcase(new_root_pem_manual_cleanup = Case, Config) ->
+ ssl:clear_pem_cache(),
adjust_pem_periodical_cleanup_interval(Case, Config, ?BIG_CLEANUP_INTERVAL),
Config;
init_per_testcase(_Case, Config) ->
ssl_test_lib:clean_start(),
+ ssl:clear_pem_cache(),
ct:timetrap({seconds, 20}),
Config.
@@ -146,19 +150,18 @@ end_per_testcase(_TestCase, Config) ->
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
pem_certfile_keyfile_periodical_cleanup() ->
- [{doc, "Test pem cache invalidate mechanism using mtime attribute "
+ [{doc, "Test PEM cache invalidate mechanism using mtime attribute "
"adjustment - certfile and keyfile."}].
%% 1. establish TLS connection
%% 2. mtime adjusted for certfile, keyfile for server
%% 3. during cleanup:
-%% 1. 2 files removed from PemCache
-%% 2. tables unchanged (Cert, CaFileRef, CaRefCnt)
-%% 4. after TLS disconnect PemCache size is 4, rest of tables are empty
+%% 1. 2 files removed from PEM Cache
+%% 2. Ca trust store and its references mapping tables are not affected
+%% 4. After TLS disconnect only the PEM cache is populated
pem_certfile_keyfile_periodical_cleanup(Config) when is_list(Config) ->
Expected = #{init => [0, 0, 0, 0], connected => [6, 6, 2, 2],
cleaned => [4, 6, 2, 2], disconnected => [4, 0, 0, 0]},
- pem_periodical_cleanup(Config, [certfile, keyfile], Expected),
- ok.
+ pem_periodical_cleanup(Config, [certfile, keyfile], Expected, false).
pem_cacertfile_periodical_cleanup() ->
[{doc, "Test pem cache invalidate mechanism using mtime attribute "
@@ -166,88 +169,77 @@ pem_cacertfile_periodical_cleanup() ->
%% 1. establish TLS connection
%% 2. mtime adjusted for cacertfile for server
%% 3. during cleanup:
-%% 1. 1 file removed from PemCache
-%% 2. tables unchanged (Cert, CaFileRef, CaRefCnt)
-%% 4. after TLS disconnect PemCache size is 5, rest of tables are empty
+%% 1. 1 file removed from PEM Cache
+%% 2. The file will however be reentered as it is a CA file that is
+%% referenced
+%% 4. After TLS disconnect only the PEM cache is populated
pem_cacertfile_periodical_cleanup(Config) when is_list(Config) ->
Expected = #{init => [0, 0, 0, 0], connected => [6, 6, 2, 2],
- cleaned => [5, 6, 2, 2], disconnected => [5, 0, 0, 0]},
- pem_periodical_cleanup(Config, [cacertfile], Expected),
- ok.
+ cleaned => [6, 6, 2, 2], disconnected => [6, 0, 0, 0]},
+ pem_periodical_cleanup(Config, [cacertfile], Expected, true).
pem_manual_cleanup() ->
[{doc,"Test that internal reference table is cleaned properly even when "
" the PEM cache is cleared" }].
%% 1. establish 1st TLS connection
-%% 2. check CaRefCnt content - expecting summed ref counters value of 2
-%% 3. store content of all tables for future checks
-%% 4. ssl:clear_pem_cache()
-%% 5. verify that PemCache table is now empty
-%% 6. verify Cert, CaFileRef, CaRefCnt were unchanged
-%% 7. establish 2nd TLS connection
-%% 8. check CaRefCnt content - expecting summed ref counters value of 4
-%% 9. verify that PemCache table size is 4 (cafileoption is ignored? not loaded
-%% to PemCache because CaFileRef contains entry for path; probably previously
-%% cached CA certs are used)
-%% 10. verify that Cert and CaFileRef table content is the same as after 1st connection
-%% 11. PemCache contains files specified with certfile and keyfile - their
-%% content should be the same as for 1st TLS connection
-%% 12. Upon disconnecting summed ref counters are reduced to 2 and 0
-%% 13. When that value becomes 0 (after terminating both connection) CaFileRef,
-%% Cert, CaRefCnt tables are cleared
+%% 2. The server and client process will now have a reference to its CA file
+%% 3. ssl:clear_pem_cache() will empty the PEM cache but as we have
+%% references to the CAfiles they will be read back, and possible updated
+%% in the CA store. (Will not happen in this test).
+%% 4. establish 2nd TLS connection
+%% 5. Now there will be two server and two client processes in total 4 references
+%% 6. The same files are used for both connections so the total amount of file is
+%% still 6.
+%% 7. Upon disconnecting summed ref counters are reduced to 2 and 0
+%% When there is no reference to a CA file (the entries for its CA certs)
+%% will be removed from the CA store. All files will still be cached.
+%% 10. Clear the PEM cache and check that it is empty.
pem_manual_cleanup(Config) when is_list(Config) ->
+ %% Initialy all tables are empty. First size will be nubmer of
+ %% files (PEM cache), second size will be number of CA certs (CA store),
+ %% third and forth number of CA files (used for reference counting and mapping
+ %% from filename to ref key)
[0, 0, 0, 0] = get_table_sizes(),
+
{Server, Client} = basic_verify_test_no_close(Config),
- 2 = get_total_counter(),
+ 2 = get_total_number_of_references(),
+ %% 6 files (cacerts,cert,key x 2), 6 CA certs in 2 different CA files
[6, 6, 2, 2] = get_table_sizes(),
- [{pem_cache, PemCacheData0}, {cert, CertData0}, {ca_ref_cnt, CaRefCntData0},
- {ca_file_ref, CaFileRefData0}] = get_tables(),
- ssl:clear_pem_cache(),
- _ = sys:get_status(whereis(ssl_manager)),
- [0, 6, 2, 2] = get_table_sizes(),
- check_tables([{pem_cache, []}, {cert, CertData0},
- {ca_ref_cnt, CaRefCntData0}, {ca_file_ref, CaFileRefData0}]),
+ ssl:clear_pem_cache(),
+ [2, 6, 2, 2] = get_table_sizes(), %% Pemcache cleared and used CAs read back
+
{Server1, Client1} = basic_verify_test_no_close(Config),
- 4 = get_total_counter(),
- [4, 6, 2, 2] = get_table_sizes(),
- [{pem_cache, PemCacheData2}, _, {ca_ref_cnt, CaRefCntData2}, _] = get_tables(),
- check_tables([{pem_cache, PemCacheData2}, {cert, CertData0},
- {ca_ref_cnt, CaRefCntData2}, {ca_file_ref, CaFileRefData0}]),
- [true = lists:member(Row, PemCacheData0) || Row <- PemCacheData2],
+ %% Total 4 processes reference a CA file
+ 4 = get_total_number_of_references(),
+ %% New connection causes keys end entity certs and keys to be cached again
+ [6, 6, 2, 2] = get_table_sizes(),
[ssl_test_lib:close(A) || A <- [Server, Client]],
- ct:sleep(2 * ?SLEEP_AMOUNT),
-
- _ = sys:get_status(whereis(ssl_manager)),
- 2 = get_total_counter(),
-
- [4, 6, 2, 2] = get_table_sizes(),
- check_tables([{pem_cache, PemCacheData2}, {cert, CertData0},
- {ca_ref_cnt, CaRefCntData0}, {ca_file_ref, CaFileRefData0}]),
+ [6, 6, 2, 2] = get_table_sizes(), %% All CA files are still referenced
+ %% Total 2 processes reference a CA file
+ 2 = get_total_number_of_references(),
[ssl_test_lib:close(A) || A <- [Server1, Client1]],
- ct:sleep(2 * ?SLEEP_AMOUNT),
+ %% No processes reference a CA file
+ 0 = get_total_number_of_references(),
+ %% All files are still cached
+ [6, 0, 0, 0] = get_table_sizes(),
- _ = sys:get_status(whereis(ssl_manager)),
- 0 = get_total_counter(),
- [4, 0, 0, 0] = get_table_sizes(),
- check_tables([{pem_cache, PemCacheData2}, {cert, []},
- {ca_ref_cnt, []}, {ca_file_ref, []}]),
- ok.
+ ssl:clear_pem_cache(),
+ [0, 0, 0, 0] = get_table_sizes().
invalid_insert() ->
[{doc, "Test that insert of invalid pem does not cause empty cache entry"}].
%% 1. attempt to establish TLS connection with client passing invalid path in
%% cacertfile option
-%% 2. verify PemCache table is populated with options passed by server
-%% 3. verify Cert, CaFileRef, CaRefCnt tables are empty
-%% 4. connection is not established
-%% 5. error happens during handshake, when client tries to open file passed in
-%% cacertfile option
+%% 2. verify PEM Cache table is populated with options passed by server
+%% 3. connection is not established as error happens during handshake,
+%% when client tries to open file passed in cacertfile option
+%% 4. verify Cert, CaFileRef tables are empty and the reference count is zero
invalid_insert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- [0, 0, 0, 0] = get_table_sizes(),
+ [0, 0, 0, 0] = get_table_sizes(), %% Initialy all tables are empty
ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -262,10 +254,9 @@ invalid_insert(Config) when is_list(Config) ->
ssl_test_lib:start_client_error([{node, ClientNode},
{port, Port}, {host, Hostname},
{from, self()}, {options, BadClientOpts}]),
- [3, 0, 0, 0] = get_table_sizes(),
- ssl_test_lib:close(Server),
- ct:sleep(?SLEEP_AMOUNT),
- [3, 0, 0, 0] = get_table_sizes().
+ [3, 0, 0, 0] = get_table_sizes(), %% Server options are cached and bad client options ignored
+ 0 = get_total_number_of_references(),
+ [3, 0, 0, 0] = get_table_sizes(). %% Cache is still valid
new_root_pem_manual_cleanup() ->
[{doc, "Test that changed PEM-files on disk followed by ssl:clear_pem_cache()"
@@ -302,21 +293,21 @@ new_root_pem_manual_cleanup() ->
%% is terminated)
new_root_pem_manual_cleanup(Config) when is_list(Config) ->
Expected = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
- cleaned => [0, 6, 2, 2], connected2 => [4, 6, 2, 2],
- disconnected1 => [4, 6, 2, 2], disconnected2 => [4,0,0,0]},
+ cleaned => [2, 6, 2, 2], connected2 => [6, 6, 2, 2],
+ disconnected1 => [6, 6, 2, 2], disconnected2 => [6,0,0,0]},
new_root_pem_helper(Config, manual, Expected, fun identity/1),
%% verify also same key sequence for initial and overwritten certs PEM files
new_root_pem_helper(Config, manual, Expected, fun identity/1, 5).
new_root_pem_periodical_cleanup() ->
[{doc, "Test that changed PEM-files on disk followed by periodical cleanup"
- " invalidates trusted CA cache as well as ordinary PEM cache. "
+ " invalidates trusted CA store as well as ordinary PEM cache. "
"This test case recreates a PEM file, resulting with its actual content change."}].
%% see new_root_pem_manual_cleanup for specification
new_root_pem_periodical_cleanup(Config) when is_list(Config) ->
ExpectedStats = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
- cleaned => [0, 6, 2, 2], connected2 => [4, 6, 2, 2],
- disconnected1 => [4, 6, 2, 2], disconnected2 => [4,0,0,0]},
+ cleaned => [2, 6, 2, 2], connected2 => [6, 6, 2, 2],
+ disconnected1 => [6, 6, 2, 2], disconnected2 => [6,0,0,0]},
new_root_pem_helper(Config, periodical, ExpectedStats, fun identity/1),
%% verify also same key sequence for initial and overwritten certs PEM files
new_root_pem_helper(Config, periodical, ExpectedStats, fun identity/1, 5).
@@ -427,13 +418,11 @@ get_table_sizes() ->
{Label, Db} <- get_table_refs()],
[Size || {_, _, Size} <- DbSizes].
-get_total_counter() ->
+get_total_number_of_references() ->
CaFileRef = proplists:get_value(ca_ref_cnt, get_table_refs()),
- CountReferencedFiles = fun({_, -1}, Acc) ->
- Acc;
- ({_, N}, Acc) ->
- N + Acc
- end,
+ CountReferencedFiles = fun({Ref, _}, Acc) ->
+ ets:update_counter(CaFileRef,Ref,0) + Acc
+ end,
ets:foldl(CountReferencedFiles, 0, CaFileRef).
get_table_refs() ->
@@ -536,26 +525,21 @@ new_root_pem_helper(Config, CleanMode,
[{pem_cache, PemCacheData1}, {cert, CertData1},
{ca_ref_cnt, CaRefCntData1}, {ca_file_ref, _}] = get_tables(),
- case CleanMode of
- no_cleanup ->
- check_tables([{pem_cache, PemCacheData0},
- {cert, CertData0},
- {ca_ref_cnt, CaRefCntData0},
- {ca_file_ref, CaFileRefData0}]),
- {Client1, Server1} =
- make_connection_check_cert(ServerRootCert0, ClientNode, ClientConf,
- ServerNode, ServerConf, Hostname, ServerCAFile0);
- _ ->
- check_tables([{pem_cache, []},
- {ca_ref_cnt, CaRefCntData0},
- {ca_file_ref, CaFileRefData0}]),
- false = (CertData1 == CertData0),
- {Client1, Server1} =
- make_connection_check_cert(ServerRootCert, ClientNode, ClientConf,
- ServerNode, ServerConf, Hostname, ServerCAFile)
- end,
-
- 4 = get_total_counter(),
+ {Client1, Server1} = case CleanMode of
+ no_cleanup ->
+ check_tables([{pem_cache, PemCacheData0},
+ {cert, CertData0},
+ {ca_ref_cnt, CaRefCntData0},
+ {ca_file_ref, CaFileRefData0}]),
+ make_connection_check_cert(ServerRootCert0, ClientNode, ClientConf,
+ ServerNode, ServerConf, Hostname, ServerCAFile0);
+ _ ->
+ false = (CertData1 == CertData0),
+ make_connection_check_cert(ServerRootCert, ClientNode, ClientConf,
+ ServerNode, ServerConf, Hostname, ServerCAFile)
+ end,
+
+ 4 = get_total_number_of_references(),
Connected2 = get_table_sizes(),
[{pem_cache, PemCacheData2}, {cert, CertData2},
{ca_ref_cnt, CaRefCntData2}, {ca_file_ref, _}] = get_tables(),
@@ -571,7 +555,7 @@ new_root_pem_helper(Config, CleanMode,
true = (CaRefCntData2 /= CaRefCntData1),
[ssl_test_lib:close(A) || A <- [Client1, Server1]],
- 2 = get_total_counter(),
+ 2 = get_total_number_of_references(),
Disconnected1 = get_table_sizes(),
case CleanMode of
@@ -583,7 +567,7 @@ new_root_pem_helper(Config, CleanMode,
check_tables([{cert, CertData1}, {ca_ref_cnt, CaRefCntData0},
{ca_file_ref, CaFileRefData0}]),
[ssl_test_lib:close(A) || A <- [Client0, Server0]],
- 0 = get_total_counter(),
+ 0 = get_total_number_of_references(),
Disconnected2 = get_table_sizes(),
case CleanMode of
no_cleanup ->
@@ -617,13 +601,13 @@ alternative_path_helper(Config, GetAlternative,
CACertFilePath1 = filename:join([Cwd, CACertFilename]),
{ok, _} = file:copy(CACertFilePath0, CACertFilePath1),
- 0 = get_total_counter(),
+ 0 = get_total_number_of_references(),
Init = get_table_sizes(),
%% connect with full path
{Server0, Client0} = basic_verify_test_no_close(
replace_cacertfile(Config, CACertFilePath1)),
- 2 = get_total_counter(),
+ 2 = get_total_number_of_references(),
Connected1 = get_table_sizes(),
TestAlternative = fun(ExpectedTotalCounter, ExpectedSizes, CertPath) ->
@@ -634,7 +618,7 @@ alternative_path_helper(Config, GetAlternative,
%% connect with filename only
{Server, Client} = basic_verify_test_no_close(
replace_cacertfile(Config, Alternative)),
- ExpectedTotalCounter = get_total_counter(),
+ ExpectedTotalCounter = get_total_number_of_references(),
ExpectedSizes = get_table_sizes(),
[Server, Client]
end
@@ -663,7 +647,7 @@ alternative_path_helper(Config, GetAlternative,
ProcessesCreated],
ct:sleep(?SLEEP_AMOUNT),
_ = sys:get_status(whereis(ssl_manager)),
- 0 = get_total_counter(),
+ 0 = get_total_number_of_references(),
Disconnected = get_table_sizes(),
ok;
{skip, Reason} ->
@@ -705,7 +689,7 @@ create_initial_config(Config) ->
intermediates => [[{key, ?KEY(2)}]],
peer => [{key, ?KEY(3)}]}}),
ClientBase = filename:join(PrivDir, "client_test"),
- ServerBase = filename:join(PrivDir, "server_test"),
+ ServerBase = filename:join(PrivDir, "server_test"),
PemConfig = x509_test:gen_pem_config_files(DerConfig, ClientBase, ServerBase),
ClientConf = proplists:get_value(client_config, PemConfig),
ServerConf = proplists:get_value(server_config, PemConfig),
@@ -739,7 +723,7 @@ overwrite_files_with_new_configuration(ServerRootCert0, ClientBase,
pem_periodical_cleanup(Config, FileIds,
#{init := Init, connected := Connected,
- cleaned := Cleaned, disconnected := Disconnected} = _ExpectedStats)->
+ cleaned := Cleaned, disconnected := Disconnected} = _ExpectedStats, IsSame)->
%% ExpectedStats map passed to function contains expected sizes of tables
%% holding various cert, cacert, keyfile data.
%% Init - represents initial state
@@ -754,20 +738,9 @@ pem_periodical_cleanup(Config, FileIds,
ct:sleep(4 * ?SLEEP_AMOUNT),
Init = get_table_sizes(),
- ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
+
+ {Server, Client} = basic_verify_test_no_close(Config),
Connected = get_table_sizes(),
[{pem_cache, PemCacheData0}, {cert, CertData0}, {ca_ref_cnt, CaRefCntData0},
@@ -786,21 +759,32 @@ pem_periodical_cleanup(Config, FileIds,
Memory = [MakeLookingYounger(F) || F <- FileIds],
ct:sleep(round(1.5 * ?CLEANUP_INTERVAL)),
+ _ = sys:get_status(whereis(ssl_manager)),
+ Result =
+ try
+ Cleaned = get_table_sizes(),
+ [{pem_cache, PemCacheData1}, _, _, _] = get_tables(),
+ IsSame = PemCacheData1 == PemCacheData0,
+ check_tables([{pem_cache, PemCacheData1}, {cert, CertData0}, {ca_ref_cnt, CaRefCntData0},
+ {ca_file_ref, CaFileRefData0}]),
+ [true = lists:member(Row, PemCacheData0) || Row <- PemCacheData1],
+
+ [ssl_test_lib:close(A) || A <- [Server, Client]],
+ Disconnected = get_table_sizes(),
+ ok
+ catch _:Reason ->
+ Reason
+ end,
- Cleaned = get_table_sizes(),
- [{pem_cache, PemCacheData1}, _, _, _] = get_tables(),
- false = PemCacheData1 == PemCacheData0,
- check_tables([{pem_cache, PemCacheData1}, {cert, CertData0}, {ca_ref_cnt, CaRefCntData0},
- {ca_file_ref, CaFileRefData0}]),
- [true = lists:member(Row, PemCacheData0) || Row <- PemCacheData1],
-
- [ssl_test_lib:close(A) || A <- [Server, Client]],
- ct:sleep(?SLEEP_AMOUNT),
-
- Disconnected = get_table_sizes(),
%% restore original mtime attributes
[ok = file:write_file_info(C, F#file_info{mtime = OT}) ||
- {C, F, OT} <- Memory].
+ {C, F, OT} <- Memory],
+ case Result of
+ ok ->
+ ok;
+ _ ->
+ ct:fail(Result)
+ end.
later()->
DateTime = calendar:now_to_local_time(os:timestamp()),
--
2.35.3