From 0fe2096144104e63d403896844af121c9622a7a8 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Wed, 12 Oct 2022 20:29:11 +0200 Subject: [PATCH 1/2] Clip DNS TTL values once in event callback This change ensures that other parts of the code base always operate on the same clipped TTL values, notably without being aware of clipping. --- src/core/or/connection_edge.c | 4 ++-- src/feature/relay/dns.c | 8 ++++---- src/test/test_cell_formats.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 5ef7a0982b..d071bf8937 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -532,7 +532,7 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason) memcpy(payload+1, tor_addr_to_in6_addr8(&conn->base_.addr), 16); addrlen = 16; } - set_uint32(payload+1+addrlen, htonl(clip_dns_ttl(conn->address_ttl))); + set_uint32(payload+1+addrlen, htonl(conn->address_ttl)); payload_len += 4+addrlen; } @@ -926,7 +926,7 @@ connected_cell_format_payload(uint8_t *payload_out, return -1; } - set_uint32(payload_out + connected_payload_len, htonl(clip_dns_ttl(ttl))); + set_uint32(payload_out + connected_payload_len, htonl(ttl)); connected_payload_len += 4; tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN); diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c index 4ae4a8e4b9..06087e6b47 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -512,7 +512,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type, uint32_t ttl; buf[0] = answer_type; - ttl = clip_dns_ttl(conn->address_ttl); + ttl = conn->address_ttl; switch (answer_type) { @@ -584,7 +584,7 @@ send_resolved_hostname_cell,(edge_connection_t *conn, size_t namelen = strlen(hostname); tor_assert(namelen < 256); - ttl = clip_dns_ttl(conn->address_ttl); + ttl = conn->address_ttl; buf[0] = RESOLVED_TYPE_HOSTNAME; buf[1] = (uint8_t)namelen; @@ -1310,7 +1310,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve) resolve->ttl_hostname < ttl) ttl = resolve->ttl_hostname; - set_expiry(new_resolve, time(NULL) + clip_dns_ttl(ttl)); + set_expiry(new_resolve, time(NULL) + ttl); } assert_cache_ok(); @@ -1725,7 +1725,7 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses, } if (result != DNS_ERR_SHUTDOWN) dns_found_answer(string_address, orig_query_type, - result, &addr, hostname, ttl); + result, &addr, hostname, clip_dns_ttl(ttl)); /* The result can be changed within this function thus why we note the result * at the end. */ diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index a004f76b19..b7b149cd66 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -354,7 +354,7 @@ test_cfmt_connected_cells(void *arg) rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE, &addr, 1024); tt_int_op(rh.length, OP_EQ, 8); - test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000e10"); + test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000400"); /* Try parsing it. */ tor_addr_make_unspec(&addr); @@ -362,7 +362,7 @@ test_cfmt_connected_cells(void *arg) tt_int_op(r, OP_EQ, 0); tt_int_op(tor_addr_family(&addr), OP_EQ, AF_INET); tt_str_op(fmt_addr(&addr), OP_EQ, "30.40.50.60"); - tt_int_op(ttl, OP_EQ, 3600); /* not 1024, since we clipped to 3600 */ + tt_int_op(ttl, OP_EQ, 1024); /* Try an IPv6 address */ memset(&rh, 0, sizeof(rh)); From 21eac1e8d8a116f2dd8dd0a7d150916646ee9120 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Wed, 12 Oct 2022 20:29:11 +0200 Subject: [PATCH 2/2] dns: Make TTLs fuzzy at exit relays This change mitigates DNS-based website oracles by making the time that a domain name is cached uncertain (+- 4 minutes of what's measurable). Resolves TROVE-2021-009. Fixes #40674 --- src/core/or/connection_edge.c | 16 ++++++++++++++++ src/core/or/connection_edge.h | 14 ++++++++++---- src/feature/relay/dns.c | 2 +- src/test/test_dns.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index d071bf8937..7bed089449 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -102,6 +102,7 @@ #include "feature/stats/predict_ports.h" #include "feature/stats/rephist.h" #include "lib/buf/buffers.h" +#include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" #include "core/or/cell_st.h" @@ -484,6 +485,21 @@ clip_dns_ttl(uint32_t ttl) return MAX_DNS_TTL; } +/** Given a TTL (in seconds), determine what TTL an exit relay should use by + * first clipping as usual and then adding some randomness which is sampled + * uniformly at random from [-FUZZY_DNS_TTL, FUZZY_DNS_TTL]. This facilitates + * fuzzy TTLs, which makes it harder to infer when a website was visited via + * side-channels like DNS (see "Website Fingerprinting with Website Oracles"). + * + * Note that this can't underflow because FUZZY_DNS_TTL < MIN_DNS_TTL. + */ +uint32_t +clip_dns_fuzzy_ttl(uint32_t ttl) +{ + return clip_dns_ttl(ttl) + + crypto_rand_uint(1 + 2*FUZZY_DNS_TTL) - FUZZY_DNS_TTL; +} + /** Send a relay end cell from stream conn down conn's circuit, and * remember that we've done so. If this is not a client connection, set the * relay end cell's reason for closing as reason. diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 966a9391d8..1816f2a463 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -187,11 +187,9 @@ void connection_ap_warn_and_unmark_if_pending_circ( entry_connection_t *entry_conn, const char *where); -/** Lowest value for DNS ttl that a server should give or a client should - * believe. */ +/** Lowest value for DNS ttl clipping excluding the random addition. */ #define MIN_DNS_TTL (5*60) -/** Highest value for DNS ttl that a server should give or a client should - * believe. */ +/** Highest value for DNS ttl clipping excluding the random addition. */ #define MAX_DNS_TTL (60*60) /** How long do we keep DNS cache entries before purging them (regardless of * their TTL)? */ @@ -199,8 +197,16 @@ void connection_ap_warn_and_unmark_if_pending_circ( /** How long do we cache/tell clients to cache DNS records when no TTL is * known? */ #define DEFAULT_DNS_TTL (30*60) +/** How much should we +- each TTL to make it fuzzy with uniform sampling at + * exits? The value 4 minutes was chosen so that the lowest possible clip is + * 60s. Such low clips were used in the past for all TTLs due to a bug in Tor, + * see "The effect of DNS on Tor's Anonymity" by Greschbach et al. In other + * words, sampling such low clips is unlikely to cause any breakage at exits. + */ +#define FUZZY_DNS_TTL (4*60) uint32_t clip_dns_ttl(uint32_t ttl); +uint32_t clip_dns_fuzzy_ttl(uint32_t ttl); int connection_half_edge_is_valid_data(const smartlist_t *half_conns, streamid_t stream_id); diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c index 06087e6b47..a38bf5cf5a 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -1725,7 +1725,7 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses, } if (result != DNS_ERR_SHUTDOWN) dns_found_answer(string_address, orig_query_type, - result, &addr, hostname, clip_dns_ttl(ttl)); + result, &addr, hostname, clip_dns_fuzzy_ttl(ttl)); /* The result can be changed within this function thus why we note the result * at the end. */ diff --git a/src/test/test_dns.c b/src/test/test_dns.c index dc38b53e0f..6612391127 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -90,6 +90,36 @@ test_dns_clip_ttl(void *arg) return; } +static void +test_dns_clip_fuzzy_ttl(void *arg) +{ + (void)arg; + + /* Case 0: check that the fuzzy TTL constant is valid + */ + tt_int_op(FUZZY_DNS_TTL, OP_LE, MIN_DNS_TTL); + tt_int_op(FUZZY_DNS_TTL, OP_LE, MAX_DNS_TTL); + + /* Case 1: low clips + */ + for (int i = 0; i < 1024; i++) { + int fuzzy_ttl = clip_dns_fuzzy_ttl(MIN_DNS_TTL - 1); + tt_int_op(fuzzy_ttl, OP_GE, MIN_DNS_TTL-FUZZY_DNS_TTL); + tt_int_op(fuzzy_ttl, OP_LE, MIN_DNS_TTL+FUZZY_DNS_TTL); + } + + /* Case 2: high clips + */ + for (int i = 0; i < 1024; i++) { + int fuzzy_ttl = clip_dns_fuzzy_ttl(MIN_DNS_TTL); + tt_int_op(fuzzy_ttl, OP_GE, MAX_DNS_TTL-FUZZY_DNS_TTL); + tt_int_op(fuzzy_ttl, OP_LE, MAX_DNS_TTL+FUZZY_DNS_TTL); + } + + done: + return; +} + static int resolve_retval = 0; static int resolve_made_conn_pending = 0; static char *resolved_name = NULL; @@ -779,6 +809,7 @@ struct testcase_t dns_tests[] = { TT_FORK, NULL, NULL }, #endif { "clip_ttl", test_dns_clip_ttl, TT_FORK, NULL, NULL }, + { "clip_fuzzy_ttl", test_dns_clip_fuzzy_ttl, TT_FORK, NULL, NULL }, { "resolve", test_dns_resolve, TT_FORK, NULL, NULL }, { "impl_addr_is_ip", test_dns_impl_addr_is_ip, TT_FORK, NULL, NULL }, { "impl_non_exit", test_dns_impl_non_exit, TT_FORK, NULL, NULL },