Merge "Support strict mode private DNS on VPNs that provide Internet."

This commit is contained in:
Chalard Jean
2019-05-10 09:39:17 +00:00
committed by Gerrit Code Review
3 changed files with 67 additions and 8 deletions

View File

@@ -40,7 +40,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.shared.NetworkMonitorUtils.isValidationRequired; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID; import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.IPPROTO_UDP;
@@ -2824,8 +2824,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
private boolean networkRequiresValidation(NetworkAgentInfo nai) { private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
return isValidationRequired(nai.networkCapabilities); return isPrivateDnsValidationRequired(nai.networkCapabilities);
} }
private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) { private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
@@ -2843,7 +2843,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
handlePerNetworkPrivateDnsConfig(nai, cfg); handlePerNetworkPrivateDnsConfig(nai, cfg);
if (networkRequiresValidation(nai)) { if (networkRequiresPrivateDnsValidation(nai)) {
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
} }
} }
@@ -2852,7 +2852,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) { private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
// Private DNS only ever applies to networks that might provide // Private DNS only ever applies to networks that might provide
// Internet access and therefore also require validation. // Internet access and therefore also require validation.
if (!networkRequiresValidation(nai)) return; if (!networkRequiresPrivateDnsValidation(nai)) return;
// Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or // Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
// schedule DNS resolutions. If a DNS resolution is required the // schedule DNS resolutions. If a DNS resolution is required the

View File

@@ -483,11 +483,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// down an explicitly selected network before the user gets a chance to prefer it when // down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available. // a higher-scoring network (e.g., Ethernet) is available.
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) { if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
return ConnectivityConstants.MAXIMUM_NETWORK_SCORE; return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
} }
int score = currentScore; int score = currentScore;
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) { if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
} }
if (score < 0) score = 0; if (score < 0) score = 0;

View File

@@ -28,6 +28,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
@@ -489,7 +490,7 @@ public class ConnectivityServiceTest {
MockNetworkAgent(int transport, LinkProperties linkProperties) { MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport); final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(transport); final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport); mNetworkCapabilities.addTransportType(transport);
@@ -619,6 +620,10 @@ public class ConnectivityServiceTest {
mNetworkAgent.sendNetworkScore(mScore); mNetworkAgent.sendNetworkScore(mScore);
} }
public int getScore() {
return mScore;
}
public void explicitlySelected(boolean acceptUnvalidated) { public void explicitlySelected(boolean acceptUnvalidated) {
mNetworkAgent.explicitlySelected(acceptUnvalidated); mNetworkAgent.explicitlySelected(acceptUnvalidated);
} }
@@ -1330,6 +1335,8 @@ public class ConnectivityServiceTest {
return TYPE_WIFI; return TYPE_WIFI;
case TRANSPORT_CELLULAR: case TRANSPORT_CELLULAR:
return TYPE_MOBILE; return TYPE_MOBILE;
case TRANSPORT_VPN:
return TYPE_VPN;
default: default:
return TYPE_NONE; return TYPE_NONE;
} }
@@ -5369,6 +5376,58 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(defaultCallback); mCm.unregisterNetworkCallback(defaultCallback);
} }
@Test
public void testVpnUnvalidated() throws Exception {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(callback);
// Bring up Ethernet.
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
final int uid = Process.myUid();
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
// TODO: this looks like a spurious callback.
callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
callback.assertNoCallback();
assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
vpnNetworkAgent.mNetworkCapabilities));
// Pretend that the VPN network validates.
vpnNetworkAgent.setNetworkValid();
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
callback.assertNoCallback();
vpnNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@Test @Test
public void testVpnSetUnderlyingNetworks() { public void testVpnSetUnderlyingNetworks() {
final int uid = Process.myUid(); final int uid = Process.myUid();