File 1009-ssl-internal-doc-and-code-comments-for-PEM-cert-cach.patch of Package erlang

From 53baff501d7fe39f9ab900be0419564ec40e6f36 Mon Sep 17 00:00:00 2001
From: Jakub Witczak <kuba@erlang.org>
Date: Fri, 29 Jul 2022 11:37:53 +0200
Subject: [PATCH 1/2] ssl: internal doc and code comments for PEM, cert cache

---
 lib/ssl/internal_doc/pem_and_cert_cache.md |  39 +++++++
 lib/ssl/src/ssl_manager.erl                |   2 +
 lib/ssl/src/ssl_pem_cache.erl              |   2 +
 lib/ssl/test/ssl_pem_cache_SUITE.erl       | 121 ++++++++++++++++++---
 4 files changed, 148 insertions(+), 16 deletions(-)
 create mode 100644 lib/ssl/internal_doc/pem_and_cert_cache.md

diff --git a/lib/ssl/internal_doc/pem_and_cert_cache.md b/lib/ssl/internal_doc/pem_and_cert_cache.md
new file mode 100644
index 0000000000..52fac1e6fe
--- /dev/null
+++ b/lib/ssl/internal_doc/pem_and_cert_cache.md
@@ -0,0 +1,39 @@
+# Notes on the PEM and cert caches
+## Data relations
+
+     |---------------|                 |------------------------|
+     | PemCache      |                 | CertDb                 |
+     |---------------|               * |------------------------|
+     | FilePath (PK) |           +---- | {Ref, SN, Issuer} (PK) |
+     | FileContent   |           |     | Cert (Subject)         |
+     |---------------|           |     |------------------------|
+        |0,1                     |
+        |            +-----------+
+        |0,1         |1
+     |-----------------|               |------------|
+     | FileMapDb       |               | RefDb      |
+     |-----------------|1           1  |------------|
+     | CaCertFile (PK) |---------------| Ref (PK)   |
+     | Ref (FK)        |               | Counter    |
+     |-----------------|               |------------|
+
+### PemCache
+1. stores a copy of file content in memory
+2. includes files from cacertfile, certfile, keyfile options
+3. content is added unless FileMapDb table contains entry with specified path
+
+### FileMapDb
+1. holds relation between specific path (PEM file with CA certificates) and a ref
+2. ref is generated when file from path is added for 1st time
+3. ref is used as path identifier in CertDb and RefDb tables
+
+### RefDb
+1. holds an active connections counter for a specific ref
+2. when counter reaches zero - related data in CertDb, FileMapDb, RefDb is deleted
+
+### CertDb
+1. holds decoded CA ceritificates (only those taken from cacertfile option)
+2. used for building certificate chains
+3. it is an ETS set table - when iterating in search of Issuer certificate,
+   processing order is not guaranted
+4. Table key is: {Ref, SerialNumber, Issuer}
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index a4fb07c795..5071bcadff 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -20,6 +20,8 @@
 
 %%----------------------------------------------------------------------
 %% Purpose: Manages ssl sessions and trusted certifacates
+%% (Note: See the document internal_doc/pem_and_cert_cache.md additional
+%% information)
 %%----------------------------------------------------------------------
 
 -module(ssl_manager).
diff --git a/lib/ssl/src/ssl_pem_cache.erl b/lib/ssl/src/ssl_pem_cache.erl
index 2c24351714..2c3d65c0a1 100644
--- a/lib/ssl/src/ssl_pem_cache.erl
+++ b/lib/ssl/src/ssl_pem_cache.erl
@@ -20,6 +20,8 @@
 
 %%----------------------------------------------------------------------
 %% Purpose: Manages ssl sessions and trusted certifacates
+%% (Note: See the document internal_doc/pem_and_cert_cache.md additional
+%% information)
 %%----------------------------------------------------------------------
 
 -module(ssl_pem_cache).
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index 0439bbcc5d..2ac38d41a4 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -17,7 +17,8 @@
 %%
 %% %CopyrightEnd%
 %%
-
+%% (Note: See the document internal_doc/pem_and_cert_cache.md additional
+%% information)
 %%
 
 -module(ssl_pem_cache_SUITE).
@@ -143,6 +144,12 @@ end_per_testcase(_TestCase, Config) ->
 pem_certfile_keyfile_periodical_cleanup() ->
     [{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
 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]},
@@ -152,6 +159,12 @@ pem_certfile_keyfile_periodical_cleanup(Config) when is_list(Config) ->
 pem_cacertfile_periodical_cleanup() ->
     [{doc, "Test pem cache invalidate mechanism using mtime attribute "
       "adjustment - cacertfile."}].
+%% 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
 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]},
@@ -161,6 +174,23 @@ pem_cacertfile_periodical_cleanup(Config) when is_list(Config) ->
 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
 pem_manual_cleanup(Config) when is_list(Config) ->
     [0, 0, 0, 0] = get_table_sizes(),
     {Server, Client} = basic_verify_test_no_close(Config),
@@ -204,6 +234,13 @@ pem_manual_cleanup(Config) when is_list(Config) ->
 
 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
 invalid_insert(Config) when is_list(Config) ->
     process_flag(trap_exit, true),
     [0, 0, 0, 0] = get_table_sizes(),
@@ -230,6 +267,35 @@ new_root_pem_manual_cleanup() ->
     [{doc, "Test that changed PEM-files on disk followed by ssl:clear_pem_cache()"
       " invalidates trusted CA cache as well as ordinary PEM cache. "
       "This test case recreates a PEM file, resulting with its actual content change."}].
+%% 1. ITERATE below over 2 cert chains
+%%    1. in one scenario overwritten cert chain has the same private keys - so both
+%%       cert chains are 'compatible'
+%%    2. in second scenario overwrittern cert chain has a differet private key for
+%%       intermediate cert - cert chains are not 'compatible'
+%% 2. create initial config in PEM files - 1st cert chain is created
+%% 3. make 1st TLS connection
+%% 4. verify cert on client side by:
+%%    1. get server Cert with ssl:peercert(Socket)
+%%    2. extract trusted certs from file - load data from disk
+%%    3. with CA certs extracted from file, attempt to build a chain for server Cert
+%%    4. verify that RootCert found is the same as one passed as argument
+%% 5. check tables are populated as expected
+%% 6. (copy server cacertfile to separate file - for checking chain with not
+%%    cleanup flow)
+%% 7. overwrite config in PEM files - server cert chain is updated
+%% 8. check in-memory tables were not changed even though files on disk are updated
+%% 9. -> ssl:clear_pem_cache() / ct:sleep() -
+%% 10. check PemCache is empty CaRefCnt and CaFileRef tables are unchanged
+%% 11. check Cert table was update (certificate conntent was updated)
+%% 12. make 2nd TLS connection
+%% 13. verify server cert on client side (use NEW server root as argument)
+%% 14. check the sum of  counters is 4 - 2 connections using same cert data
+%% 15. check CaFileRef table contains same data as for 1st connection
+%% 16. check Cert table was reloaded with different certs
+%% 17. check PemCache table was loaded with different file content
+%% 18. close connections in a sequence, check sum of counters value and final
+%%     content of tables (connection related should be empty when last connection
+%%     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],
@@ -242,6 +308,7 @@ 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. "
       "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],
@@ -254,6 +321,27 @@ new_root_pem_no_cleanup() ->
     [{doc, "Test that changed PEM-files on disk not followed by any cleanup"
       " will not be used for making connection."
       "This test case recreates a PEM file, resulting with its actual content change."}].
+%% 1. for link variant, replace path in ClientConf with a link - both
+%%    connection will use links on client side
+%% 2. create initial config in PEM files - 1st cert chain is created
+%% 3. make 1st TLS connection
+%% 4. verify server cert on client side by:
+%%    1. get server Cert with ssl:peercert(Socket)
+%%    2. extract trusted certs from file - load data from disk
+%%    3. with CA certs extracted from file, attempt to build a chain for server Cert
+%%    4. verify that RootCert found is the same as one passed as argument
+%% 5. check tables are populated as expected
+%% 6. (copy server cacertfile to separate file - for checking chain with not
+%%    cleanup flow)
+%% 7. overwrite config in PEM files - server cert chain is updated
+%% 8. check in-memory tables were not changed even though files on disk are updated
+%% 9. make 2nd TLS connection
+%% 10. verify server cert on client side (use the same server root as argument)
+%% 11. check the sum of  counters is 4 - 2 connections using same cert data
+%% 12. check PemCache and Cert tables hold the same data as for 1st connection
+%% 13. close connections in a sequence, check sum of counters value and final
+%%     content of tables (connection related should be empty when last connection
+%%     is terminated)
 new_root_pem_no_cleanup(Config) when is_list(Config) ->
     ExpectedStats = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                       cleaned => [6, 6, 2, 2], connected2 => [6, 6, 2, 2],
@@ -264,6 +352,7 @@ new_root_pem_no_cleanup_symlink() ->
     [{doc, "Test that changed PEM-files on disk not followed by any cleanup"
       " will not be used for making connection - even with symlink. "
       "This test case recreates a PEM file, resulting with its actual content change."}].
+%% see new_root_pem_no_cleanup for specification
 new_root_pem_no_cleanup_symlink(Config) when is_list(Config) ->
     ExpectedStats = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                       cleaned => [6, 6, 2, 2], connected2 => [6, 6, 2, 2],
@@ -274,6 +363,7 @@ new_root_pem_no_cleanup_hardlink() ->
     [{doc, "Test that changed PEM-files on disk not followed by any cleanup"
       " will not be used for making connection - even with hardlink. "
       "This test case recreates a PEM file, resulting with its actual content change."}].
+%% see new_root_pem_no_cleanup for specification
 new_root_pem_no_cleanup_hardlink(Config) when is_list(Config) ->
     ExpectedStats = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                       cleaned => [6, 6, 2, 2], connected2 => [6, 6, 2, 2],
@@ -284,6 +374,18 @@ alternative_path_hardlink() ->
     [{doc,"Test that internal reference table contains expected data for"
       " absolute and hard link. "
       "This test verifies handling of same file with an alternative reference."}].
+%% 1. copy client cacertfile to CWD
+%% 2. establish connection using client CA cert file specified with full path
+%% 3. check sum of counters is 2
+%% 4. check table sizes
+%% 5. establish connection using client CA cert file specified with alternative
+%%    - hardlink, symlink or just filename instead of path
+%% 6. check sum of counters is 4
+%% 7. check table sizes
+%% 8. copy client CA cert file into sub-directory
+%% 9. change CWD to subdirectory
+%% 10. establish connection using client CA cert file specified with alternative
+%%    - hardlink, symlink or just filename instead of path
 alternative_path_hardlink(Config) when is_list(Config) ->
     Expected = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                  connected2 => [7, 9, 3, 3], connected3 => [8, 12, 4, 4],
@@ -294,6 +396,7 @@ alternative_path_symlink() ->
     [{doc,"Test that internal reference table contains expected data for"
       " absolute and symbolic link. "
       "This test verifies handling of same file with an alternative reference."}].
+%% see alternative_path_hardlink for specification
 alternative_path_symlink(Config) when is_list(Config) ->
     Expected = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                  connected2 => [7, 9, 3, 3], connected3 => [8, 12, 4, 4],
@@ -304,6 +407,7 @@ alternative_path_noabspath() ->
     [{doc,"Test that internal reference table contains expected data for"
       " absolute and relative paths. "
       "This test verifies handling of same file with an alternative reference."}].
+%% see alternative_path_hardlink for specification
 alternative_path_noabspath(Config) when is_list(Config) ->
     Expected = #{init => [0, 0, 0, 0], connected1 => [6, 6, 2, 2],
                  connected2 => [7, 9, 3, 3], connected3 => [7, 9, 3, 3],
@@ -313,21 +417,6 @@ alternative_path_noabspath(Config) when is_list(Config) ->
 %%--------------------------------------------------------------------
 %% Internal functions
 %%--------------------------------------------------------------------
-%% |---------------|                   |------------------------|
-%% | PemCache      |                   | Cert                   |
-%% |---------------|0,1               *|------------------------|
-%% | FilePath (PK) |-------------------| {Ref, SN, Issuer} (PK) |
-%% | FileContent   |                   | Cert                   |
-%% |---------------|                   |------------------------|
-%%    |0,1     |0,1
-%%    |        +------------------------+
-%%    |0,1                              |0,1
-%% |-----------------|               |------------|
-%% | CaFileRef       |               | CaRefCnt   |
-%% |-----------------|               |------------|
-%% | CaCertFile (PK) |               | Ref (PK)   |
-%% | Ref (FK)        |               | Counter    |
-%% |-----------------|               |------------|
 get_table_sizes() ->
     ct:sleep(?SLEEP_AMOUNT),
     DbSizes = [{Label, Db, ssl_pkix_db:db_size(Db)} ||
-- 
2.35.3

openSUSE Build Service is sponsored by