[tor-commits] [tor/master] hs-v3: Purge ephemeral client auth on NEWNYM

asn at torproject.org asn at torproject.org
Tue Feb 4 12:20:53 UTC 2020


commit 08f31e405d34fe47a8a4ae6e304058051c7818b6
Author: David Goulet <dgoulet at torproject.org>
Date:   Mon Feb 3 11:57:30 2020 -0500

    hs-v3: Purge ephemeral client auth on NEWNYM
    
    Fixes #33139.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 changes/ticket33139        |   3 ++
 src/feature/hs/hs_client.c |  22 ++++++++++
 src/feature/hs/hs_client.h |   2 +
 src/test/test_hs_client.c  | 100 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 127 insertions(+)

diff --git a/changes/ticket33139 b/changes/ticket33139
new file mode 100644
index 000000000..a90ab8f73
--- /dev/null
+++ b/changes/ticket33139
@@ -0,0 +1,3 @@
+  o Minor bugfixes (Onion service client, authorization):
+    - On a NEWNYM signal, purge the ephemeral client authorization cache. The
+      permanent ones are kept. Fixes bug 33139; bugfix on 0.4.3.1-alpha.
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index bcb0495c6..611cc5430 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -1249,6 +1249,26 @@ can_client_refetch_desc(const ed25519_public_key_t *identity_pk,
   return 0;
 }
 
+/** Purge the client authorization cache of all ephemeral entries that is the
+ * entries that are not flagged with CLIENT_AUTH_FLAG_IS_PERMANENT.
+ *
+ * This is called from the hs_client_purge_state() used by a SIGNEWNYM. */
+STATIC void
+purge_ephemeral_client_auth(void)
+{
+  DIGEST256MAP_FOREACH_MODIFY(client_auths, key,
+                              hs_client_service_authorization_t *, auth) {
+    /* Cleanup every entry that are _NOT_ permanent that is ephemeral. */
+    if (!(auth->flags & CLIENT_AUTH_FLAG_IS_PERMANENT)) {
+      MAP_DEL_CURRENT(key);
+      client_service_authorization_free(auth);
+    }
+  } DIGESTMAP_FOREACH_END;
+
+  log_info(LD_REND, "Client onion service ephemeral authorization "
+                    "cache has been purged.");
+}
+
 /** Return the client auth in the map using the service identity public key.
  * Return NULL if it does not exist in the map. */
 static hs_client_service_authorization_t *
@@ -2433,6 +2453,8 @@ hs_client_purge_state(void)
   hs_cache_purge_as_client();
   /* Purge the last hidden service request cache. */
   hs_purge_last_hid_serv_requests();
+  /* Purge ephemeral client authorization. */
+  purge_ephemeral_client_auth();
 
   log_info(LD_REND, "Hidden service client state has been purged.");
 }
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 56b24a411..3660bfa96 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -162,6 +162,8 @@ MOCK_DECL(STATIC hs_client_fetch_status_t,
 
 STATIC void retry_all_socks_conn_waiting_for_desc(void);
 
+STATIC void purge_ephemeral_client_auth(void);
+
 #ifdef TOR_UNIT_TESTS
 
 STATIC void set_hs_client_auths_map(digest256map_t *map);
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 9f09cc3ec..5f7fe9c40 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -80,6 +80,23 @@ mock_networkstatus_get_live_consensus(time_t now)
 }
 
 static int
+mock_write_str_to_file(const char *path, const char *str, int bin)
+{
+  (void) bin;
+  (void) path;
+  (void) str;
+  return 0;
+}
+
+static or_options_t mocked_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+  return &mocked_options;
+}
+
+static int
 helper_config_client(const char *conf, int validate_only)
 {
   int ret = 0;
@@ -1330,6 +1347,85 @@ test_close_intro_circuit_failure(void *arg)
   hs_free_all();
 }
 
+static void
+test_purge_ephemeral_client_auth(void *arg)
+{
+  ed25519_keypair_t service_kp;
+  hs_client_service_authorization_t *auth = NULL;
+  hs_client_register_auth_status_t status;
+
+  (void) arg;
+
+  /* We will try to write on disk client credentials. */
+  MOCK(check_private_dir, mock_check_private_dir);
+  MOCK(get_options, mock_get_options);
+  MOCK(write_str_to_file, mock_write_str_to_file);
+
+  /* Boggus directory so when we try to write the permanent client
+   * authorization data to disk, we don't fail. See
+   * store_permanent_client_auth_credentials() for more details. */
+  mocked_options.ClientOnionAuthDir = tor_strdup("auth_dir");
+
+  hs_init();
+
+  /* Generate service keypair */
+  tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
+
+  /* Generate a client authorization object. */
+  auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
+
+  /* Set it up. No flags meaning it is ephemeral. */
+  curve25519_secret_key_generate(&auth->enc_seckey, 0);
+  hs_build_address(&service_kp.pubkey, HS_VERSION_THREE, auth->onion_address);
+  auth->flags = 0;
+
+  /* Confirm that there is nothing in the client auth map. It is unallocated
+   * until we add the first entry. */
+  tt_assert(!get_hs_client_auths_map());
+
+  /* Add an entry to the client auth list. We loose ownership of the auth
+   * object so nullify it. */
+  status = hs_client_register_auth_credentials(auth);
+  auth = NULL;
+  tt_int_op(status, OP_EQ, REGISTER_SUCCESS);
+
+  /* We should have the entry now. */
+  digest256map_t *client_auths = get_hs_client_auths_map();
+  tt_assert(client_auths);
+  tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
+
+  /* Purge the cache that should remove all ephemeral values. */
+  purge_ephemeral_client_auth();
+  tt_int_op(digest256map_size(client_auths), OP_EQ, 0);
+
+  /* Now add a new authorization object but permanent. */
+  /* Generate a client authorization object. */
+  auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
+  curve25519_secret_key_generate(&auth->enc_seckey, 0);
+  hs_build_address(&service_kp.pubkey, HS_VERSION_THREE, auth->onion_address);
+  auth->flags = CLIENT_AUTH_FLAG_IS_PERMANENT;
+
+  /* Add an entry to the client auth list. We loose ownership of the auth
+   * object so nullify it. */
+  status = hs_client_register_auth_credentials(auth);
+  auth = NULL;
+  tt_int_op(status, OP_EQ, REGISTER_SUCCESS);
+  tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
+
+  /* Purge again, the entry should still be there. */
+  purge_ephemeral_client_auth();
+  tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
+
+ done:
+  client_service_authorization_free(auth);
+  hs_free_all();
+  tor_free(mocked_options.ClientOnionAuthDir);
+
+  UNMOCK(check_private_dir);
+  UNMOCK(get_options);
+  UNMOCK(write_str_to_file);
+}
+
 struct testcase_t hs_client_tests[] = {
   { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
     TT_FORK, NULL, NULL },
@@ -1357,5 +1453,9 @@ struct testcase_t hs_client_tests[] = {
   /* SOCKS5 Extended Error Code. */
   { "socks_hs_errors", test_socks_hs_errors, TT_FORK, NULL, NULL },
 
+  /* Client authorization. */
+  { "purge_ephemeral_client_auth", test_purge_ephemeral_client_auth, TT_FORK,
+    NULL, NULL },
+
   END_OF_TESTCASES
 };





More information about the tor-commits mailing list