Merge "Add methods for IKEv2/IPsec test mode profiles" am: f44b90fdc4

Change-Id: I9ff22188a641fce6b7bd44f7619d1b825988667a
This commit is contained in:
Benedict Wong
2020-05-15 02:35:39 +00:00
committed by Automerger Merge Worker
4 changed files with 133 additions and 22 deletions

View File

@@ -101,6 +101,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
private final boolean mIsBypassable; // Defaults in builder
private final boolean mIsMetered; // Defaults in builder
private final int mMaxMtu; // Defaults in builder
private final boolean mIsRestrictedToTestNetworks;
private Ikev2VpnProfile(
int type,
@@ -116,7 +117,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
@NonNull List<String> allowedAlgorithms,
boolean isBypassable,
boolean isMetered,
int maxMtu) {
int maxMtu,
boolean restrictToTestNetworks) {
super(type);
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
@@ -140,6 +142,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mIsBypassable = isBypassable;
mIsMetered = isMetered;
mMaxMtu = maxMtu;
mIsRestrictedToTestNetworks = restrictToTestNetworks;
validate();
}
@@ -329,6 +332,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
return mMaxMtu;
}
/**
* Returns whether or not this VPN profile is restricted to test networks.
*
* @hide
*/
public boolean isRestrictedToTestNetworks() {
return mIsRestrictedToTestNetworks;
}
@Override
public int hashCode() {
return Objects.hash(
@@ -345,7 +357,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mAllowedAlgorithms,
mIsBypassable,
mIsMetered,
mMaxMtu);
mMaxMtu,
mIsRestrictedToTestNetworks);
}
@Override
@@ -368,7 +381,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
&& Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
&& mIsBypassable == other.mIsBypassable
&& mIsMetered == other.mIsMetered
&& mMaxMtu == other.mMaxMtu;
&& mMaxMtu == other.mMaxMtu
&& mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
}
/**
@@ -381,7 +395,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
*/
@NonNull
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
mIsRestrictedToTestNetworks);
profile.type = mType;
profile.server = mServerAddr;
profile.ipsecIdentifier = mUserIdentity;
@@ -449,6 +464,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
builder.setBypassable(profile.isBypassable);
builder.setMetered(profile.isMetered);
builder.setMaxMtu(profile.maxMtu);
if (profile.isRestrictedToTestNetworks) {
builder.restrictToTestNetworks();
}
switch (profile.type) {
case TYPE_IKEV2_IPSEC_USER_PASS:
@@ -621,6 +639,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
private boolean mIsBypassable = false;
private boolean mIsMetered = true;
private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
private boolean mIsRestrictedToTestNetworks = false;
/**
* Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
@@ -841,6 +860,21 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
return this;
}
/**
* Restricts this profile to use test networks (only).
*
* <p>This method is for testing only, and must not be used by apps. Calling
* provisionVpnProfile() with a profile where test-network usage is enabled will require the
* MANAGE_TEST_NETWORKS permission.
*
* @hide
*/
@NonNull
public Builder restrictToTestNetworks() {
mIsRestrictedToTestNetworks = true;
return this;
}
/**
* Validates, builds and provisions the VpnProfile.
*
@@ -862,7 +896,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mAllowedAlgorithms,
mIsBypassable,
mIsMetered,
mMaxMtu);
mMaxMtu,
mIsRestrictedToTestNetworks);
}
}
}

View File

@@ -136,13 +136,19 @@ public final class VpnProfile implements Cloneable, Parcelable {
public boolean isMetered = false; // 21
public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; // 22
public boolean areAuthParamsInline = false; // 23
public final boolean isRestrictedToTestNetworks; // 24
// Helper fields.
@UnsupportedAppUsage
public transient boolean saveLogin = false;
public VpnProfile(String key) {
this(key, false);
}
public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
this.key = key;
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
}
@UnsupportedAppUsage
@@ -171,6 +177,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
isMetered = in.readBoolean();
maxMtu = in.readInt();
areAuthParamsInline = in.readBoolean();
isRestrictedToTestNetworks = in.readBoolean();
}
/**
@@ -220,6 +227,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
out.writeBoolean(isMetered);
out.writeInt(maxMtu);
out.writeBoolean(areAuthParamsInline);
out.writeBoolean(isRestrictedToTestNetworks);
}
/**
@@ -237,12 +245,21 @@ public final class VpnProfile implements Cloneable, Parcelable {
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
// Acceptable numbers of values are:
// 14-19: Standard profile, with option for serverCert, proxy
// 24: Standard profile with serverCert, proxy and platform-VPN parameters.
if ((values.length < 14 || values.length > 19) && values.length != 24) {
// 24: Standard profile with serverCert, proxy and platform-VPN parameters
// 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
if ((values.length < 14 || values.length > 19)
&& values.length != 24 && values.length != 25) {
return null;
}
VpnProfile profile = new VpnProfile(key);
final boolean isRestrictedToTestNetworks;
if (values.length >= 25) {
isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]);
} else {
isRestrictedToTestNetworks = false;
}
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks);
profile.name = values[0];
profile.type = Integer.parseInt(values[1]);
if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -283,6 +300,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
}
// isRestrictedToTestNetworks (values[24]) assigned as part of the constructor
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
return profile;
} catch (Exception e) {
@@ -330,6 +349,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
builder.append(VALUE_DELIMITER).append(isMetered);
builder.append(VALUE_DELIMITER).append(maxMtu);
builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
@@ -421,7 +441,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
return Objects.hash(
key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
isRestrictedToTestNetworks);
}
/** Checks VPN profiles for interior equality. */
@@ -453,7 +474,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
&& isBypassable == other.isBypassable
&& isMetered == other.isMetered
&& maxMtu == other.maxMtu
&& areAuthParamsInline == other.areAuthParamsInline;
&& areAuthParamsInline == other.areAuthParamsInline
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks;
}
@NonNull

View File

@@ -65,6 +65,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnManager;
@@ -2225,12 +2226,27 @@ public class Vpn {
@Override
public void run() {
// Explicitly use only the network that ConnectivityService thinks is the "best." In
// other words, only ever use the currently selected default network. This does mean
// that in both onLost() and onConnected(), any old sessions MUST be torn down. This
// does NOT include VPNs.
// Unless the profile is restricted to test networks, explicitly use only the network
// that ConnectivityService thinks is the "best." In other words, only ever use the
// currently selected default network. This does mean that in both onLost() and
// onConnected(), any old sessions MUST be torn down. This does NOT include VPNs.
//
// When restricted to test networks, select any network with TRANSPORT_TEST. Since the
// creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
// this is considered safe.
final ConnectivityManager cm = ConnectivityManager.from(mContext);
cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
final NetworkRequest req;
if (mProfile.isRestrictedToTestNetworks()) {
req = new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
.build();
} else {
req = cm.getDefaultRequest();
}
cm.requestNetwork(req, mNetworkCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -2868,6 +2884,11 @@ public class Vpn {
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
if (profile.isRestrictedToTestNetworks) {
mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS,
"Test-mode profiles require the MANAGE_TEST_NETWORKS permission");
}
final byte[] encodedProfile = profile.encode();
if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
throw new IllegalArgumentException("Profile too big");

View File

@@ -33,7 +33,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Unit tests for {@link VpnProfile}. */
@SmallTest
@@ -41,6 +43,9 @@ import java.util.Arrays;
public class VpnProfileTest {
private static final String DUMMY_PROFILE_KEY = "Test";
private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
@Test
public void testDefaults() throws Exception {
final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
@@ -67,10 +72,11 @@ public class VpnProfileTest {
assertFalse(p.isMetered);
assertEquals(1360, p.maxMtu);
assertFalse(p.areAuthParamsInline);
assertFalse(p.isRestrictedToTestNetworks);
}
private VpnProfile getSampleIkev2Profile(String key) {
final VpnProfile p = new VpnProfile(key);
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
p.name = "foo";
p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -116,7 +122,7 @@ public class VpnProfileTest {
@Test
public void testParcelUnparcel() {
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
}
@Test
@@ -159,14 +165,41 @@ public class VpnProfileTest {
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
}
private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
// Sort to ensure when we remove, we can do it from greatest first.
Arrays.sort(missingIndices);
final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
final List<String> parts =
new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
// Remove from back first to ensure indexing is consistent.
for (int i = missingIndices.length - 1; i >= 0; i--) {
parts.remove(missingIndices[i]);
}
return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
}
@Test
public void testEncodeDecodeInvalidNumberOfValues() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
final String encoded = new String(profile.encode());
final byte[] tooFewValues =
encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_AUTH_PARAMS_INLINE,
ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
}
@Test
public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
// Verify decoding without isRestrictedToTestNetworks defaults to false
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
assertFalse(decoded.isRestrictedToTestNetworks);
}
@Test