From b8d9f52adb2b51bde74a9c3f52a5d8ac5130e684 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 28 Jul 2016 17:14:11 +0900 Subject: [PATCH] Restrict access to background networks to CHANGE_NETWORK_STATE. When a network goes into the background, tell netd to set the network's permission to NETWORK. Also, close all TCP sockets on that network, to prevent long-lived TCP connections from staying on it and possibly continuing to use metered data. Bug: 23113288 Change-Id: Ie89c1940b6739160e25c6e9022b8b977afb3e16e --- .../java/android/net/ConnectivityManager.java | 10 +++++ .../android/server/ConnectivityService.java | 37 ++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index b9e9b28310..0afb546aa4 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1825,6 +1825,16 @@ public class ConnectivityManager { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } + /* TODO: These permissions checks don't belong in client-side code. Move them to + * services.jar, possibly in com.android.server.net. */ + + /** {@hide} */ + public static final boolean checkChangePermission(Context context) { + int uid = Binder.getCallingUid(); + return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings + .getPackageNameForUid(context, uid), false /* throwException */); + } + /** {@hide} */ public static final void enforceChangePermission(Context context) { int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 92cb3e6904..524c3cc87c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4330,8 +4330,17 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceAccessPermission(); } - NetworkRequest networkRequest = new NetworkRequest( - new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(), + NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + if (!ConnectivityManager.checkChangePermission(mContext)) { + // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so + // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get + // onLost and onAvailable callbacks when networks move in and out of the background. + // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE + // can't request networks. + nc.addCapability(NET_CAPABILITY_FOREGROUND); + } + + NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); if (VDBG) log("listenForNetwork for " + nri); @@ -4646,6 +4655,17 @@ public class ConnectivityService extends IConnectivityManager.Stub mNumDnsEntries = last; } + private String getNetworkPermission(NetworkCapabilities nc) { + // TODO: make these permission strings AIDL constants instead. + if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { + return NetworkManagementService.PERMISSION_SYSTEM; + } + if (!nc.hasCapability(NET_CAPABILITY_FOREGROUND)) { + return NetworkManagementService.PERMISSION_NETWORK; + } + return null; + } + /** * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities} * augmented with any stateful capabilities implied from {@code networkAgent} @@ -4684,12 +4704,11 @@ public class ConnectivityService extends IConnectivityManager.Stub if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return; - if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) != - networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { + final String oldPermission = getNetworkPermission(nai.networkCapabilities); + final String newPermission = getNetworkPermission(networkCapabilities); + if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) { try { - mNetd.setNetworkPermission(nai.network.netId, - networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) ? - null : NetworkManagementService.PERMISSION_SYSTEM); + mNetd.setNetworkPermission(nai.network.netId, newPermission); } catch (RemoteException e) { loge("Exception in setNetworkPermission: " + e); } @@ -5259,9 +5278,7 @@ public class ConnectivityService extends IConnectivityManager.Stub !networkAgent.networkMisc.allowBypass)); } else { mNetd.createPhysicalNetwork(networkAgent.network.netId, - networkAgent.networkCapabilities.hasCapability( - NET_CAPABILITY_NOT_RESTRICTED) ? - null : NetworkManagementService.PERMISSION_SYSTEM); + getNetworkPermission(networkAgent.networkCapabilities)); } } catch (Exception e) { loge("Error creating network " + networkAgent.network.netId + ": "