fchown DNS lookup sockets to UID of the app that sent the DNS lookup.
Currently DNS lookups are all sent as AID_DNS. A seperate UID of the
app that sent the DNS lookup allows us to account for and route DNS
traffic differently from AID_DNS.
Also, currently DNS over TLS lookups are all sent as UID 0.
A seperate UID(AID_DNS) allows us to account for and route DNS over
TLS traffic differently from other UID 0 traffic.
Bug: 132125333
Test: system/netd && atest resolv_integration_test
Change-Id: I47a8d41d2a9bfd9af44efe291b56b46c7da9c346
Signed-off-by: Sehee Park <sehee32.park@samsung.com>
diff --git a/DnsTlsSocket.cpp b/DnsTlsSocket.cpp
index df0db86..e3d46bb 100644
--- a/DnsTlsSocket.cpp
+++ b/DnsTlsSocket.cpp
@@ -26,6 +26,7 @@
#include <openssl/sha.h>
#include <sys/eventfd.h>
#include <sys/poll.h>
+#include <unistd.h>
#include <algorithm>
#include "DnsTlsSessionCache.h"
@@ -34,6 +35,7 @@
#include <android-base/logging.h>
#include "netdutils/SocketOption.h"
+#include "private/android_filesystem_config.h" // AID_DNS
namespace android {
@@ -80,6 +82,10 @@
return Status(errno);
}
+ if (fchown(mSslFd.get(), AID_DNS, -1) == -1) {
+ LOG(WARNING) << "Failed to chown socket: %s" << strerror(errno);
+ }
+
const socklen_t len = sizeof(mMark);
if (setsockopt(mSslFd.get(), SOL_SOCKET, SO_MARK, &mMark, len) == -1) {
LOG(ERROR) << "Failed to set socket mark";
diff --git a/res_init.cpp b/res_init.cpp
index bb86723..04ac3aa 100644
--- a/res_init.cpp
+++ b/res_init.cpp
@@ -313,6 +313,7 @@
android::net::NetworkDnsEventReported* _Nonnull event) {
if (statp != NULL) {
statp->netid = netcontext->dns_netid;
+ statp->uid = netcontext->uid;
statp->_mark = netcontext->dns_mark;
if (netcontext->flags & NET_CONTEXT_FLAG_USE_EDNS) {
statp->options |= RES_USE_EDNS0 | RES_USE_DNSSEC;
diff --git a/res_send.cpp b/res_send.cpp
index ce6f36f..4f9e793 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -772,7 +772,9 @@
return -1;
}
}
- fchown(statp->_vcsock, AID_DNS, -1);
+ if (fchown(statp->_vcsock, statp->uid, -1) == -1) {
+ PLOG(WARNING) << __func__ << ": Failed to chown socket";
+ }
if (statp->_mark != MARK_UNSET) {
if (setsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &statp->_mark,
sizeof(statp->_mark)) < 0) {
@@ -1015,7 +1017,9 @@
}
}
- fchown(statp->_u._ext.nssocks[ns], AID_DNS, -1);
+ if (fchown(statp->_u._ext.nssocks[ns], statp->uid, -1) == -1) {
+ PLOG(WARNING) << __func__ << ": Failed to chown socket";
+ }
if (statp->_mark != MARK_UNSET) {
if (setsockopt(statp->_u._ext.nssocks[ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
sizeof(statp->_mark)) < 0) {
diff --git a/resolv_private.h b/resolv_private.h
index 6d58d15..45ae337 100644
--- a/resolv_private.h
+++ b/resolv_private.h
@@ -89,6 +89,7 @@
struct __res_state {
unsigned netid; /* NetId: cache key and socket mark */
+ uid_t uid; /* uid of the app that sent the DNS lookup */
u_long options; /* option flags - see below. */
int nscount; /* number of name srvers */
struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */
diff --git a/resolver_test.cpp b/resolver_test.cpp
index 55bd5ac..578a991 100644
--- a/resolver_test.cpp
+++ b/resolver_test.cpp
@@ -66,6 +66,9 @@
constexpr int TEST_VPN_NETID = 65502;
constexpr int MAXPACKET = (8 * 1024);
+// Use maximum reserved appId for applications to avoid conflict with existing uids.
+static const int TEST_UID = 99999;
+
// Semi-public Bionic hook used by the NDK (frameworks/base/native/android/net.c)
// Tested here for convenience.
extern "C" int android_getaddrinfofornet(const char* hostname, const char* servname,
@@ -75,6 +78,7 @@
using android::base::ParseInt;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::net::INetd;
using android::net::ResolverStats;
using android::net::metrics::DnsMetricsListener;
using android::netdutils::enableSockopt;
@@ -3440,3 +3444,59 @@
// Keep in sync with FrameworkListener.cpp (500, "Command not recognized")
EXPECT_EQ(500, readResponseCode(fd));
}
+
+TEST_F(ResolverTest, BlockDnsQueryWithUidRule) {
+ constexpr char listen_addr1[] = "127.0.0.4";
+ constexpr char listen_addr2[] = "::1";
+ constexpr char host_name[] = "howdy.example.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"},
+ };
+
+ test::DNSResponder dns1(listen_addr1);
+ test::DNSResponder dns2(listen_addr2);
+ StartDns(dns1, records);
+ StartDns(dns2, records);
+
+ std::vector<std::string> servers = {listen_addr1, listen_addr2};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+ dns1.clearQueries();
+ dns2.clearQueries();
+
+ // Add drop Rule for Uid
+ EXPECT_TRUE(mDnsClient.netdService()
+ ->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, TEST_UID,
+ INetd::FIREWALL_RULE_DENY)
+ .isOk());
+
+ // Save uid
+ int suid = getuid();
+
+ // Switch to TEST_UID
+ EXPECT_TRUE(seteuid(TEST_UID) == 0);
+
+ // Dns Query
+ int fd1 = resNetworkQuery(TEST_NETID, host_name, ns_c_in, ns_t_a, 0);
+ int fd2 = resNetworkQuery(TEST_NETID, host_name, ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+ int res = getAsyncResponse(fd2, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(-ECONNREFUSED, res);
+
+ memset(buf, 0, MAXPACKET);
+ res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(-ECONNREFUSED, res);
+
+ // Restore uid
+ EXPECT_TRUE(seteuid(suid) == 0);
+
+ // Remove drop Rule for Uid
+ EXPECT_TRUE(mDnsClient.netdService()
+ ->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, TEST_UID,
+ INetd::FIREWALL_RULE_ALLOW)
+ .isOk());
+}