Merge "Allow VPN apps to ask for running the validation checks" am: cbb2fa2f00 am: 64ba50411e

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1962928

Change-Id: I8dcb813cfe589411da7345bc2c4308e9f149b275
This commit is contained in:
Jean Chalard
2022-02-09 06:38:31 +00:00
committed by Automerger Merge Worker
6 changed files with 98 additions and 25 deletions

View File

@@ -25227,6 +25227,7 @@ package android.net {
method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int); method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean); method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
method @NonNull public android.net.Ikev2VpnProfile.Builder setRequiresInternetValidation(boolean);
} }
public final class IpSecAlgorithm implements android.os.Parcelable { public final class IpSecAlgorithm implements android.os.Parcelable {
@@ -25370,6 +25371,7 @@ package android.net {
public abstract class PlatformVpnProfile { public abstract class PlatformVpnProfile {
method public final boolean getExcludeLocalRoutes(); method public final boolean getExcludeLocalRoutes();
method public final boolean getRequiresInternetValidation();
method public final int getType(); method public final int getType();
method @NonNull public final String getTypeString(); method @NonNull public final String getTypeString();
field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7 field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7

View File

@@ -159,8 +159,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
boolean isMetered, boolean isMetered,
int maxMtu, int maxMtu,
boolean restrictToTestNetworks, boolean restrictToTestNetworks,
boolean excludeLocalRoutes) { boolean excludeLocalRoutes,
super(type, excludeLocalRoutes); boolean requiresInternetValidation) {
super(type, excludeLocalRoutes, requiresInternetValidation);
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address"); checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
@@ -181,7 +182,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms)); mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
if (excludeLocalRoutes && !isBypassable) { if (excludeLocalRoutes && !isBypassable) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Vpn should be byassable if excludeLocalRoutes is set"); "Vpn must be bypassable if excludeLocalRoutes is set");
} }
mIsBypassable = isBypassable; mIsBypassable = isBypassable;
@@ -238,7 +239,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
* that provides Authentication, and one that provides Encryption. Authenticated Encryption with * that provides Authentication, and one that provides Encryption. Authenticated Encryption with
* Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption. * Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption.
* *
* @param allowedAlgorithms The list to be validated * @param algorithmNames The list to be validated
*/ */
private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) { private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) {
// First, make sure no insecure algorithms were proposed. // First, make sure no insecure algorithms were proposed.
@@ -400,7 +401,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mIsBypassable, mIsBypassable,
mIsMetered, mIsMetered,
mMaxMtu, mMaxMtu,
mIsRestrictedToTestNetworks); mIsRestrictedToTestNetworks,
mExcludeLocalRoutes,
mRequiresInternetValidation);
} }
@Override @Override
@@ -425,7 +428,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
&& mIsMetered == other.mIsMetered && mIsMetered == other.mIsMetered
&& mMaxMtu == other.mMaxMtu && mMaxMtu == other.mMaxMtu
&& mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks
&& mExcludeLocalRoutes == other.mExcludeLocalRoutes; && mExcludeLocalRoutes == other.mExcludeLocalRoutes
&& mRequiresInternetValidation == other.mRequiresInternetValidation;
} }
/** /**
@@ -439,7 +443,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
@NonNull @NonNull
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { 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, mExcludeLocalRoutes); mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation);
profile.type = mType; profile.type = mType;
profile.server = mServerAddr; profile.server = mServerAddr;
profile.ipsecIdentifier = mUserIdentity; profile.ipsecIdentifier = mUserIdentity;
@@ -544,6 +548,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN"); Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
} }
builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable); builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable);
builder.setRequiresInternetValidation(profile.requiresInternetValidation);
return builder.build(); return builder.build();
} }
@@ -776,6 +781,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
@Nullable private ProxyInfo mProxyInfo; @Nullable private ProxyInfo mProxyInfo;
@NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS; @NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS;
private boolean mRequiresInternetValidation = false;
private boolean mIsBypassable = false; private boolean mIsBypassable = false;
private boolean mIsMetered = true; private boolean mIsMetered = true;
private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
@@ -987,6 +993,30 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
return this; return this;
} }
/**
* Request that this VPN undergoes Internet validation.
*
* If this is true, the platform will perform basic validation checks for Internet
* connectivity over this VPN. If and when they succeed, the VPN network capabilities will
* reflect this by gaining the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}
* capability.
*
* If this is false, the platform assumes the VPN either is always capable of reaching the
* Internet or intends not to. In this case, the VPN network capabilities will
* always gain the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} capability
* immediately after it connects, whether it can reach public Internet destinations or not.
*
* @param requiresInternetValidation {@code true} if the framework should attempt to
* validate this VPN for Internet connectivity. Defaults
* to {@code false}.
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setRequiresInternetValidation(boolean requiresInternetValidation) {
mRequiresInternetValidation = requiresInternetValidation;
return this;
}
/** /**
* Marks the VPN network as metered. * Marks the VPN network as metered.
* *
@@ -1103,7 +1133,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
mIsMetered, mIsMetered,
mMaxMtu, mMaxMtu,
mIsRestrictedToTestNetworks, mIsRestrictedToTestNetworks,
mExcludeLocalRoutes); mExcludeLocalRoutes,
mRequiresInternetValidation);
} }
} }
} }

View File

@@ -16,10 +16,6 @@
package android.net; package android.net;
import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -67,11 +63,15 @@ public abstract class PlatformVpnProfile {
/** @hide */ /** @hide */
protected final boolean mExcludeLocalRoutes; protected final boolean mExcludeLocalRoutes;
/** @hide */
protected final boolean mRequiresInternetValidation;
/** @hide */ /** @hide */
PlatformVpnProfile(@PlatformVpnType int type, boolean excludeLocalRoutes) { PlatformVpnProfile(@PlatformVpnType int type, boolean excludeLocalRoutes,
boolean requiresValidation) {
mType = type; mType = type;
mExcludeLocalRoutes = excludeLocalRoutes; mExcludeLocalRoutes = excludeLocalRoutes;
mRequiresInternetValidation = requiresValidation;
} }
/** Returns the profile integer type. */ /** Returns the profile integer type. */
@@ -80,14 +80,30 @@ public abstract class PlatformVpnProfile {
return mType; return mType;
} }
/** /**
* Returns if the local traffic is exempted from the VPN. * Returns whether the local traffic is exempted from the VPN.
*/ */
public final boolean getExcludeLocalRoutes() { public final boolean getExcludeLocalRoutes() {
return mExcludeLocalRoutes; return mExcludeLocalRoutes;
} }
/**
* Returns whether this VPN should undergo Internet validation.
*
* If this is true, the platform will perform basic validation checks for Internet
* connectivity over this VPN. If and when they succeed, the VPN network capabilities will
* reflect this by gaining the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}
* capability.
*
* If this is false, the platform assumes the VPN either is always capable of reaching the
* Internet or intends not to. In this case, the VPN network capabilities will
* always gain the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} capability
* immediately after it connects, whether it can reach public Internet destinations or not.
*/
public final boolean getRequiresInternetValidation() {
return mRequiresInternetValidation;
}
/** Returns a type string describing the VPN profile type */ /** Returns a type string describing the VPN profile type */
@NonNull @NonNull
public final String getTypeString() { public final String getTypeString() {

View File

@@ -105,6 +105,7 @@ public class VpnConfig implements Parcelable {
public boolean allowIPv4; public boolean allowIPv4;
public boolean allowIPv6; public boolean allowIPv6;
public boolean isMetered = true; public boolean isMetered = true;
public boolean requiresInternetValidation = false;
public Network[] underlyingNetworks; public Network[] underlyingNetworks;
public ProxyInfo proxyInfo; public ProxyInfo proxyInfo;
@@ -131,6 +132,7 @@ public class VpnConfig implements Parcelable {
allowIPv4 = other.allowIPv4; allowIPv4 = other.allowIPv4;
allowIPv6 = other.allowIPv6; allowIPv6 = other.allowIPv6;
isMetered = other.isMetered; isMetered = other.isMetered;
requiresInternetValidation = other.requiresInternetValidation;
underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf( underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
other.underlyingNetworks, other.underlyingNetworks.length) : null; other.underlyingNetworks, other.underlyingNetworks.length) : null;
proxyInfo = other.proxyInfo; proxyInfo = other.proxyInfo;
@@ -189,6 +191,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowIPv4 ? 1 : 0); out.writeInt(allowIPv4 ? 1 : 0);
out.writeInt(allowIPv6 ? 1 : 0); out.writeInt(allowIPv6 ? 1 : 0);
out.writeInt(isMetered ? 1 : 0); out.writeInt(isMetered ? 1 : 0);
out.writeInt(requiresInternetValidation ? 1 : 0);
out.writeTypedArray(underlyingNetworks, flags); out.writeTypedArray(underlyingNetworks, flags);
out.writeParcelable(proxyInfo, flags); out.writeParcelable(proxyInfo, flags);
} }
@@ -216,6 +219,7 @@ public class VpnConfig implements Parcelable {
config.allowIPv4 = in.readInt() != 0; config.allowIPv4 = in.readInt() != 0;
config.allowIPv6 = in.readInt() != 0; config.allowIPv6 = in.readInt() != 0;
config.isMetered = in.readInt() != 0; config.isMetered = in.readInt() != 0;
config.requiresInternetValidation = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR); config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
config.proxyInfo = in.readParcelable(null); config.proxyInfo = in.readParcelable(null);
return config; return config;
@@ -248,6 +252,8 @@ public class VpnConfig implements Parcelable {
.append(", allowBypass=").append(allowBypass) .append(", allowBypass=").append(allowBypass)
.append(", allowIPv4=").append(allowIPv4) .append(", allowIPv4=").append(allowIPv4)
.append(", allowIPv6=").append(allowIPv6) .append(", allowIPv6=").append(allowIPv6)
.append(", isMetered=").append(isMetered)
.append(", requiresInternetValidation").append(requiresInternetValidation)
.append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks)) .append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks))
.append(", proxyInfo=").append(proxyInfo) .append(", proxyInfo=").append(proxyInfo)
.append("}") .append("}")

View File

@@ -144,23 +144,26 @@ public final class VpnProfile implements Cloneable, Parcelable {
public final boolean isRestrictedToTestNetworks; // 24 public final boolean isRestrictedToTestNetworks; // 24
public final boolean excludeLocalRoutes; // 25 public final boolean excludeLocalRoutes; // 25
public final boolean requiresInternetValidation; // 26
// Helper fields. // Helper fields.
@UnsupportedAppUsage @UnsupportedAppUsage
public transient boolean saveLogin = false; public transient boolean saveLogin = false;
public VpnProfile(String key) { public VpnProfile(String key) {
this(key, false, false); this(key, false, false, false);
} }
public VpnProfile(String key, boolean isRestrictedToTestNetworks) { public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
this(key, isRestrictedToTestNetworks, false); this(key, isRestrictedToTestNetworks, false, false);
} }
public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes) { public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
boolean requiresInternetValidation) {
this.key = key; this.key = key;
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
this.excludeLocalRoutes = excludeLocalRoutes; this.excludeLocalRoutes = excludeLocalRoutes;
this.requiresInternetValidation = requiresInternetValidation;
} }
@UnsupportedAppUsage @UnsupportedAppUsage
@@ -191,6 +194,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
areAuthParamsInline = in.readBoolean(); areAuthParamsInline = in.readBoolean();
isRestrictedToTestNetworks = in.readBoolean(); isRestrictedToTestNetworks = in.readBoolean();
excludeLocalRoutes = in.readBoolean(); excludeLocalRoutes = in.readBoolean();
requiresInternetValidation = in.readBoolean();
} }
/** /**
@@ -239,6 +243,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
out.writeBoolean(areAuthParamsInline); out.writeBoolean(areAuthParamsInline);
out.writeBoolean(isRestrictedToTestNetworks); out.writeBoolean(isRestrictedToTestNetworks);
out.writeBoolean(excludeLocalRoutes); out.writeBoolean(excludeLocalRoutes);
out.writeBoolean(requiresInternetValidation);
} }
/** /**
@@ -258,9 +263,11 @@ public final class VpnProfile implements Cloneable, Parcelable {
// 14-19: Standard profile, with option for serverCert, proxy // 14-19: Standard profile, with option for serverCert, proxy
// 24: Standard profile with serverCert, proxy and platform-VPN parameters // 24: Standard profile with serverCert, proxy and platform-VPN parameters
// 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
// 26: Standard profile with platform-VPN parameters and excludeLocalRoutes // 26: ...and excludeLocalRoutes
if ((values.length < 14 || values.length > 19) // (26 can only be found on dogfood devices)
&& values.length != 24 && values.length != 25 && values.length != 26) { // 27: ...and requiresInternetValidation
if ((values.length < 14 || (values.length > 19 && values.length < 24)
|| values.length > 27)) {
return null; return null;
} }
@@ -278,8 +285,15 @@ public final class VpnProfile implements Cloneable, Parcelable {
excludeLocalRoutes = false; excludeLocalRoutes = false;
} }
final boolean requiresInternetValidation;
if (values.length >= 27) {
requiresInternetValidation = Boolean.parseBoolean(values[26]);
} else {
requiresInternetValidation = false;
}
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks, VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
excludeLocalRoutes); excludeLocalRoutes, requiresInternetValidation);
profile.name = values[0]; profile.name = values[0];
profile.type = Integer.parseInt(values[1]); profile.type = Integer.parseInt(values[1]);
if (profile.type < 0 || profile.type > TYPE_MAX) { if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -390,6 +404,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks); builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
builder.append(VALUE_DELIMITER).append(excludeLocalRoutes); builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
return builder.toString().getBytes(StandardCharsets.UTF_8); return builder.toString().getBytes(StandardCharsets.UTF_8);
} }
@@ -471,7 +486,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
key, type, server, username, password, dnsServers, searchDomains, routes, mppe, key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
isRestrictedToTestNetworks, excludeLocalRoutes); isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation);
} }
/** Checks VPN profiles for interior equality. */ /** Checks VPN profiles for interior equality. */
@@ -505,11 +520,12 @@ public final class VpnProfile implements Cloneable, Parcelable {
&& maxMtu == other.maxMtu && maxMtu == other.maxMtu
&& areAuthParamsInline == other.areAuthParamsInline && areAuthParamsInline == other.areAuthParamsInline
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
&& excludeLocalRoutes == other.excludeLocalRoutes; && excludeLocalRoutes == other.excludeLocalRoutes
&& requiresInternetValidation == other.requiresInternetValidation;
} }
@NonNull @NonNull
public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() { public static final Creator<VpnProfile> CREATOR = new Creator<>() {
@Override @Override
public VpnProfile createFromParcel(Parcel in) { public VpnProfile createFromParcel(Parcel in) {
return new VpnProfile(in); return new VpnProfile(in);

View File

@@ -1319,6 +1319,7 @@ public class Vpn {
.setLegacyType(ConnectivityManager.TYPE_VPN) .setLegacyType(ConnectivityManager.TYPE_VPN)
.setLegacyTypeName("VPN") .setLegacyTypeName("VPN")
.setBypassableVpn(mConfig.allowBypass && !mLockdown) .setBypassableVpn(mConfig.allowBypass && !mLockdown)
.setVpnRequiresValidation(mConfig.requiresInternetValidation)
.build(); .build();
capsBuilder.setOwnerUid(mOwnerUID); capsBuilder.setOwnerUid(mOwnerUID);
@@ -3383,6 +3384,7 @@ public class Vpn {
} }
mConfig.startTime = SystemClock.elapsedRealtime(); mConfig.startTime = SystemClock.elapsedRealtime();
mConfig.proxyInfo = profile.proxy; mConfig.proxyInfo = profile.proxy;
mConfig.requiresInternetValidation = profile.requiresInternetValidation;
switch (profile.type) { switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: