Re-implement NetworkUtils#queryUserAccess.

Currently, queryUserAccess talks to netd via FwmarkServer.
Doing this from the module would require exposing queryUserAccess
as an NDK API or reimplementing FwmarkClient.

Because queryUserAccess really only uses information that comes
from ConnectivityService/PermissionMonitor anyway, just use that
information without calling to net.

Test: atest HostsideVpnTests
Bug: 171540887
Change-Id: If855de1ea3e1fd2ed30f2795d9b4acfcf969a2dc
This commit is contained in:
Lorenzo Colitti
2021-03-23 21:01:07 +09:00
committed by Remi NGUYEN VAN
parent 0da9e8b1e7
commit d81932b836
5 changed files with 51 additions and 11 deletions

View File

@@ -123,11 +123,6 @@ static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, job
return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
}
static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
{
if (env->GetArrayLength(addr) != len) {
@@ -267,7 +262,6 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "getBoundNetworkHandleForProcess", "()J", (void*) android_net_utils_getBoundNetworkHandleForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
{ "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },

View File

@@ -103,7 +103,10 @@ public class NetworkUtils {
* Determine if {@code uid} can access network designated by {@code netId}.
* @return {@code true} if {@code uid} can access network, {@code false} otherwise.
*/
public native static boolean queryUserAccess(int uid, int netId);
public static boolean queryUserAccess(int uid, int netId) {
// TODO (b/183485986): remove this method
return false;
}
/**
* DNS resolver series jni method.

View File

@@ -1145,8 +1145,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
/**
* @see NetworkUtils#queryUserAccess(int, int)
*/
public boolean queryUserAccess(int uid, int netId) {
return NetworkUtils.queryUserAccess(uid, netId);
public boolean queryUserAccess(int uid, Network network, ConnectivityService cs) {
return cs.queryUserAccess(uid, network);
}
/**
@@ -4839,6 +4839,42 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.networkMonitor().forceReevaluation(uid);
}
// TODO: call into netd.
private boolean queryUserAccess(int uid, Network network) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return false;
// Any UID can use its default network.
if (nai == getDefaultNetworkForUid(uid)) return true;
// Privileged apps can use any network.
if (mPermissionMonitor.hasRestrictedNetworksPermission(uid)) {
return true;
}
// An unprivileged UID can use a VPN iff the VPN applies to it.
if (nai.isVPN()) {
return nai.networkCapabilities.appliesToUid(uid);
}
// An unprivileged UID can bypass the VPN that applies to it only if it can protect its
// sockets, i.e., if it is the owner.
final NetworkAgentInfo vpn = getVpnForUid(uid);
if (vpn != null && !vpn.networkAgentConfig.allowBypass
&& uid != vpn.networkCapabilities.getOwnerUid()) {
return false;
}
// The UID's permission must be at least sufficient for the network. Since the restricted
// permission was already checked above, that just leaves background networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) {
return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
}
// Unrestricted network. Anyone gets to use it.
return true;
}
/**
* Returns information about the proxy a certain network is using. If given a null network, it
* it will return the proxy for the bound network for the caller app or the default proxy if
@@ -4859,7 +4895,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
return getLinkPropertiesProxyInfo(activeNetwork);
} else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network.getNetId())) {
} else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network, this)) {
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
return getLinkPropertiesProxyInfo(network);

View File

@@ -271,6 +271,13 @@ public class PermissionMonitor {
return mApps.containsKey(uid);
}
/**
* Returns whether the given uid has permission to use restricted networks.
*/
public synchronized boolean hasRestrictedNetworksPermission(int uid) {
return Boolean.TRUE.equals(mApps.get(uid));
}
private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
List<Integer> network = new ArrayList<>();
List<Integer> system = new ArrayList<>();

View File

@@ -1556,7 +1556,7 @@ public class ConnectivityServiceTest {
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));