Merge "Sanitize owner UID iff owning app does not have location permissions." am: 4d95254cb3 am: aba23b3f43 am: a06f220d24

Change-Id: I9b8fb3ff8d907ecda918a8a926edb133b742c8f1
This commit is contained in:
Automerger Merge Worker
2020-02-20 05:59:35 +00:00
5 changed files with 185 additions and 62 deletions

View File

@@ -1279,7 +1279,8 @@ public class ConnectivityManager {
@UnsupportedAppUsage
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
return mService.getDefaultNetworkCapabilitiesForUser(userId);
return mService.getDefaultNetworkCapabilitiesForUser(
userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1361,7 +1362,7 @@ public class ConnectivityManager {
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
return mService.getNetworkCapabilities(network);
return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4039,10 +4040,9 @@ public class ConnectivityManager {
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingRequestForNetwork(
request.networkCapabilities, operation, callingPackageName);
request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4154,10 +4154,9 @@ public class ConnectivityManager {
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingListenForNetwork(
request.networkCapabilities, operation, callingPackageName);
request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {

View File

@@ -60,7 +60,8 @@ interface IConnectivityManager
NetworkInfo[] getAllNetworkInfo();
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
int userId, String callingPackageName);
boolean isNetworkSupported(int networkType);
@@ -69,7 +70,7 @@ interface IConnectivityManager
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
NetworkCapabilities getNetworkCapabilities(in Network network);
NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
@UnsupportedAppUsage
NetworkState[] getAllNetworkState();

View File

@@ -830,6 +830,23 @@ public final class NetworkCapabilities implements Parcelable {
* <p>This field keeps track of the UID of the app that created this network and is in charge of
* its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
* VPN, or Carrier Service app managing a cellular data connection.
*
* <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
* reset to Process.INVALID_UID unless all the following conditions are met:
*
* <ol>
* <li>The destination app is the network owner
* <li>The destination app has the ACCESS_FINE_LOCATION permission granted
* <li>The user's location toggle is on
* </ol>
*
* This is because the owner UID is location-sensitive. The apps that request a network could
* know where the device is if they can tell for sure the system has connected to the network
* they requested.
*
* <p>This is populated by the network agents and for the NetworkCapabilities instance sent by
* an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system
* server.
*/
private int mOwnerUid = Process.INVALID_UID;
@@ -842,7 +859,16 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
* Retrieves the UID of the owner app.
* Retrieves the UID of the app that owns this network.
*
* <p>For user privacy reasons, this field will only be populated if:
*
* <ol>
* <li>The calling app is the network owner
* <li>The calling app has the ACCESS_FINE_LOCATION permission granted
* <li>The user's location toggle is on
* </ol>
*
*/
public int getOwnerUid() {
return mOwnerUid;
@@ -880,8 +906,9 @@ public final class NetworkCapabilities implements Parcelable {
* @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
*/
@NonNull
@SystemApi
public @NonNull NetworkCapabilities setAdministratorUids(
public NetworkCapabilities setAdministratorUids(
@NonNull final List<Integer> administratorUids) {
mAdministratorUids.clear();
mAdministratorUids.addAll(administratorUids);

View File

@@ -1534,7 +1534,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
int userId, String callingPackageName) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1556,7 +1557,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkAgentInfo nai = getDefaultNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
result.put(nai.network, nc);
result.put(
nai.network,
maybeSanitizeLocationInfoForCaller(
nc, Binder.getCallingUid(), callingPackageName));
}
synchronized (mVpns) {
@@ -1566,10 +1570,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
Network[] networks = vpn.getUnderlyingNetworks();
if (networks != null) {
for (Network network : networks) {
nai = getNetworkAgentInfoForNetwork(network);
nc = getNetworkCapabilitiesInternal(nai);
nc = getNetworkCapabilitiesInternal(network);
if (nc != null) {
result.put(network, nc);
result.put(
network,
maybeSanitizeLocationInfoForCaller(
nc, Binder.getCallingUid(), callingPackageName));
}
}
}
@@ -1636,20 +1642,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) {
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
if (nai == null) return null;
synchronized (nai) {
if (nai.networkCapabilities == null) return null;
return networkCapabilitiesRestrictedForCallerPermissions(
nai.networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
}
}
@Override
public NetworkCapabilities getNetworkCapabilities(Network network) {
public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
enforceAccessPermission();
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
return maybeSanitizeLocationInfoForCaller(
getNetworkCapabilitiesInternal(network),
Binder.getCallingUid(), callingPackageName);
}
@VisibleForTesting
@@ -1665,20 +1677,34 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
newNc.setAdministratorUids(Collections.EMPTY_LIST);
maybeSanitizeLocationInfoForCaller(newNc, callerUid);
return newNc;
}
private void maybeSanitizeLocationInfoForCaller(
NetworkCapabilities nc, int callerUid) {
// TODO(b/142072839): Conditionally reset the owner UID if the following
// conditions are not met:
// 1. The destination app is the network owner
// 2. The destination app has the ACCESS_COARSE_LOCATION permission granted
// if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted
// 3. The user's location toggle is on
nc.setOwnerUid(INVALID_UID);
@VisibleForTesting
@Nullable
NetworkCapabilities maybeSanitizeLocationInfoForCaller(
@Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
if (nc == null) {
return null;
}
final NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (callerUid != newNc.getOwnerUid()) {
newNc.setOwnerUid(INVALID_UID);
return newNc;
}
Binder.withCleanCallingIdentity(
() -> {
if (!mLocationPermissionChecker.checkLocationPermission(
callerPkgName, null /* featureId */, callerUid, null /* message */)) {
// Caller does not have the requisite location permissions. Reset the
// owner's UID in the NetworkCapabilities.
newNc.setOwnerUid(INVALID_UID);
}
}
);
return newNc;
}
private LinkProperties linkPropertiesRestrictedForCallerPermissions(
@@ -1753,7 +1779,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork());
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
@@ -5330,8 +5356,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
public String toString() {
return "uid/pid:" + mUid + "/" + mPid + " " + request +
(mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
return "uid/pid:" + mUid + "/" + mPid + " " + request
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -6408,8 +6434,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_AVAILABLE: {
putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
networkAgent.networkCapabilities, nri.mPid, nri.mUid));
final NetworkCapabilities nc =
networkCapabilitiesRestrictedForCallerPermissions(
networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(
bundle,
maybeSanitizeLocationInfoForCaller(
nc, nri.mUid, nri.request.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6422,9 +6453,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
final NetworkCapabilities netCap =
networkCapabilitiesRestrictedForCallerPermissions(
networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(bundle, nc);
putParcelable(
bundle,
maybeSanitizeLocationInfoForCaller(
netCap, nri.mUid, nri.request.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {

View File

@@ -1180,6 +1180,10 @@ public class ConnectivityServiceTest {
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -3042,7 +3046,7 @@ public class ConnectivityServiceTest {
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
});
class NonParcelableSpecifier extends NetworkSpecifier {
@@ -6439,17 +6443,89 @@ public class ConnectivityServiceTest {
assertEquals(wifiLp, mService.getActiveLinkProperties());
}
private void setupLocationPermissions(
int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
if (op != null) {
when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
.thenReturn(AppOpsManager.MODE_ALLOWED);
}
if (perm != null) {
mServiceContext.setPermission(perm, PERMISSION_GRANTED);
}
}
private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
return mService
.maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
.getOwnerUid();
}
@Test
public void testNetworkCapabilitiesRestrictedForCallerPermissions() {
int callerUid = Process.myUid();
final NetworkCapabilities originalNc = new NetworkCapabilities();
originalNc.setOwnerUid(callerUid);
public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final NetworkCapabilities newNc =
mService.networkCapabilitiesRestrictedForCallerPermissions(
originalNc, Process.myPid(), callerUid);
final int myUid = Process.myUid();
assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
assertEquals(Process.INVALID_UID, newNc.getOwnerUid());
@Test
public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
final int myUid = Process.myUid();
assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
@Test
public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
// Test that even with fine location permission, and UIDs matching, the UID is sanitized.
setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
@Test
public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
// Test that even with fine location permission, not being the owner leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
}
@Test
public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
// Test that not having fine location permission leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
// Test that without the location permission, the owner field is sanitized.
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
@Test
public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
// Test that without the location permission, the owner field is sanitized.
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -6735,21 +6811,6 @@ public class ConnectivityServiceTest {
mContext.getOpPackageName()));
}
private void setupLocationPermissions(
int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mServiceContext.setPermission(perm, PERMISSION_GRANTED);
}
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);