Merge "Enterprise slicing for profile blocking default" am: 73bf06f6b5 am: 784b333169

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2361702

Change-Id: Ida4505a47d93d7ee57b47dc11a4bd6afc3eca565
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Junyu Lai 2023-01-11 08:35:20 +00:00 • committed by Automerger Merge Worker
commit c9f27862d5
5 changed files with 288 additions and 9 deletions

View file

@ -63,6 +63,7 @@ package android.net {
field public static final int FIREWALL_RULE_DENY = 2; // 0x2
field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING = 3; // 0x3
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2; // 0x2
}

View file

@ -1232,16 +1232,19 @@ public class ConnectivityManager {
}
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
* Preference for {@link ProfileNetworkPreference.Builder#setPreference(int)}.
* See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by follow the default rules.
* Specify that the traffic for this user should by follow the default rules:
* applications in the profile designated by the UserHandle behave like any
* other application and use the system default network as their default
* network. Compare other PROFILE_NETWORK_PREFERENCE_* settings.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
* Preference for {@link ProfileNetworkPreference.Builder#setPreference(int)}.
* See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
@ -1252,16 +1255,38 @@ public class ConnectivityManager {
public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
* Preference for {@link ProfileNetworkPreference.Builder#setPreference(int)}.
* See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE} and if no such network is available
* should not go on the system default network
* should not have a default network at all (that is, network accesses that
* do not specify a network explicitly terminate with an error), even if there
* is a system default network available to apps outside this preference.
* The apps can still use a non-enterprise network if they request it explicitly
* provided that specific network doesn't require any specific permission they
* do not hold.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2;
/**
* Preference for {@link ProfileNetworkPreference.Builder#setPreference(int)}.
* See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}.
* If there is no such network, the apps will have no default
* network at all, even if there are available non-enterprise networks on the
* device (that is, network accesses that do not specify a network explicitly
* terminate with an error). Additionally, the designated apps should be
* blocked from using any non-enterprise network even if they specify it
* explicitly, unless they hold specific privilege overriding this (see
* {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS}).
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {

View file

@ -4366,6 +4366,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworkForNetId.remove(nai.network.getNetId());
}
propagateUnderlyingNetworkCapabilities(nai.network);
// Update allowed network lists in netd. This should be called after removing nai
// from mNetworkAgentInfos.
updateProfileAllowedNetworks();
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
final NetworkRequest request = nai.requestAt(i);
@ -4800,6 +4803,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
}
nri.mPerUidCounter.decrementCount(nri.mUid);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
checkNrisConsistency(nri);
@ -6166,12 +6170,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
if (!mProfileNetworkPreferences.isEmpty()) {
updateProfileAllowedNetworks();
}
}
private void onUserRemoved(@NonNull final UserHandle user) {
// If there was a network preference for this user, remove it.
handleSetProfileNetworkPreference(
List.of(new ProfileNetworkPreferenceInfo(user, null, true)),
List.of(new ProfileNetworkPreferenceInfo(user, null, true,
false /* blockingNonEnterprise */)),
null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
@ -8688,6 +8696,73 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* Collect restricted uid ranges for the given network and UserHandle, these uids
* are not restricted for matched enterprise networks but being restricted for non-matched
* enterprise networks and non-enterprise networks.
*/
@NonNull
private ArraySet<UidRange> getRestrictedUidRangesForEnterpriseBlocking(
@NonNull NetworkAgentInfo nai, @NonNull UserHandle user) {
final ArraySet<UidRange> restrictedUidRanges = new ArraySet<>();
for (final ProfileNetworkPreferenceInfo pref : mProfileNetworkPreferences) {
if (!pref.user.equals(user) || !pref.blockingNonEnterprise) continue;
if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_ENTERPRISE)) {
// The NC is built from a `ProfileNetworkPreference` which has only one
// enterprise ID, so it's guaranteed to have exactly one.
final int prefId = pref.capabilities.getEnterpriseIds()[0];
if (nai.networkCapabilities.hasEnterpriseId(prefId)) {
continue;
}
}
if (UidRangeUtils.doesRangeSetOverlap(restrictedUidRanges,
pref.capabilities.getUidRanges())) {
throw new IllegalArgumentException(
"Overlapping uid range in preference: " + pref);
}
restrictedUidRanges.addAll(pref.capabilities.getUidRanges());
}
return restrictedUidRanges;
}
private void updateProfileAllowedNetworks() {
ensureRunningOnConnectivityServiceThread();
final ArrayList<NativeUidRangeConfig> configs = new ArrayList<>();
final List<UserHandle> users = mContext.getSystemService(UserManager.class)
.getUserHandles(true /* excludeDying */);
if (users.isEmpty()) {
throw new IllegalStateException("No user is available");
}
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
ArraySet<UidRange> allowedUidRanges = new ArraySet<>();
for (final UserHandle user : users) {
final ArraySet<UidRange> restrictedUidRanges =
getRestrictedUidRangesForEnterpriseBlocking(nai, user);
allowedUidRanges.addAll(UidRangeUtils.removeRangeSetFromUidRange(
UidRange.createForUser(user), restrictedUidRanges));
}
final UidRangeParcel[] rangesParcel = toUidRangeStableParcels(allowedUidRanges);
configs.add(new NativeUidRangeConfig(
nai.network.netId, rangesParcel, 0 /* subPriority */));
}
// The netd API replaces the previous configs with the current configs.
// Thus, for network disconnection or preference removal, no need to
// unset previous config. Instead, collecting all currently needed
// configs and issue to netd.
try {
mNetd.setNetworkAllowlist(configs.toArray(new NativeUidRangeConfig[0]));
} catch (ServiceSpecificException e) {
// Has the interface disappeared since the network was built?
} catch (RemoteException e) {
// Netd died. This usually causes a runtime restart anyway.
}
}
private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
try {
if (null != newDefaultNetwork) {
@ -9320,6 +9395,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkAgent.setCreated();
networkAgent.onNetworkCreated();
updateAllowedUids(networkAgent, null, networkAgent.networkCapabilities);
updateProfileAllowedNetworks();
}
if (!networkAgent.everConnected() && state == NetworkInfo.State.CONNECTED) {
@ -10856,6 +10932,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (final ProfileNetworkPreference preference : preferences) {
final NetworkCapabilities nc;
boolean allowFallback = true;
boolean blockingNonEnterprise = false;
switch (preference.getPreference()) {
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
nc = null;
@ -10865,6 +10942,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
"Invalid enterprise identifier in setProfileNetworkPreferences");
}
break;
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING:
blockingNonEnterprise = true;
// continue to process the enterprise preference.
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK:
allowFallback = false;
// continue to process the enterprise preference.
@ -10898,7 +10978,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
throw new IllegalArgumentException(
"Invalid preference in setProfileNetworkPreferences");
}
preferenceList.add(new ProfileNetworkPreferenceInfo(profile, nc, allowFallback));
preferenceList.add(new ProfileNetworkPreferenceInfo(
profile, nc, allowFallback, blockingNonEnterprise));
if (hasDefaultPreference && preferenceList.size() > 1) {
throw new IllegalArgumentException(
"Default profile preference should not be set along with other preference");
@ -11011,6 +11092,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
addPerAppDefaultNetworkRequests(
createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
updateProfileAllowedNetworks();
// Finally, rematch.
rematchAllNetworksAndRequests();

View file

@ -32,13 +32,15 @@ public class ProfileNetworkPreferenceInfo
@Nullable
public final NetworkCapabilities capabilities;
public final boolean allowFallback;
public final boolean blockingNonEnterprise;
public ProfileNetworkPreferenceInfo(@NonNull final UserHandle user,
@Nullable final NetworkCapabilities capabilities,
final boolean allowFallback) {
final boolean allowFallback, final boolean blockingNonEnterprise) {
this.user = user;
this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
this.allowFallback = allowFallback;
this.blockingNonEnterprise = blockingNonEnterprise;
}
@Override
@ -57,6 +59,7 @@ public class ProfileNetworkPreferenceInfo
return "[ProfileNetworkPreference user=" + user
+ " caps=" + capabilities
+ " allowFallback=" + allowFallback
+ " blockingNonEnterprise=" + blockingNonEnterprise
+ "]";
}
}

View file

@ -64,6 +64,7 @@ import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
@ -10428,6 +10429,7 @@ public class ConnectivityServiceTest {
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
verify(mMockNetd).setNetworkAllowlist(any());
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mClatCoordinator);
reset(mMockNetd);
@ -10468,6 +10470,7 @@ public class ConnectivityServiceTest {
verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
verify(mMockNetd).setNetworkAllowlist(any());
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mClatCoordinator);
@ -15779,6 +15782,171 @@ public class ConnectivityServiceTest {
PREFERENCE_ORDER_PROFILE));
}
@Test
public void testProfileNetworkPreferenceBlocking_changePreference() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
doReturn(asList(PRIMARY_USER_HANDLE, testHandle))
.when(mUserManager).getUserHandles(anyBoolean());
// Start with 1 default network and 1 enterprise network, both networks should
// not be restricted since the blocking preference is not set yet.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
// Verify uid ranges 0~99999, 200000~299999 are all allowed for cellular.
final UidRange profileUidRange =
UidRange.createForUser(UserHandle.of(TEST_WORK_PROFILE_USER_ID));
ArraySet<UidRange> allowedAllUidRanges = new ArraySet<>();
allowedAllUidRanges.add(PRIMARY_UIDRANGE);
allowedAllUidRanges.add(profileUidRange);
final UidRangeParcel[] allowAllUidRangesParcel = toUidRangeStableParcels(
allowedAllUidRanges);
final NativeUidRangeConfig cellAllAllowedConfig = new NativeUidRangeConfig(
mCellNetworkAgent.getNetwork().netId,
allowAllUidRangesParcel,
0 /* subPriority */);
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellAllAllowedConfig});
// Verify the same uid ranges are also applied for enterprise network.
final TestNetworkAgentWrapper enterpriseAgent = makeEnterpriseNetworkAgent(
NET_ENTERPRISE_ID_1);
enterpriseAgent.connect(true);
final NativeUidRangeConfig enterpriseAllAllowedConfig = new NativeUidRangeConfig(
enterpriseAgent.getNetwork().netId,
allowAllUidRangesParcel,
0 /* subPriority */);
// Network agents are stored in an ArraySet which does not guarantee the order and
// making the order of the list undeterministic. Thus, verify this in order insensitive way.
final ArgumentCaptor<NativeUidRangeConfig[]> configsCaptor = ArgumentCaptor.forClass(
NativeUidRangeConfig[].class);
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellAllAllowedConfig, enterpriseAllAllowedConfig));
// Setup profile preference which only applies to test app uid on the managed profile.
ProfileNetworkPreference.Builder prefBuilder = new ProfileNetworkPreference.Builder();
prefBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING)
.setIncludedUids(new int[]{testHandle.getUid(TEST_WORK_PROFILE_APP_UID)})
.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreferences(testHandle,
List.of(prefBuilder.build()),
r -> r.run(), listener);
listener.expectOnComplete();
// Verify Netd is called for the preferences changed.
// Cell: 0~99999, 200000~TEST_APP_UID-1, TEST_APP_UID+1~299999
// Enterprise: 0~99999, 200000~299999
final ArraySet<UidRange> excludeAppRanges = new ArraySet<>();
excludeAppRanges.add(PRIMARY_UIDRANGE);
excludeAppRanges.addAll(UidRangeUtils.removeRangeSetFromUidRange(
profileUidRange,
new ArraySet(new UidRange[]{
(new UidRange(TEST_WORK_PROFILE_APP_UID, TEST_WORK_PROFILE_APP_UID))})
));
final UidRangeParcel[] excludeAppRangesParcel = toUidRangeStableParcels(excludeAppRanges);
final NativeUidRangeConfig cellExcludeAppConfig = new NativeUidRangeConfig(
mCellNetworkAgent.getNetwork().netId,
excludeAppRangesParcel,
0 /* subPriority */);
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellExcludeAppConfig, enterpriseAllAllowedConfig));
// Verify unset by giving all allowed set for all users when the preference got removed.
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellAllAllowedConfig, enterpriseAllAllowedConfig));
// Verify issuing with cellular set only when a network with enterprise capability
// disconnects.
enterpriseAgent.disconnect();
waitForIdle();
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellAllAllowedConfig});
}
@Test
public void testProfileNetworkPreferenceBlocking_networkChanges() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
doReturn(asList(PRIMARY_USER_HANDLE, testHandle))
.when(mUserManager).getUserHandles(anyBoolean());
// Setup profile preference which only applies to test app uid on the managed profile.
ProfileNetworkPreference.Builder prefBuilder = new ProfileNetworkPreference.Builder();
prefBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING)
.setIncludedUids(new int[]{testHandle.getUid(TEST_WORK_PROFILE_APP_UID)})
.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreferences(testHandle,
List.of(prefBuilder.build()),
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{});
// Start with 1 default network, which should be restricted since the blocking
// preference is already set.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
// Verify cellular network applies to the allow list.
// Cell: 0~99999, 200000~TEST_APP_UID-1, TEST_APP_UID+1~299999
// Enterprise: 0~99999, 200000~299999
final ArraySet<UidRange> excludeAppRanges = new ArraySet<>();
final UidRange profileUidRange =
UidRange.createForUser(UserHandle.of(TEST_WORK_PROFILE_USER_ID));
excludeAppRanges.add(PRIMARY_UIDRANGE);
excludeAppRanges.addAll(UidRangeUtils.removeRangeSetFromUidRange(
profileUidRange,
new ArraySet(new UidRange[]{
(new UidRange(TEST_WORK_PROFILE_APP_UID, TEST_WORK_PROFILE_APP_UID))})
));
final UidRangeParcel[] excludeAppRangesParcel = toUidRangeStableParcels(excludeAppRanges);
final NativeUidRangeConfig cellExcludeAppConfig = new NativeUidRangeConfig(
mCellNetworkAgent.getNetwork().netId,
excludeAppRangesParcel,
0 /* subPriority */);
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellExcludeAppConfig});
// Verify enterprise network is not blocked for test app.
final TestNetworkAgentWrapper enterpriseAgent = makeEnterpriseNetworkAgent(
NET_ENTERPRISE_ID_1);
enterpriseAgent.connect(true);
ArraySet<UidRange> allowedAllUidRanges = new ArraySet<>();
allowedAllUidRanges.add(PRIMARY_UIDRANGE);
allowedAllUidRanges.add(profileUidRange);
final UidRangeParcel[] allowAllUidRangesParcel = toUidRangeStableParcels(
allowedAllUidRanges);
final NativeUidRangeConfig enterpriseAllAllowedConfig = new NativeUidRangeConfig(
enterpriseAgent.getNetwork().netId,
allowAllUidRangesParcel,
0 /* subPriority */);
// Network agents are stored in an ArraySet which does not guarantee the order and
// making the order of the list undeterministic. Thus, verify this in order insensitive way.
final ArgumentCaptor<NativeUidRangeConfig[]> configsCaptor = ArgumentCaptor.forClass(
NativeUidRangeConfig[].class);
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(enterpriseAllAllowedConfig, cellExcludeAppConfig));
// Verify issuing with cellular set only when enterprise network disconnects.
enterpriseAgent.disconnect();
waitForIdle();
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellExcludeAppConfig});
mCellNetworkAgent.disconnect();
waitForIdle();
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{});
}
/**
* Make sure wrong preferences for per-profile default networking are rejected.
*/
@ -15789,7 +15957,7 @@ public class ConnectivityServiceTest {
ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
new ProfileNetworkPreference.Builder();
profileNetworkPreferenceBuilder.setPreference(
PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK + 1);
PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING + 1);
profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
assertThrows("Should not be able to set an illegal preference",
IllegalArgumentException.class,