summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Huang <huangluke@google.com>2020-06-16 19:14:05 +0800
committerLuke Huang <huangluke@google.com>2020-06-18 04:38:44 +0800
commit86983208a199d147d63033aec7794666e55d27a8 (patch)
treeac16144b2380c0b91aa7e1e63ca599e7315822cd
parenta60b74e9f13c2f5cf618797bb3e11d56d5734591 (diff)
downloadnetd-86983208a199d147d63033aec7794666e55d27a8.tar.gz
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
-rw-r--r--client/NetdClient.cpp17
-rw-r--r--include/NetdClient.h2
-rw-r--r--tests/Android.bp1
-rw-r--r--tests/netd_client_test.cpp113
4 files changed, 133 insertions, 0 deletions
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 74a441ae..0105a047 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -60,6 +60,7 @@ constexpr char PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED[] = "net.redirect_socket_ca
std::atomic_uint netIdForProcess(NETID_UNSET);
std::atomic_uint netIdForResolv(NETID_UNSET);
+std::atomic_bool allowNetworkingForProcess(true);
typedef int (*Accept4FunctionType)(int, sockaddr*, socklen_t*, int);
typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
@@ -182,6 +183,11 @@ int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
}
int netdClientSocket(int domain, int type, int protocol) {
+ // Block creating AF_INET/AF_INET6 socket if networking is not allowed.
+ if (FwmarkCommand::isSupportedFamily(domain) && !allowNetworkingForProcess.load()) {
+ errno = EPERM;
+ return -1;
+ }
int socketFd = libcSocket(domain, type, protocol);
if (socketFd == -1) {
return -1;
@@ -285,6 +291,13 @@ int dns_open_proxy() {
return -1;
}
+ // If networking is not allowed, dns_open_proxy should just fail here.
+ // Then eventually, the DNS related functions in local mode will get
+ // EPERM while creating socket.
+ if (!allowNetworkingForProcess.load()) {
+ errno = EPERM;
+ return -1;
+ }
const auto socketFunc = libcSocket ? libcSocket : socket;
int s = socketFunc(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (s == -1) {
@@ -593,6 +606,10 @@ extern "C" void resNetworkCancel(int fd) {
close(fd);
}
+extern "C" void setAllowNetworkingForProcess(bool allowNetworking) {
+ allowNetworkingForProcess.store(allowNetworking);
+}
+
extern "C" int getNetworkForDns(unsigned* dnsNetId) {
if (dnsNetId == nullptr) return -EFAULT;
int fd = dns_open_proxy();
diff --git a/include/NetdClient.h b/include/NetdClient.h
index 89f0a41a..cc835ede 100644
--- a/include/NetdClient.h
+++ b/include/NetdClient.h
@@ -40,6 +40,8 @@ int setNetworkForUser(uid_t uid, int socketFd);
int queryUserAccess(uid_t uid, unsigned netId);
+void setAllowNetworkingForProcess(bool allowNetworking);
+
int tagSocket(int socketFd, uint32_t tag, uid_t uid);
int untagSocket(int socketFd);
diff --git a/tests/Android.bp b/tests/Android.bp
index f696bab6..3baaa184 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -71,6 +71,7 @@ cc_test {
":netd_integration_test_shared",
"binder_test.cpp",
"bpf_base_test.cpp",
+ "netd_client_test.cpp",
"netd_test.cpp",
"netlink_listener_test.cpp",
],
diff --git a/tests/netd_client_test.cpp b/tests/netd_client_test.cpp
new file mode 100644
index 00000000..c8af408b
--- /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();
+}