Provide a way to disable socket() and DNS lookups in libnetd_client.
This is a Client-only solution.
- Add to NetdClient a per-process std::atomic_boolean
similar to netIdForProcess and netIdForResolv.
- The boolean says whether the process should be
allowed Internet connectivity.
- Add an @hide method to NetUtils.java to set the boolean;
call it from the initialization code of the new
process just after forking from zygote.
- Make netdClientSocket and dnsOpenProxy check the
boolean. If the boolean is false, return EPERM from
socket calls.
Bug: 150028556
Test: atest netd_integration_test
Test: atest CtsAppSecurityHostTestCases:UseProcessTest
Change-Id: Ic697afd284ba250e56bd9492241452762da15770
diff --git a/tests/netd_client_test.cpp b/tests/netd_client_test.cpp
new file mode 100644
index 0000000..c8af408
--- /dev/null
+++ b/tests/netd_client_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h> /* poll */
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include "NetdClient.h"
+
+#define SKIP_IF_NO_NETWORK_CONNECTIVITY \
+ do { \
+ if (!checkNetworkConnectivity()) { \
+ GTEST_LOG_(INFO) << "Skip. Required Network Connectivity. \n"; \
+ return; \
+ } \
+ } while (0)
+
+namespace {
+
+constexpr char TEST_DOMAIN[] = "www.google.com";
+
+bool checkNetworkConnectivity() {
+ android::base::unique_fd sock(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
+ if (sock == -1) return false;
+ static const sockaddr_in6 server6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr.s6_addr = {// 2000::
+ 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+ int ret = connect(sock, reinterpret_cast<const sockaddr*>(&server6), sizeof(server6));
+ if (ret == 0) return true;
+ sock.reset(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
+ if (sock == -1) return false;
+ static const sockaddr_in server4 = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8
+ };
+ ret = connect(sock, reinterpret_cast<const sockaddr*>(&server4), sizeof(server4));
+ return !ret;
+}
+
+void expectHasNetworking() {
+ // Socket
+ android::base::unique_fd ipv4(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)),
+ ipv6(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, ipv4);
+ EXPECT_LE(3, ipv6);
+
+ // DNS
+ addrinfo* result = nullptr;
+ errno = 0;
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ EXPECT_EQ(0, getaddrinfo(TEST_DOMAIN, nullptr, &hints, &result));
+ EXPECT_EQ(0, errno);
+ freeaddrinfo(result);
+}
+
+void expectNoNetworking() {
+ // Socket
+ android::base::unique_fd unixSocket(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, unixSocket);
+ android::base::unique_fd ipv4(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv4);
+ EXPECT_EQ(EPERM, errno);
+ android::base::unique_fd ipv6(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv6);
+ EXPECT_EQ(EPERM, errno);
+
+ // DNS
+ addrinfo* result = nullptr;
+ errno = 0;
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ EXPECT_EQ(EAI_NODATA, getaddrinfo(TEST_DOMAIN, nullptr, &hints, &result));
+ EXPECT_EQ(EPERM, errno);
+ freeaddrinfo(result);
+}
+
+} // namespace
+
+TEST(NetdClientIntegrationTest, setAllowNetworkingForProcess) {
+ SKIP_IF_NO_NETWORK_CONNECTIVITY;
+ // At the beginning, we should be able to use socket since the default setting is allowing.
+ expectHasNetworking();
+ // Disable
+ setAllowNetworkingForProcess(false);
+ expectNoNetworking();
+ // Reset
+ setAllowNetworkingForProcess(true);
+ expectHasNetworking();
+}