From 609065f165a8e145f404e55e01e8f5ac5c013bc3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 Jan 2017 14:55:39 -0500 Subject: DefecTor countermeasure: change server- and client-side DNS TTL clipping The server-side clipping now clamps to one of two values, both for what to report, and how long to cache. Additionally, we move some defines to dns.h, and give them better names. --- changes/ticket19769 | 7 +++++++ src/or/dns.c | 30 ++++++++++-------------------- src/or/dns.h | 14 ++++++++++++-- src/or/or.h | 12 ------------ src/test/test_dns.c | 28 ++++------------------------ 5 files changed, 33 insertions(+), 58 deletions(-) create mode 100644 changes/ticket19769 diff --git a/changes/ticket19769 b/changes/ticket19769 new file mode 100644 index 0000000000..9fc05c3e9e --- /dev/null +++ b/changes/ticket19769 @@ -0,0 +1,7 @@ + o Major features (security): + - Change the algorithm used to decide DNS TTLs on client and server side, + to better resist DNS-based correlation attacks like the DefecTor attack + of Greschbach, Pulls, Roberts, Winter, and Feamster). Now + relays only return one of two possible DNS TTL values, and clients + are willing to believe DNS TTL values up to 3 hours long. + Closes ticket 19769. diff --git a/src/or/dns.c b/src/or/dns.c index 5f9813b912..41a6dfd0a4 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -243,29 +243,19 @@ has_dns_init_failed(void) } /** Helper: Given a TTL from a DNS response, determine what TTL to give the - * OP that asked us to resolve it. */ + * OP that asked us to resolve it, and how long to cache that record + * ourselves. */ uint32_t dns_clip_ttl(uint32_t ttl) { - if (ttl < MIN_DNS_TTL) - return MIN_DNS_TTL; - else if (ttl > MAX_DNS_TTL) - return MAX_DNS_TTL; - else - return ttl; -} - -/** Helper: Given a TTL from a DNS response, determine how long to hold it in - * our cache. */ -STATIC uint32_t -dns_get_expiry_ttl(uint32_t ttl) -{ - if (ttl < MIN_DNS_TTL) - return MIN_DNS_TTL; - else if (ttl > MAX_DNS_ENTRY_AGE) - return MAX_DNS_ENTRY_AGE; + /* This logic is a defense against "DefectTor" DNS-based traffic + * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf . + * We only give two values: a "low" value and a "high" value. + */ + if (ttl < MIN_DNS_TTL_AT_EXIT) + return MIN_DNS_TTL_AT_EXIT; else - return ttl; + return MAX_DNS_TTL_AT_EXIT; } /** Helper: free storage held by an entry in the DNS cache. */ @@ -1317,7 +1307,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve) resolve->ttl_hostname < ttl) ttl = resolve->ttl_hostname; - set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl)); + set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl)); } assert_cache_ok(); diff --git a/src/or/dns.h b/src/or/dns.h index b14f7dd29c..951a2a3467 100644 --- a/src/or/dns.h +++ b/src/or/dns.h @@ -12,6 +12,18 @@ #ifndef TOR_DNS_H #define TOR_DNS_H +/** Lowest value for DNS ttl that a server will give. */ +#define MIN_DNS_TTL_AT_EXIT (5*60) +/** Highest value for DNS ttl that a server will give. */ +#define MAX_DNS_TTL_AT_EXIT (60*60) + +/** How long do we keep DNS cache entries before purging them (regardless of + * their TTL)? */ +#define MAX_DNS_ENTRY_AGE (3*60*60) +/** How long do we cache/tell clients to cache DNS records when no TTL is + * known? */ +#define DEFAULT_DNS_TTL (30*60) + int dns_init(void); int has_dns_init_failed(void); void dns_free_all(void); @@ -31,8 +43,6 @@ void dump_dns_mem_usage(int severity); #ifdef DNS_PRIVATE #include "dns_structs.h" -STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl); - MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,or_circuit_t *oncirc, char **hostname_out, int *made_connection_pending_out, cached_resolve_t **resolve_out)); diff --git a/src/or/or.h b/src/or/or.h index 66717792b4..e48cd92f03 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -143,18 +143,6 @@ /** Maximum size of a single extrainfo document, as above. */ #define MAX_EXTRAINFO_UPLOAD_SIZE 50000 -/** How long do we keep DNS cache entries before purging them (regardless of - * their TTL)? */ -#define MAX_DNS_ENTRY_AGE (30*60) -/** How long do we cache/tell clients to cache DNS records when no TTL is - * known? */ -#define DEFAULT_DNS_TTL (30*60) -/** How long can a TTL be before we stop believing it? */ -#define MAX_DNS_TTL (3*60*60) -/** How small can a TTL be before we stop believing it? Provides rudimentary - * pinning. */ -#define MIN_DNS_TTL 60 - /** How often do we rotate onion keys? */ #define MIN_ONION_KEY_LIFETIME (7*24*60*60) /** How often do we rotate TLS contexts? */ diff --git a/src/test/test_dns.c b/src/test/test_dns.c index 8346c0a33f..6a8e92cb47 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -16,30 +16,11 @@ NS(test_main)(void *arg) { (void)arg; - uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2; + uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2; - tt_int_op(dns_clip_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); - tt_int_op(dns_clip_ttl(ttl_mid),==,ttl_mid); - tt_int_op(dns_clip_ttl(MAX_DNS_TTL + 1),==,MAX_DNS_TTL); - - done: - return; -} - -#undef NS_SUBMODULE - -#define NS_SUBMODULE expiry_ttl - -static void -NS(test_main)(void *arg) -{ - (void)arg; - - uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_ENTRY_AGE / 2; - - tt_int_op(dns_get_expiry_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); - tt_int_op(dns_get_expiry_ttl(ttl_mid),==,ttl_mid); - tt_int_op(dns_get_expiry_ttl(MAX_DNS_ENTRY_AGE + 1),==,MAX_DNS_ENTRY_AGE); + tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),==,MIN_DNS_TTL_AT_EXIT); + tt_int_op(dns_clip_ttl(ttl_mid),==,MAX_DNS_TTL_AT_EXIT); + tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),==,MAX_DNS_TTL_AT_EXIT); done: return; @@ -749,7 +730,6 @@ NS(test_main)(void *arg) struct testcase_t dns_tests[] = { TEST_CASE(clip_ttl), - TEST_CASE(expiry_ttl), TEST_CASE(resolve), TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve), TEST_CASE_ASPECT(resolve_impl, non_exit), -- cgit v1.2.3-54-g00ecf From a969ae8e2135c5411537b14bcab310341260ef32 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 Jan 2017 14:57:50 -0500 Subject: test_cfmt_connected_cells: use TTL value that's above the new min. Related to 19769. --- src/test/test_cell_formats.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index f839a5b72c..f429f4291d 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -345,9 +345,9 @@ test_cfmt_connected_cells(void *arg) memset(&cell, 0, sizeof(cell)); tor_addr_parse(&addr, "30.40.50.60"); rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE, - &addr, 128); + &addr, 1024); tt_int_op(rh.length, OP_EQ, 8); - test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000080"); + test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000e10"); /* Try parsing it. */ tor_addr_make_unspec(&addr); @@ -355,7 +355,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, 128); + tt_int_op(ttl, OP_EQ, 3600); /* not 1024, since we clipped to 3600 */ /* Try an IPv6 address */ memset(&rh, 0, sizeof(rh)); -- cgit v1.2.3-54-g00ecf From eae68fa2d2bde931bf8b9115f7a9cb7a0f0c9073 Mon Sep 17 00:00:00 2001 From: Philipp Winter Date: Wed, 27 Jul 2016 12:01:03 -0400 Subject: Initialise DNS TTL for A and AAAA records. So far, the TTLs for both A and AAAA records were not initialised, resulting in exit relays sending back the value 60 to Tor clients. This also impacts exit relays' DNS cache -- the expiry time for all domains is set to 60. This fixes . --- src/or/dns.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/or/dns.c b/src/or/dns.c index 41a6dfd0a4..e007a402f4 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -326,7 +326,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve, resolve->result_ipv4.err_ipv4 = dns_result; resolve->res_status_ipv4 = RES_STATUS_DONE_ERR; } - + resolve->ttl_ipv4 = ttl; } else if (query_type == DNS_IPv6_AAAA) { if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT) return; @@ -341,6 +341,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve, resolve->result_ipv6.err_ipv6 = dns_result; resolve->res_status_ipv6 = RES_STATUS_DONE_ERR; } + resolve->ttl_ipv6 = ttl; } } -- cgit v1.2.3-54-g00ecf From 0151e1d1586b2e96dffb667cf2825e4fe993b62e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 18 Jan 2017 09:01:26 -0500 Subject: Changes file for 19025. --- changes/bug19025 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changes/bug19025 diff --git a/changes/bug19025 b/changes/bug19025 new file mode 100644 index 0000000000..0f365f52ba --- /dev/null +++ b/changes/bug19025 @@ -0,0 +1,4 @@ + o Major bugfixes (DNS): + - Fix a bug that prevented exit nodes from caching DNS records for more + than 60 seconds. + Fixes bug 19025; bugfix on 0.2.4.7-alpha. -- cgit v1.2.3-54-g00ecf