]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
hs: Add downloaded counter to an HSDir cache entry
authorDavid Goulet <dgoulet@torproject.org>
Wed, 18 Dec 2024 15:24:28 +0000 (10:24 -0500)
committerDavid Goulet <dgoulet@torproject.org>
Wed, 18 Dec 2024 15:24:28 +0000 (10:24 -0500)
This adds a counter for the number of times a descriptor is downloaded from an
HSDir. Future commit will change the OOM subsystem to clean that cache based on
the lowest downloaded counts instead of time in cache.

In order to raise the bar even more for an attacker, the downloaded counter is
only marked when the directory request stream is closed. To pull this off, the
HS identifier on the directory connection is populated with the blinded key
requested (only on success). Finally, when the connection closes, we can then
lookup the cache entry with it and increment the counter.

Part of #40996

Signed-off-by: David Goulet <dgoulet@torproject.org>
src/feature/dircache/dircache.c
src/feature/dircommon/dir_connection_st.h
src/feature/dircommon/directory.c
src/feature/hs/hs_cache.c
src/feature/hs/hs_cache.h
src/feature/hs/hs_ident.c
src/feature/hs/hs_ident.h

index 7319b96caf8ceba675040040648f26022185321d..6fe3d407915a5f0ab2d1ff526b85dd8d8a2a1e39 100644 (file)
@@ -26,6 +26,7 @@
 #include "feature/dircommon/directory.h"
 #include "feature/dircommon/fp_pair.h"
 #include "feature/hs/hs_cache.h"
+#include "feature/hs/hs_ident.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/networkstatus.h"
 #include "feature/nodelist/routerlist.h"
@@ -34,6 +35,7 @@
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/rephist.h"
 #include "lib/compress/compress.h"
+#include "lib/crypt_ops/crypto_format.h"
 
 #include "feature/dircache/cached_dir_st.h"
 #include "feature/dircommon/dir_connection_st.h"
@@ -1381,6 +1383,17 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
   write_http_response_header(conn, strlen(desc_str), NO_METHOD, 0);
   connection_buf_add(desc_str, strlen(desc_str), TO_CONN(conn));
 
+  /* We have successfully written the descriptor on the connection outbuf so
+   * save this query identifier into the dir_connection_t in order
+   * to associate it to the descriptor when closing. */
+  {
+    /* Decode blinded key. This is certain to work else
+     * hs_cache_lookup_as_dir() would have failed. */
+    ed25519_public_key_t blinded_key;
+    ed25519_public_from_base64(&blinded_key, pubkey_str);
+    conn->hs_ident = hs_ident_server_dir_conn_new(&blinded_key);
+  }
+
  done:
   return 0;
 }
index e1a88a45b0a47f1d4eb4345cf5f513750a949208..2a3dea5e6656a905e540cab2693abf454b50a64c 100644 (file)
@@ -44,7 +44,9 @@ struct dir_connection_t {
 
   /* Hidden service connection identifier for dir connections: Used by HS
      client-side code to fetch HS descriptors, and by the service-side code to
-     upload descriptors. */
+     upload descriptors. Also used by the HSDir, setting only the blinded key,
+     in order to locate back the descriptor in the cache once the dir stream is
+     closed. */
   struct hs_ident_dir_conn_t *hs_ident;
 
   /** If this is a one-hop connection, tracks the state of the directory guard
index 6614bb065e8fc2c1f64e70ce54a7fee79f32e0a9..96c6e8e060b59995d7664abbeca63f8b3c767ca5 100644 (file)
@@ -16,6 +16,7 @@
 #include "feature/dirclient/dirclient.h"
 #include "feature/dircommon/directory.h"
 #include "feature/dircommon/fp_pair.h"
+#include "feature/hs/hs_cache.h"
 #include "feature/stats/geoip_stats.h"
 #include "lib/compress/compress.h"
 
@@ -492,6 +493,17 @@ connection_dir_about_to_close(dir_connection_t *dir_conn)
     connection_dir_client_request_failed(dir_conn);
   }
 
+  /* If we are an HSDir, mark the corresponding descriptor as downloaded. This
+   * is needed for the OOM cache cleanup.
+   *
+   * This is done when the direction connection is closed in order to raise the
+   * attack cost of filling the cache with bogus descriptors. That attacker
+   * would need to increase that downloaded counter for the attack to be
+   * successful which is expensive. */
+  if (conn->purpose == DIR_PURPOSE_SERVER && dir_conn->hs_ident) {
+    hs_cache_mark_dowloaded_as_dir(dir_conn->hs_ident);
+  }
+
   connection_dir_client_refetch_hsdesc_if_needed(dir_conn);
 }
 
index 0cc7dfd031c7e22022238bd3d8141f313da5368f..61e686c5a9f585fc999082ac961cfa501828f3ec 100644 (file)
@@ -334,6 +334,22 @@ hs_cache_lookup_as_dir(uint32_t version, const char *query,
   return found;
 }
 
+/** Using the given directory identifier, lookup the descriptor in our cache
+ * and if present, increment the downloaded counter. This is done when the
+ * directory connection fetching this descriptor is closed. */
+void
+hs_cache_mark_dowloaded_as_dir(const hs_ident_dir_conn_t *ident)
+{
+  hs_cache_dir_descriptor_t *entry;
+
+  tor_assert(ident);
+
+  entry = lookup_v3_desc_as_dir(ident->blinded_pk.pubkey);
+  if (entry) {
+    entry->n_downloaded++;
+  }
+}
+
 /** Clean all directory caches using the current time now. */
 void
 hs_cache_clean_as_dir(time_t now)
index dd55f54ba42e8de8142427f1289b4e981bc1582d..1b5b22194e8d217e6997deefb1215d7073daafd5 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "feature/hs/hs_common.h"
 #include "feature/hs/hs_descriptor.h"
+#include "feature/hs/hs_ident.h"
 #include "feature/rend/rendcommon.h"
 #include "feature/nodelist/torcert.h"
 
@@ -68,6 +69,10 @@ typedef struct hs_cache_dir_descriptor_t {
   /** Encoded descriptor which is basically in text form. It's a NUL terminated
    * string thus safe to strlen(). */
   char *encoded_desc;
+  /** How many times this descriptor has been downloaded. We use this as an
+   * heuristic for the OOM cache cleaning. It is very large so we avoid an kind
+   * of possible wrapping. */
+  uint64_t n_downloaded;
 } hs_cache_dir_descriptor_t;
 
 /* Public API */
@@ -92,6 +97,7 @@ unsigned int hs_cache_get_max_descriptor_size(void);
 int hs_cache_store_as_dir(const char *desc);
 int hs_cache_lookup_as_dir(uint32_t version, const char *query,
                            const char **desc_out);
+void hs_cache_mark_dowloaded_as_dir(const hs_ident_dir_conn_t *ident);
 
 const hs_descriptor_t *
 hs_cache_lookup_as_client(const struct ed25519_public_key_t *key);
index 7e99f033eac1283ace120cdee146c2d1b8ef1968..cd5a82d05db688c9d4b6fdfbe9cc5e4c519a0784 100644 (file)
@@ -62,6 +62,16 @@ hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident)
   tor_free(ident);
 }
 
+/** Return a newly allocated HS directory connection identifier that is meant
+ * for the server side (HSDir). Only the blinded key is known by the HSDir. */
+hs_ident_dir_conn_t *
+hs_ident_server_dir_conn_new(const ed25519_public_key_t *blinded_pk)
+{
+  hs_ident_dir_conn_t *ident = tor_malloc_zero(sizeof(*ident));
+  ed25519_pubkey_copy(&ident->blinded_pk, blinded_pk);
+  return ident;
+}
+
 /** Initialized the allocated ident object with identity_pk and blinded_pk.
  * None of them can be NULL since a valid directory connection identifier must
  * have all fields set. */
index cb1249cbdcc4edddb34770ff6b67303a9d0097c0..fd8425004dab5b3c3cc6661991b1778850739dd5 100644 (file)
@@ -128,6 +128,8 @@ void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident);
 void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
                             const ed25519_public_key_t *blinded_pk,
                             hs_ident_dir_conn_t *ident);
+hs_ident_dir_conn_t *hs_ident_server_dir_conn_new(
+                              const ed25519_public_key_t *blinded_pk);
 
 /* Edge connection identifier API. */
 hs_ident_edge_conn_t *hs_ident_edge_conn_new(