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
This commit is contained in:
Lorenzo Colitti
2016-07-28 17:14:11 +09:00
parent 73e97b9fdb
commit b8d9f52adb
2 changed files with 37 additions and 10 deletions

View File

@@ -1825,6 +1825,16 @@ public class ConnectivityManager {
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 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} */ /** {@hide} */
public static final void enforceChangePermission(Context context) { public static final void enforceChangePermission(Context context) {
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();

View File

@@ -4330,8 +4330,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceAccessPermission(); enforceAccessPermission();
} }
NetworkRequest networkRequest = new NetworkRequest( NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(), 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); NetworkRequest.Type.LISTEN);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
if (VDBG) log("listenForNetwork for " + nri); if (VDBG) log("listenForNetwork for " + nri);
@@ -4646,6 +4655,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNumDnsEntries = last; 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} * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
* augmented with any stateful capabilities implied from {@code networkAgent} * 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 (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) != final String oldPermission = getNetworkPermission(nai.networkCapabilities);
networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { final String newPermission = getNetworkPermission(networkCapabilities);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try { try {
mNetd.setNetworkPermission(nai.network.netId, mNetd.setNetworkPermission(nai.network.netId, newPermission);
networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) ?
null : NetworkManagementService.PERMISSION_SYSTEM);
} catch (RemoteException e) { } catch (RemoteException e) {
loge("Exception in setNetworkPermission: " + e); loge("Exception in setNetworkPermission: " + e);
} }
@@ -5259,9 +5278,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
!networkAgent.networkMisc.allowBypass)); !networkAgent.networkMisc.allowBypass));
} else { } else {
mNetd.createPhysicalNetwork(networkAgent.network.netId, mNetd.createPhysicalNetwork(networkAgent.network.netId,
networkAgent.networkCapabilities.hasCapability( getNetworkPermission(networkAgent.networkCapabilities));
NET_CAPABILITY_NOT_RESTRICTED) ?
null : NetworkManagementService.PERMISSION_SYSTEM);
} }
} catch (Exception e) { } catch (Exception e) {
loge("Error creating network " + networkAgent.network.netId + ": " loge("Error creating network " + networkAgent.network.netId + ": "