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());
+}