aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Huang <huangluke@google.com>2020-05-28 10:40:22 +0000
committerLuke Huang <huangluke@google.com>2020-05-29 16:44:46 +0000
commitb9a10a82912cb33f05d44f0d0d2744edf5bc2d25 (patch)
treebcb4c9061e235a0f77712ad52b5edcabda5ce527
parent638d7e4e02367ba25eecefb9f7fc05b4d740e459 (diff)
downloadDnsResolver-b9a10a82912cb33f05d44f0d0d2744edf5bc2d25.tar.gz
Fix the side channel attack by using aysnc DnsResolver API with FLAG_NO_CACHE_STORE
Before this CL, FLAG_NO_CACHE_STORE could be used to perform side channel attack. Because this flag ensures the result is never added to the cache, but will return a cached response if one exists. So make FLAG_NO_CACHE_STORE imply FLAG_NO_CACHE_LOOKUP to block the possibility of side channel attacking. Bug: 150371903 Test: atest Merged-In: I37391ffe315b90c0cdfd86888c6bf68b2b89f601 Change-Id: I94d8544d85c615ee985d70e9aeb8f368f100cf9b (cherry picked from commit 9931b28c98218dac4fa50288b4deb10da85073f1)
-rw-r--r--res_cache.cpp10
-rw-r--r--resolv_cache_unit_test.cpp2
-rw-r--r--tests/resolv_integration_test.cpp88
3 files changed, 54 insertions, 46 deletions
diff --git a/res_cache.cpp b/res_cache.cpp
index 0cd1f39d..07985d17 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -1190,7 +1190,9 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que
// possible to cache the answer of this query.
// If ANDROID_RESOLV_NO_CACHE_STORE is set, return RESOLV_CACHE_SKIP to skip possible cache
// storing.
- if (flags & ANDROID_RESOLV_NO_CACHE_LOOKUP) {
+ // (b/150371903): ANDROID_RESOLV_NO_CACHE_STORE should imply ANDROID_RESOLV_NO_CACHE_LOOKUP
+ // to avoid side channel attack.
+ if (flags & (ANDROID_RESOLV_NO_CACHE_LOOKUP | ANDROID_RESOLV_NO_CACHE_STORE)) {
return flags & ANDROID_RESOLV_NO_CACHE_STORE ? RESOLV_CACHE_SKIP : RESOLV_CACHE_NOTFOUND;
}
Entry key;
@@ -1221,10 +1223,6 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que
if (e == NULL) {
LOG(INFO) << __func__ << ": NOT IN CACHE";
- // If it is no-cache-store mode, we won't wait for possible query.
- if (flags & ANDROID_RESOLV_NO_CACHE_STORE) {
- return RESOLV_CACHE_SKIP;
- }
if (!cache_has_pending_request_locked(cache, &key, true)) {
return RESOLV_CACHE_NOTFOUND;
@@ -1264,7 +1262,7 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que
LOG(INFO) << __func__ << ": NOT IN CACHE (STALE ENTRY " << *lookup << "DISCARDED)";
res_pquery(e->query, e->querylen);
_cache_remove_p(cache, lookup);
- return (flags & ANDROID_RESOLV_NO_CACHE_STORE) ? RESOLV_CACHE_SKIP : RESOLV_CACHE_NOTFOUND;
+ return RESOLV_CACHE_NOTFOUND;
}
*answerlen = e->answerlen;
diff --git a/resolv_cache_unit_test.cpp b/resolv_cache_unit_test.cpp
index 40f1ec6c..7b022bbe 100644
--- a/resolv_cache_unit_test.cpp
+++ b/resolv_cache_unit_test.cpp
@@ -353,7 +353,7 @@ TEST_F(ResolvCacheTest, CacheLookup_CacheFlags) {
EXPECT_TRUE(cacheLookup(RESOLV_CACHE_NOTFOUND, TEST_NETID, ce, ANDROID_RESOLV_NO_CACHE_LOOKUP));
// Now no-cache-store has no effect if a same entry is existent in the cache.
- EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ce, ANDROID_RESOLV_NO_CACHE_STORE));
+ EXPECT_TRUE(cacheLookup(RESOLV_CACHE_SKIP, TEST_NETID, ce, ANDROID_RESOLV_NO_CACHE_STORE));
// Skip the cache lookup again regardless of a same entry being already in the cache.
EXPECT_TRUE(cacheLookup(RESOLV_CACHE_SKIP, TEST_NETID, ce,
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 2e19bf82..a2a694e1 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -2239,13 +2239,13 @@ TEST_F(ResolverTest, Async_MalformedQuery) {
TEST_F(ResolverTest, Async_CacheFlags) {
constexpr char listen_addr[] = "127.0.0.4";
- constexpr char host_name[] = "howdy.example.com.";
- constexpr char another_host_name[] = "howdy.example2.com.";
+ constexpr char host_name1[] = "howdy.example.com.";
+ constexpr char host_name2[] = "howdy.example2.com.";
+ constexpr char host_name3[] = "howdy.example3.com.";
const std::vector<DnsRecord> records = {
- {host_name, ns_type::ns_t_a, "1.2.3.4"},
- {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
- {another_host_name, ns_type::ns_t_a, "1.2.3.5"},
- {another_host_name, ns_type::ns_t_aaaa, "::1.2.3.5"},
+ {host_name1, ns_type::ns_t_a, "1.2.3.4"}, {host_name1, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.5"}, {host_name2, ns_type::ns_t_aaaa, "::1.2.3.5"},
+ {host_name3, ns_type::ns_t_a, "1.2.3.6"}, {host_name3, ns_type::ns_t_aaaa, "::1.2.3.6"},
};
test::DNSResponder dns(listen_addr);
@@ -2269,17 +2269,28 @@ TEST_F(ResolverTest, Async_CacheFlags) {
expectAnswersValid(fd1, AF_INET, "1.2.3.4");
// No cache exists, expect 3 queries
- EXPECT_EQ(3U, GetNumQueries(dns, host_name));
+ EXPECT_EQ(3U, GetNumQueries(dns, host_name1));
- // Re-query and cache
+ // Raise a query with no flags to ensure no cache exists. Also make an cache entry for the
+ // query.
fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd1 != -1);
expectAnswersValid(fd1, AF_INET, "1.2.3.4");
- // Now we have cache, expect 4 queries
- EXPECT_EQ(4U, GetNumQueries(dns, host_name));
+ // Expect 4 queries because there should be no cache before this query.
+ EXPECT_EQ(4U, GetNumQueries(dns, host_name1));
+
+ // Now we have the cache entry, re-query with ANDROID_RESOLV_NO_CACHE_STORE to ensure
+ // that ANDROID_RESOLV_NO_CACHE_STORE implied ANDROID_RESOLV_NO_CACHE_LOOKUP.
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+ // Expect 5 queries because we shouldn't do cache lookup for the query which has
+ // ANDROID_RESOLV_NO_CACHE_STORE.
+ EXPECT_EQ(5U, GetNumQueries(dns, host_name1));
// ANDROID_RESOLV_NO_CACHE_LOOKUP
fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
@@ -2293,78 +2304,77 @@ TEST_F(ResolverTest, Async_CacheFlags) {
expectAnswersValid(fd2, AF_INET, "1.2.3.4");
expectAnswersValid(fd1, AF_INET, "1.2.3.4");
- // Skip cache, expect 6 queries
- EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+ // Cache was skipped, expect 2 more queries.
+ EXPECT_EQ(7U, GetNumQueries(dns, host_name1));
// Re-query verify cache works
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
- ANDROID_RESOLV_NO_CACHE_STORE);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd1 != -1);
expectAnswersValid(fd1, AF_INET, "1.2.3.4");
- // Cache hits, expect still 6 queries
- EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+ // Cache hits, expect still 7 queries
+ EXPECT_EQ(7U, GetNumQueries(dns, host_name1));
// Start to verify if ANDROID_RESOLV_NO_CACHE_LOOKUP does write response into cache
dns.clearQueries();
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa,
ANDROID_RESOLV_NO_CACHE_LOOKUP);
- fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa,
ANDROID_RESOLV_NO_CACHE_LOOKUP);
EXPECT_TRUE(fd1 != -1);
EXPECT_TRUE(fd2 != -1);
- expectAnswersValid(fd2, AF_INET6, "::1.2.3.4");
- expectAnswersValid(fd1, AF_INET6, "::1.2.3.4");
+ expectAnswersValid(fd2, AF_INET6, "::1.2.3.5");
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
// Skip cache, expect 2 queries
- EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name2));
// Re-query without flags
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
- fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, 0);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, 0);
EXPECT_TRUE(fd1 != -1);
EXPECT_TRUE(fd2 != -1);
- expectAnswersValid(fd2, AF_INET6, "::1.2.3.4");
- expectAnswersValid(fd1, AF_INET6, "::1.2.3.4");
+ expectAnswersValid(fd2, AF_INET6, "::1.2.3.5");
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
// Cache hits, expect still 2 queries
- EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name2));
// Test both ANDROID_RESOLV_NO_CACHE_STORE and ANDROID_RESOLV_NO_CACHE_LOOKUP are set
dns.clearQueries();
- // Make sure that the cache of "howdy.example2.com" exists.
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, 0);
+ // Make sure that the cache of "howdy.example3.com" exists.
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example3.com", ns_c_in, ns_t_aaaa, 0);
EXPECT_TRUE(fd1 != -1);
- expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
- EXPECT_EQ(1U, GetNumQueries(dns, another_host_name));
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.6");
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name3));
// Re-query with testFlags
const int testFlag = ANDROID_RESOLV_NO_CACHE_STORE | ANDROID_RESOLV_NO_CACHE_LOOKUP;
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, testFlag);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example3.com", ns_c_in, ns_t_aaaa, testFlag);
EXPECT_TRUE(fd1 != -1);
- expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.6");
// Expect cache lookup is skipped.
- EXPECT_EQ(2U, GetNumQueries(dns, another_host_name));
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name3));
// Do another query with testFlags
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_a, testFlag);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example3.com", ns_c_in, ns_t_a, testFlag);
EXPECT_TRUE(fd1 != -1);
- expectAnswersValid(fd1, AF_INET, "1.2.3.5");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.6");
// Expect cache lookup is skipped.
- EXPECT_EQ(3U, GetNumQueries(dns, another_host_name));
+ EXPECT_EQ(3U, GetNumQueries(dns, host_name3));
// Re-query with no flags
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_a, 0);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example3.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd1 != -1);
- expectAnswersValid(fd1, AF_INET, "1.2.3.5");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.6");
// Expect no cache hit because cache storing is also skipped in previous query.
- EXPECT_EQ(4U, GetNumQueries(dns, another_host_name));
+ EXPECT_EQ(4U, GetNumQueries(dns, host_name3));
}
TEST_F(ResolverTest, Async_NoCacheStoreFlagDoesNotRefreshStaleCacheEntry) {