Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84585ee5 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 11038355 from d34abeca to 24Q1-release

Change-Id: I2115366fa33db3ef9571bf0520edd10da2d8c0df
parents 657634e4 d34abeca
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -292,6 +292,9 @@ cc_library {
        "libssl",
        "libstatssocket",
    ],
    runtime_libs: [
        "libcom.android.tethering.dns_helper",
    ],
    header_libs: [
        "libnetdbinder_utils_headers",
    ],
+69 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <arpa/inet.h>
#include <dirent.h>
#include <dlfcn.h>
#include <linux/if.h>
#include <math.h>
#include <net/if.h>
@@ -661,6 +662,54 @@ std::string makeThreadName(unsigned netId, uint32_t uid) {
    return fmt::format("Dns_{}_{}", netId, multiuser_get_app_id(uid));
}

typedef int (*InitFn)();
typedef int (*IsUidBlockedFn)(uid_t, bool);

IsUidBlockedFn ADnsHelper_isUidNetworkingBlocked;

IsUidBlockedFn resolveIsUidNetworkingBlockedFn() {
    // Related BPF maps were mainlined from T.
    if (!isAtLeastT()) return nullptr;

    // TODO: Check whether it is safe to shared link the .so without using dlopen when the carrier
    // APEX module (tethering) is fully released.
    void* handle = dlopen("libcom.android.tethering.dns_helper.so", RTLD_NOW | RTLD_LOCAL);
    if (!handle) {
        LOG(WARNING) << __func__ << ": " << dlerror();
        return nullptr;
    }

    InitFn ADnsHelper_init = reinterpret_cast<InitFn>(dlsym(handle, "ADnsHelper_init"));
    if (!ADnsHelper_init) {
        LOG(ERROR) << __func__ << ": " << dlerror();
        abort();
    }
    const int ret = (*ADnsHelper_init)();
    if (ret) {
        LOG(ERROR) << __func__ << ": ADnsHelper_init failed " << strerror(-ret);
        abort();
    }

    IsUidBlockedFn f =
            reinterpret_cast<IsUidBlockedFn>(dlsym(handle, "ADnsHelper_isUidNetworkingBlocked"));
    if (!f) {
        LOG(ERROR) << __func__ << ": " << dlerror();
        abort();
    }
    return f;
}

bool isUidNetworkingBlocked(uid_t uid, unsigned netId) {
    if (!ADnsHelper_isUidNetworkingBlocked) return false;

    // The enforceDnsUid is an OEM feature that sets DNS packet with AID_DNS instead of the
    // application's UID. Its DNS packets are not subject to certain network restriction features.
    if (resolv_is_enforceDnsUid_enabled_network(netId)) return false;

    // TODO: Pass metered information from CS to DNS resolver and replace the hardcode value.
    return (*ADnsHelper_isUidNetworkingBlocked)(uid, /*metered=*/false) == 1;
}

}  // namespace

DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) {
@@ -678,6 +727,8 @@ DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) {

    mGetDnsNetIdCommand = std::make_unique<GetDnsNetIdCommand>();
    registerCmd(mGetDnsNetIdCommand.get());

    ADnsHelper_isUidNetworkingBlocked = resolveIsUidNetworkingBlockedFn();
}

void DnsProxyListener::Handler::spawn() {
@@ -853,7 +904,10 @@ void DnsProxyListener::GetAddrInfoHandler::run() {
    int32_t rv = 0;
    NetworkDnsEventReported event;
    initDnsEvent(&event, mNetContext);
    if (startQueryLimiter(uid)) {
    if (isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid)) {
        LOG(INFO) << "GetAddrInfoHandler::run: network access blocked";
        rv = EAI_FAIL;
    } else if (startQueryLimiter(uid)) {
        const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str();
        const char* service = mService.starts_with('^') ? nullptr : mService.c_str();
        if (evaluate_domain_name(mNetContext, host)) {
@@ -1062,11 +1116,15 @@ void DnsProxyListener::ResNSendHandler::run() {
    int ansLen = -1;
    NetworkDnsEventReported event;
    initDnsEvent(&event, mNetContext);
    if (startQueryLimiter(uid)) {
    if (isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid)) {
        LOG(INFO) << "ResNSendHandler::run: network access blocked";
        ansLen = -ECONNREFUSED;
    } else if (startQueryLimiter(uid)) {
        if (evaluate_domain_name(mNetContext, rr_name.c_str())) {
            ansLen = resolv_res_nsend(&mNetContext, std::span(msg.data(), msgLen), ansBuf, &rcode,
                                      static_cast<ResNsendFlags>(mFlags), &event);
        } else {
            // TODO(b/307048182): It should return -errno.
            ansLen = -EAI_SYSTEM;
        }
        endQueryLimiter(uid);
@@ -1262,7 +1320,10 @@ void DnsProxyListener::GetHostByNameHandler::run() {
    int32_t rv = 0;
    NetworkDnsEventReported event;
    initDnsEvent(&event, mNetContext);
    if (startQueryLimiter(uid)) {
    if (isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid)) {
        LOG(INFO) << "GetHostByNameHandler::run: network access blocked";
        rv = EAI_FAIL;
    } else if (startQueryLimiter(uid)) {
        const char* name = mName.starts_with('^') ? nullptr : mName.c_str();
        if (evaluate_domain_name(mNetContext, name)) {
            rv = resolv_gethostbyname(name, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp,
@@ -1421,7 +1482,11 @@ void DnsProxyListener::GetHostByAddrHandler::run() {
    int32_t rv = 0;
    NetworkDnsEventReported event;
    initDnsEvent(&event, mNetContext);
    if (startQueryLimiter(uid)) {

    if (isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid)) {
        LOG(INFO) << "GetHostByAddrHandler::run: network access blocked";
        rv = EAI_FAIL;
    } else if (startQueryLimiter(uid)) {
        // From Android U, evaluate_domain_name() is not only for OEM customization, but also tells
        // DNS resolver whether the UID can send DNS on the specified network. The function needs
        // to be called even when there is no domain name to evaluate (GetHostByAddr). This is
+2 −2
Original line number Diff line number Diff line
@@ -4505,9 +4505,9 @@ TEST_F(ResolverTest, GetAddrinfo_BlockDnsQueryWithUidRule) {
        const char* hname;
        const int expectedErrorCode;
    } kTestData[] = {
            {host_name, EAI_NODATA},
            {host_name, isAtLeastT() ? EAI_FAIL : EAI_NODATA},
            // To test the query with search domain.
            {"howdy", EAI_AGAIN},
            {"howdy", isAtLeastT() ? EAI_FAIL : EAI_AGAIN},
    };

    INetd* netdService = mDnsClient.netdService();
+57 −0
Original line number Diff line number Diff line
@@ -528,6 +528,63 @@ TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) {
    }
}

TEST_P(TransportParameterizedTest, BlockDnsQueryWithUidRule) {
    SKIP_IF_BEFORE_T;
    constexpr char ptr_name[] = "v4v6.example.com.";
    // PTR record for IPv6 address 2001:db8::102:304
    constexpr char ptr_addr_v6[] =
            "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.";
    const DnsRecord r = {ptr_addr_v6, ns_type::ns_t_ptr, ptr_name};
    dns.addMapping(r.host_name, r.type, r.addr);
    dot_backend.addMapping(r.host_name, r.type, r.addr);
    doh_backend.addMapping(r.host_name, r.type, r.addr);

    const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
    ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));

    if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr));
    if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr));

    // This waiting time is expected to avoid that the DoH validation event interferes other tests.
    if (!testParamHasDoh()) waitForDohValidationFailed();

    // Have the test independent of the number of sent queries in private DNS validation, because
    // the DnsResolver can send either 1 or 2 queries in DoT validation.
    if (testParamHasDoh()) {
        doh.clearQueries();
    }
    if (testParamHasDot()) {
        EXPECT_TRUE(dot.waitForQueries(1));
        dot.clearQueries();
    }
    dns.clearQueries();

    // Block TEST_UID's network access
    ScopeBlockedUIDRule scopeBlockUidRule(mDnsClient.netdService(), TEST_UID);

    // getaddrinfo should fail
    const addrinfo hints = {.ai_socktype = SOCK_DGRAM};
    EXPECT_FALSE(safe_getaddrinfo(kQueryHostname, nullptr, &hints));

    // gethostbyname should fail
    EXPECT_FALSE(gethostbyname(kQueryHostname));

    // gethostbyaddr should fail
    in6_addr v6addr;
    inet_pton(AF_INET6, "2001:db8::102:304", &v6addr);
    EXPECT_FALSE(gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6));

    // resNetworkQuery should fail
    int fd = resNetworkQuery(TEST_NETID, kQueryHostname, ns_c_in, ns_t_aaaa, 0);
    EXPECT_TRUE(fd != -1);

    uint8_t buf[MAXPACKET] = {};
    int rcode;
    EXPECT_EQ(-ECONNREFUSED, getAsyncResponse(fd, &rcode, buf, MAXPACKET));

    expectQueries(0 /* dns */, 0 /* dot */, 0 /* doh */);
}

class PrivateDnsDohTest : public BasePrivateDnsTest {
  protected:
    void SetUp() override {
+8 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <netdutils/InternetAddresses.h>

#include "dns_responder/dns_responder.h"
#include "util.h"

class ScopeBlockedUIDRule {
    using INetd = aidl::android::net::INetd;
@@ -402,3 +403,10 @@ android::netdutils::ScopedAddrinfo safe_getaddrinfo(const char* node, const char

void SetMdnsRoute();
void RemoveMdnsRoute();

#define SKIP_IF_BEFORE_T                                                         \
    do {                                                                         \
        if (!isAtLeastT()) {                                                     \
            GTEST_SKIP() << "Skipping test because SDK version is less than T."; \
        }                                                                        \
    } while (0)
Loading