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

openSUSE Build Service is sponsored by