Disable sockets and DNS if process lacks INTERNET permission.
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 NetworkUtilsTest
Test: atest CtsAppSecurityHostTestCases:UseProcessTest
Change-Id: If002280fbad493dfc2db3d9d505c0257d49a9056
Exempt-From-Owner-Approval: OWNERS already approved identical patchset 5
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 779f7bc..0b92b95 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -155,6 +155,14 @@
public static native Network getDnsNetwork() throws ErrnoException;
/**
+ * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
+ *
+ * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
+ * and DNS lookups.
+ */
+ public static native void setAllowNetworkingForProcess(boolean allowNetworking);
+
+ /**
* Get the tcp repair window associated with the {@code fd}.
*
* @param fd the tcp socket's {@link FileDescriptor}.
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 03b9793..d4805ac 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -226,6 +226,11 @@
class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
}
+static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
+ jboolean hasConnectivity) {
+ setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
+}
+
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
if (javaFd == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -266,6 +271,7 @@
/*
* JNI registration.
*/
+// clang-format off
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
@@ -282,7 +288,9 @@
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
+ { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess },
};
+// clang-format on
int register_android_net_NetworkUtils(JNIEnv* env)
{
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 7748288..3158cc8 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,10 +16,24 @@
package android.net;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
import androidx.test.runner.AndroidJUnit4;
+import libcore.io.IoUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -125,4 +139,50 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
+
+ private static void expectSocketSuccess(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ } catch (ErrnoException e) {
+ fail(msg + e.getMessage());
+ }
+ }
+
+ private static void expectSocketPemissionError(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ fail(msg);
+ } catch (ErrnoException e) {
+ assertEquals(msg, e.errno, EPERM);
+ }
+ }
+
+ private static void expectHasNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
+ AF_INET, SOCK_DGRAM);
+ expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ private static void expectNoNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
+ AF_INET, SOCK_DGRAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ @Test
+ public void testSetAllowNetworkingForProcess() {
+ expectHasNetworking();
+ NetworkUtils.setAllowNetworkingForProcess(false);
+ expectNoNetworking();
+ NetworkUtils.setAllowNetworkingForProcess(true);
+ expectHasNetworking();
+ }
}