From: David Goulet Date: Wed, 18 Dec 2024 15:24:28 +0000 (-0500) Subject: hs: Add downloaded counter to an HSDir cache entry X-Git-Tag: tor-0.4.8.14~6^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b628ffeb6234c58d0921756b9a34ac601870f09a;p=thirdparty%2Ftor.git hs: Add downloaded counter to an HSDir cache entry 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 --- diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index 7319b96caf..6fe3d40791 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -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; } diff --git a/src/feature/dircommon/dir_connection_st.h b/src/feature/dircommon/dir_connection_st.h index e1a88a45b0..2a3dea5e66 100644 --- a/src/feature/dircommon/dir_connection_st.h +++ b/src/feature/dircommon/dir_connection_st.h @@ -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 diff --git a/src/feature/dircommon/directory.c b/src/feature/dircommon/directory.c index 6614bb065e..96c6e8e060 100644 --- a/src/feature/dircommon/directory.c +++ b/src/feature/dircommon/directory.c @@ -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); } diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 0cc7dfd031..61e686c5a9 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -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) diff --git a/src/feature/hs/hs_cache.h b/src/feature/hs/hs_cache.h index dd55f54ba4..1b5b22194e 100644 --- a/src/feature/hs/hs_cache.h +++ b/src/feature/hs/hs_cache.h @@ -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); diff --git a/src/feature/hs/hs_ident.c b/src/feature/hs/hs_ident.c index 7e99f033ea..cd5a82d05d 100644 --- a/src/feature/hs/hs_ident.c +++ b/src/feature/hs/hs_ident.c @@ -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. */ diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h index cb1249cbdc..fd8425004d 100644 --- a/src/feature/hs/hs_ident.h +++ b/src/feature/hs/hs_ident.h @@ -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(