Merge "Ignore other IKE options if IkeTunnelConnectionParams is set" am: 48160f9b9b
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1978249 Change-Id: I3595e032cff0b18959150139d9f84cb817784fb2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -25,12 +25,6 @@ import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
|
|||||||
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
|
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
|
||||||
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
|
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
|
||||||
import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
|
import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
|
||||||
import static android.net.eap.EapSessionConfig.EapMsChapV2Config;
|
|
||||||
import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
|
|
||||||
import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
|
|
||||||
import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
|
|
||||||
import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
|
|
||||||
import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
|
|
||||||
|
|
||||||
import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||||
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
|
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
|
||||||
@@ -40,6 +34,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.RequiresFeature;
|
import android.annotation.RequiresFeature;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
|
||||||
import android.net.ipsec.ike.IkeFqdnIdentification;
|
import android.net.ipsec.ike.IkeFqdnIdentification;
|
||||||
import android.net.ipsec.ike.IkeIdentification;
|
import android.net.ipsec.ike.IkeIdentification;
|
||||||
import android.net.ipsec.ike.IkeIpv4AddrIdentification;
|
import android.net.ipsec.ike.IkeIpv4AddrIdentification;
|
||||||
@@ -119,8 +114,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
|
DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull private final String mServerAddr;
|
@Nullable private final String mServerAddr;
|
||||||
@NonNull private final String mUserIdentity;
|
@Nullable private final String mUserIdentity;
|
||||||
|
|
||||||
// PSK authentication
|
// PSK authentication
|
||||||
@Nullable private final byte[] mPresharedKey;
|
@Nullable private final byte[] mPresharedKey;
|
||||||
@@ -146,8 +141,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
|
|
||||||
private Ikev2VpnProfile(
|
private Ikev2VpnProfile(
|
||||||
int type,
|
int type,
|
||||||
@NonNull String serverAddr,
|
@Nullable String serverAddr,
|
||||||
@NonNull String userIdentity,
|
@Nullable String userIdentity,
|
||||||
@Nullable byte[] presharedKey,
|
@Nullable byte[] presharedKey,
|
||||||
@Nullable X509Certificate serverRootCaCert,
|
@Nullable X509Certificate serverRootCaCert,
|
||||||
@Nullable String username,
|
@Nullable String username,
|
||||||
@@ -165,8 +160,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
@Nullable IkeTunnelConnectionParams ikeTunConnParams) {
|
@Nullable IkeTunnelConnectionParams ikeTunConnParams) {
|
||||||
super(type, excludeLocalRoutes, requiresInternetValidation);
|
super(type, excludeLocalRoutes, requiresInternetValidation);
|
||||||
|
|
||||||
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
|
|
||||||
checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
|
|
||||||
checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
|
checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
|
||||||
|
|
||||||
mServerAddr = serverAddr;
|
mServerAddr = serverAddr;
|
||||||
@@ -191,18 +184,12 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
mIsMetered = isMetered;
|
mIsMetered = isMetered;
|
||||||
mMaxMtu = maxMtu;
|
mMaxMtu = maxMtu;
|
||||||
mIsRestrictedToTestNetworks = restrictToTestNetworks;
|
mIsRestrictedToTestNetworks = restrictToTestNetworks;
|
||||||
|
|
||||||
mIkeTunConnParams = ikeTunConnParams;
|
mIkeTunConnParams = ikeTunConnParams;
|
||||||
|
|
||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate() {
|
private void validate() {
|
||||||
// Server Address not validated except to check an address was provided. This allows for
|
|
||||||
// dual-stack servers and hostname based addresses.
|
|
||||||
checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
|
|
||||||
checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
|
|
||||||
|
|
||||||
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
|
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
|
||||||
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
|
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
|
||||||
// (at least that of the IPv6 MTU).
|
// (at least that of the IPv6 MTU).
|
||||||
@@ -210,6 +197,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU);
|
throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip validating the other fields if mIkeTunConnParams is set because the required
|
||||||
|
// information should all come from the mIkeTunConnParams.
|
||||||
|
if (mIkeTunConnParams != null) return;
|
||||||
|
|
||||||
|
// Server Address not validated except to check an address was provided. This allows for
|
||||||
|
// dual-stack servers and hostname based addresses.
|
||||||
|
checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
|
||||||
|
checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
|
||||||
|
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
case TYPE_IKEV2_IPSEC_USER_PASS:
|
case TYPE_IKEV2_IPSEC_USER_PASS:
|
||||||
checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
|
checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
|
||||||
@@ -286,22 +282,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
/** Retrieves the server address string. */
|
/** Retrieves the server address string. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getServerAddr() {
|
public String getServerAddr() {
|
||||||
return mServerAddr;
|
if (mIkeTunConnParams == null) return mServerAddr;
|
||||||
|
|
||||||
|
final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
|
||||||
|
return ikeSessionParams.getServerHostname();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves the user identity. */
|
/** Retrieves the user identity. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getUserIdentity() {
|
public String getUserIdentity() {
|
||||||
return mUserIdentity;
|
if (mIkeTunConnParams == null) return mUserIdentity;
|
||||||
|
|
||||||
|
final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
|
||||||
|
return getUserIdentityFromIkeSession(ikeSessionParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the pre-shared key.
|
* Retrieves the pre-shared key.
|
||||||
*
|
*
|
||||||
* <p>May be null if the profile is not using Pre-shared key authentication.
|
* <p>May be null if the profile is not using Pre-shared key authentication, or the profile is
|
||||||
|
* built from an {@link IkeTunnelConnectionParams}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getPresharedKey() {
|
public byte[] getPresharedKey() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
|
return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,46 +314,62 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
* Retrieves the certificate for the server's root CA.
|
* Retrieves the certificate for the server's root CA.
|
||||||
*
|
*
|
||||||
* <p>May be null if the profile is not using RSA Digital Signature Authentication or
|
* <p>May be null if the profile is not using RSA Digital Signature Authentication or
|
||||||
* Username/Password authentication
|
* Username/Password authentication, or the profile is built from an
|
||||||
|
* {@link IkeTunnelConnectionParams}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public X509Certificate getServerRootCaCert() {
|
public X509Certificate getServerRootCaCert() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mServerRootCaCert;
|
return mServerRootCaCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the username.
|
* Retrieves the username.
|
||||||
*
|
*
|
||||||
* <p>May be null if the profile is not using Username/Password authentication
|
* <p>May be null if the profile is not using Username/Password authentication, or the profile
|
||||||
|
* is built from an {@link IkeTunnelConnectionParams}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mUsername;
|
return mUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the password.
|
* Retrieves the password.
|
||||||
*
|
*
|
||||||
* <p>May be null if the profile is not using Username/Password authentication
|
* <p>May be null if the profile is not using Username/Password authentication, or the profile
|
||||||
|
* is built from an {@link IkeTunnelConnectionParams}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mPassword;
|
return mPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the RSA private key.
|
* Retrieves the RSA private key.
|
||||||
*
|
*
|
||||||
* <p>May be null if the profile is not using RSA Digital Signature authentication
|
* <p>May be null if the profile is not using RSA Digital Signature authentication, or the
|
||||||
|
* profile is built from an {@link IkeTunnelConnectionParams}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public PrivateKey getRsaPrivateKey() {
|
public PrivateKey getRsaPrivateKey() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mRsaPrivateKey;
|
return mRsaPrivateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves the user certificate, if any was set. */
|
/** Retrieves the user certificate, if any was set.
|
||||||
|
*
|
||||||
|
* <p>May be null if the profile is built from an {@link IkeTunnelConnectionParams}.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public X509Certificate getUserCert() {
|
public X509Certificate getUserCert() {
|
||||||
|
if (mIkeTunConnParams != null) return null;
|
||||||
|
|
||||||
return mUserCert;
|
return mUserCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,9 +379,14 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
return mProxyInfo;
|
return mProxyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all the algorithms allowed by this VPN profile. */
|
/** Returns all the algorithms allowed by this VPN profile.
|
||||||
|
*
|
||||||
|
* <p>May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}.
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<String> getAllowedAlgorithms() {
|
public List<String> getAllowedAlgorithms() {
|
||||||
|
if (mIkeTunConnParams != null) return new ArrayList<>();
|
||||||
|
|
||||||
return mAllowedAlgorithms;
|
return mAllowedAlgorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,18 +481,25 @@ 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, mRequiresInternetValidation);
|
mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation,
|
||||||
profile.type = mType;
|
mIkeTunConnParams);
|
||||||
profile.server = mServerAddr;
|
|
||||||
profile.ipsecIdentifier = mUserIdentity;
|
profile.server = getServerAddr();
|
||||||
|
profile.ipsecIdentifier = getUserIdentity();
|
||||||
profile.proxy = mProxyInfo;
|
profile.proxy = mProxyInfo;
|
||||||
profile.setAllowedAlgorithms(mAllowedAlgorithms);
|
|
||||||
profile.isBypassable = mIsBypassable;
|
profile.isBypassable = mIsBypassable;
|
||||||
profile.isMetered = mIsMetered;
|
profile.isMetered = mIsMetered;
|
||||||
profile.maxMtu = mMaxMtu;
|
profile.maxMtu = mMaxMtu;
|
||||||
profile.areAuthParamsInline = true;
|
profile.areAuthParamsInline = true;
|
||||||
profile.saveLogin = true;
|
profile.saveLogin = true;
|
||||||
|
// The other fields should come from mIkeTunConnParams if it's available.
|
||||||
|
if (mIkeTunConnParams != null) {
|
||||||
|
profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS;
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.type = mType;
|
||||||
|
profile.setAllowedAlgorithms(mAllowedAlgorithms);
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
case TYPE_IKEV2_IPSEC_USER_PASS:
|
case TYPE_IKEV2_IPSEC_USER_PASS:
|
||||||
profile.username = mUsername;
|
profile.username = mUsername;
|
||||||
@@ -516,16 +549,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
|
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
// TODO: Build the VpnProfile from mIkeTunConnParams if it exists.
|
final Builder builder;
|
||||||
final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
|
if (profile.ikeTunConnParams == null) {
|
||||||
builder.setProxy(profile.proxy);
|
builder = new Builder(profile.server, profile.ipsecIdentifier);
|
||||||
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
|
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
|
||||||
builder.setBypassable(profile.isBypassable);
|
|
||||||
builder.setMetered(profile.isMetered);
|
|
||||||
builder.setMaxMtu(profile.maxMtu);
|
|
||||||
if (profile.isRestrictedToTestNetworks) {
|
|
||||||
builder.restrictToTestNetworks();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (profile.type) {
|
switch (profile.type) {
|
||||||
case TYPE_IKEV2_IPSEC_USER_PASS:
|
case TYPE_IKEV2_IPSEC_USER_PASS:
|
||||||
@@ -549,13 +576,26 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
throw new IllegalArgumentException("Invalid RSA private key prefix");
|
throw new IllegalArgumentException("Invalid RSA private key prefix");
|
||||||
}
|
}
|
||||||
|
|
||||||
final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
|
final X509Certificate userCert =
|
||||||
final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
|
certificateFromPemString(profile.ipsecUserCert);
|
||||||
|
final X509Certificate serverRootCa =
|
||||||
|
certificateFromPemString(profile.ipsecCaCert);
|
||||||
builder.setAuthDigitalSignature(userCert, key, serverRootCa);
|
builder.setAuthDigitalSignature(userCert, key, serverRootCa);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid auth method set");
|
throw new IllegalArgumentException("Invalid auth method set");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
builder = new Builder(profile.ikeTunConnParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setProxy(profile.proxy);
|
||||||
|
builder.setBypassable(profile.isBypassable);
|
||||||
|
builder.setMetered(profile.isMetered);
|
||||||
|
builder.setMaxMtu(profile.maxMtu);
|
||||||
|
if (profile.isRestrictedToTestNetworks) {
|
||||||
|
builder.restrictToTestNetworks();
|
||||||
|
}
|
||||||
|
|
||||||
if (profile.excludeLocalRoutes && !profile.isBypassable) {
|
if (profile.excludeLocalRoutes && !profile.isBypassable) {
|
||||||
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
|
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
|
||||||
@@ -678,82 +718,13 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void checkBuilderSetter(boolean constructedFromIkeTunConParams,
|
private static void checkBuilderSetter(boolean constructedFromIkeTunConParams,
|
||||||
@NonNull String message) {
|
@NonNull String field) {
|
||||||
if (constructedFromIkeTunConParams) {
|
if (constructedFromIkeTunConParams) {
|
||||||
throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams "
|
throw new IllegalArgumentException(
|
||||||
+ "should not set " + message);
|
field + " can't be set with IkeTunnelConnectionParams builder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
final IkeAuthConfig config = params.getLocalAuthConfig();
|
|
||||||
if (config instanceof IkeAuthDigitalSignLocalConfig) {
|
|
||||||
return TYPE_IKEV2_IPSEC_RSA;
|
|
||||||
} else if (config instanceof IkeAuthEapConfig) {
|
|
||||||
return TYPE_IKEV2_IPSEC_USER_PASS;
|
|
||||||
} else if (config instanceof IkeAuthPskConfig) {
|
|
||||||
return TYPE_IKEV2_IPSEC_PSK;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Invalid local IkeAuthConfig");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
|
|
||||||
final EapMsChapV2Config eapMsChapV2Config =
|
|
||||||
ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
|
|
||||||
return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
|
|
||||||
final EapMsChapV2Config eapMsChapV2Config =
|
|
||||||
ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
|
|
||||||
return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthDigitalSignLocalConfig config =
|
|
||||||
(IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
|
|
||||||
return config.getClientEndCertificate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static X509Certificate getServerRootCaCertFromIkeSession(
|
|
||||||
@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthDigitalSignRemoteConfig config =
|
|
||||||
(IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig();
|
|
||||||
return config.getRemoteCaCert();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthDigitalSignLocalConfig config =
|
|
||||||
(IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
|
|
||||||
return config.getPrivateKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) {
|
|
||||||
if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null;
|
|
||||||
|
|
||||||
final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig();
|
|
||||||
return config.getPsk();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) {
|
private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) {
|
||||||
final IkeIdentification ident = params.getLocalIdentification();
|
final IkeIdentification ident = params.getLocalIdentification();
|
||||||
@@ -768,6 +739,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress();
|
return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress();
|
||||||
} else if (ident instanceof IkeIpv6AddrIdentification) {
|
} else if (ident instanceof IkeIpv6AddrIdentification) {
|
||||||
return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress();
|
return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress();
|
||||||
|
} else if (ident instanceof IkeDerAsn1DnIdentification) {
|
||||||
|
throw new IllegalArgumentException("Unspported ASN.1 encoded identities");
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unknown IkeIdentification to get user identity");
|
throw new IllegalArgumentException("Unknown IkeIdentification to get user identity");
|
||||||
}
|
}
|
||||||
@@ -776,8 +749,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
/** A incremental builder for IKEv2 VPN profiles */
|
/** A incremental builder for IKEv2 VPN profiles */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private int mType = -1;
|
private int mType = -1;
|
||||||
@NonNull private final String mServerAddr;
|
@Nullable private final String mServerAddr;
|
||||||
@NonNull private final String mUserIdentity;
|
@Nullable private final String mUserIdentity;
|
||||||
|
|
||||||
// PSK authentication
|
// PSK authentication
|
||||||
@Nullable private byte[] mPresharedKey;
|
@Nullable private byte[] mPresharedKey;
|
||||||
@@ -831,19 +804,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams");
|
checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams");
|
||||||
|
|
||||||
mIkeTunConnParams = ikeTunConnParams;
|
mIkeTunConnParams = ikeTunConnParams;
|
||||||
|
mServerAddr = null;
|
||||||
final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
|
mUserIdentity = null;
|
||||||
mServerAddr = ikeSessionParams.getServerHostname();
|
|
||||||
|
|
||||||
mType = getTypeFromIkeSession(ikeSessionParams);
|
|
||||||
mUserCert = getUserCertFromIkeSession(ikeSessionParams);
|
|
||||||
mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
|
|
||||||
mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams);
|
|
||||||
mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
|
|
||||||
mUsername = getUsernameFromIkeSession(ikeSessionParams);
|
|
||||||
mPassword = getPasswordFromIkeSession(ikeSessionParams);
|
|
||||||
mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams);
|
|
||||||
mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetAuthParams() {
|
private void resetAuthParams() {
|
||||||
@@ -862,6 +824,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
* authentication method may be set. This method will overwrite any previously set
|
* authentication method may be set. This method will overwrite any previously set
|
||||||
* authentication method.
|
* authentication method.
|
||||||
*
|
*
|
||||||
|
* <p>It's not allowed to set this if this {@link Builder} is constructed from an
|
||||||
|
* {@link IkeTunnelConnectionParams}. This information should be retrieved from
|
||||||
|
* {@link IkeTunnelConnectionParams}
|
||||||
|
*
|
||||||
* @param user the username to be used for EAP-MSCHAPv2 authentication
|
* @param user the username to be used for EAP-MSCHAPv2 authentication
|
||||||
* @param pass the password to be used for EAP-MSCHAPv2 authentication
|
* @param pass the password to be used for EAP-MSCHAPv2 authentication
|
||||||
* @param serverRootCa the root certificate to be used for verifying the identity of the
|
* @param serverRootCa the root certificate to be used for verifying the identity of the
|
||||||
@@ -898,6 +864,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
* Only one authentication method may be set. This method will overwrite any previously set
|
* Only one authentication method may be set. This method will overwrite any previously set
|
||||||
* authentication method.
|
* authentication method.
|
||||||
*
|
*
|
||||||
|
* <p>It's not allowed to set this if this {@link Builder} is constructed from an
|
||||||
|
* {@link IkeTunnelConnectionParams}. This information should be retrieved from
|
||||||
|
* {@link IkeTunnelConnectionParams}
|
||||||
|
*
|
||||||
* @param userCert the username to be used for RSA Digital signiture authentication
|
* @param userCert the username to be used for RSA Digital signiture authentication
|
||||||
* @param key the PrivateKey instance associated with the user ceritificate, used for
|
* @param key the PrivateKey instance associated with the user ceritificate, used for
|
||||||
* constructing the signature
|
* constructing the signature
|
||||||
@@ -936,6 +906,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
* authentication method may be set. This method will overwrite any previously set
|
* authentication method may be set. This method will overwrite any previously set
|
||||||
* authentication method.
|
* authentication method.
|
||||||
*
|
*
|
||||||
|
* <p>It's not allowed to set this if this {@link Builder} is constructed from an
|
||||||
|
* {@link IkeTunnelConnectionParams}. This information should be retrieved from
|
||||||
|
* {@link IkeTunnelConnectionParams}
|
||||||
|
*
|
||||||
* @param psk the key to be used for Pre-Shared Key authentication
|
* @param psk the key to be used for Pre-Shared Key authentication
|
||||||
* @return this {@link Builder} object to facilitate chaining of method calls
|
* @return this {@link Builder} object to facilitate chaining of method calls
|
||||||
*/
|
*/
|
||||||
@@ -1068,6 +1042,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
* Authentication, and one that provides Encryption. Authenticated Encryption with
|
* Authentication, and one that provides Encryption. Authenticated Encryption with
|
||||||
* Associated Data (AEAD) algorithms provide both Authentication and Encryption.
|
* Associated Data (AEAD) algorithms provide both Authentication and Encryption.
|
||||||
*
|
*
|
||||||
|
* <p>It's not allowed to set this if this {@link Builder} is constructed from an
|
||||||
|
* {@link IkeTunnelConnectionParams}. This information should be retrieved from
|
||||||
|
* {@link IkeTunnelConnectionParams}
|
||||||
|
*
|
||||||
* <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
|
* <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
|
||||||
* with the exception of those considered insecure (as described above).
|
* with the exception of those considered insecure (as described above).
|
||||||
*
|
*
|
||||||
@@ -1079,6 +1057,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
|||||||
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
|
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
|
||||||
public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
|
public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
|
||||||
checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
|
checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
|
||||||
|
checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames");
|
||||||
validateAllowedAlgorithms(algorithmNames);
|
validateAllowedAlgorithms(algorithmNames);
|
||||||
|
|
||||||
mAllowedAlgorithms = algorithmNames;
|
mAllowedAlgorithms = algorithmNames;
|
||||||
|
|||||||
@@ -22,12 +22,17 @@ import android.net.Ikev2VpnProfile;
|
|||||||
import android.net.PlatformVpnProfile;
|
import android.net.PlatformVpnProfile;
|
||||||
import android.net.ProxyInfo;
|
import android.net.ProxyInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.net.ipsec.ike.IkeTunnelConnectionParams;
|
||||||
|
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.os.PersistableBundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.util.HexDump;
|
||||||
import com.android.net.module.util.ProxyUtils;
|
import com.android.net.module.util.ProxyUtils;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@@ -69,7 +74,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
|
public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
|
||||||
public static final int TYPE_IKEV2_IPSEC_PSK = 7;
|
public static final int TYPE_IKEV2_IPSEC_PSK = 7;
|
||||||
public static final int TYPE_IKEV2_IPSEC_RSA = 8;
|
public static final int TYPE_IKEV2_IPSEC_RSA = 8;
|
||||||
public static final int TYPE_MAX = 8;
|
public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9;
|
||||||
|
public static final int TYPE_MAX = 9;
|
||||||
|
|
||||||
// Match these constants with R.array.vpn_proxy_settings.
|
// Match these constants with R.array.vpn_proxy_settings.
|
||||||
public static final int PROXY_NONE = 0;
|
public static final int PROXY_NONE = 0;
|
||||||
@@ -145,25 +151,27 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
|
|
||||||
public final boolean excludeLocalRoutes; // 25
|
public final boolean excludeLocalRoutes; // 25
|
||||||
public final boolean requiresInternetValidation; // 26
|
public final boolean requiresInternetValidation; // 26
|
||||||
|
public final IkeTunnelConnectionParams ikeTunConnParams; // 27
|
||||||
|
|
||||||
// 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, false);
|
this(key, false, false, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
|
public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
|
||||||
this(key, isRestrictedToTestNetworks, false, false);
|
this(key, isRestrictedToTestNetworks, false, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
|
public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
|
||||||
boolean requiresInternetValidation) {
|
boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
|
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
|
||||||
this.excludeLocalRoutes = excludeLocalRoutes;
|
this.excludeLocalRoutes = excludeLocalRoutes;
|
||||||
this.requiresInternetValidation = requiresInternetValidation;
|
this.requiresInternetValidation = requiresInternetValidation;
|
||||||
|
this.ikeTunConnParams = ikeTunConnParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
@@ -195,6 +203,10 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
isRestrictedToTestNetworks = in.readBoolean();
|
isRestrictedToTestNetworks = in.readBoolean();
|
||||||
excludeLocalRoutes = in.readBoolean();
|
excludeLocalRoutes = in.readBoolean();
|
||||||
requiresInternetValidation = in.readBoolean();
|
requiresInternetValidation = in.readBoolean();
|
||||||
|
final PersistableBundle bundle =
|
||||||
|
in.readParcelable(PersistableBundle.class.getClassLoader());
|
||||||
|
ikeTunConnParams = (bundle == null) ? null
|
||||||
|
: TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,6 +256,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
out.writeBoolean(isRestrictedToTestNetworks);
|
out.writeBoolean(isRestrictedToTestNetworks);
|
||||||
out.writeBoolean(excludeLocalRoutes);
|
out.writeBoolean(excludeLocalRoutes);
|
||||||
out.writeBoolean(requiresInternetValidation);
|
out.writeBoolean(requiresInternetValidation);
|
||||||
|
out.writeParcelable(ikeTunConnParams == null ? null
|
||||||
|
: TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,15 +273,17 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
|
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
|
||||||
|
|
||||||
// Acceptable numbers of values are:
|
// Acceptable numbers of values are:
|
||||||
// 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: ...and excludeLocalRoutes
|
// 26: ...and excludeLocalRoutes
|
||||||
// (26 can only be found on dogfood devices)
|
|
||||||
// 27: ...and requiresInternetValidation
|
// 27: ...and requiresInternetValidation
|
||||||
|
// (26,27 can only be found on dogfood devices)
|
||||||
|
// 28: ...and ikeTunConnParams
|
||||||
if ((values.length < 14 || (values.length > 19 && values.length < 24)
|
if ((values.length < 14 || (values.length > 19 && values.length < 24)
|
||||||
|| values.length > 27)) {
|
|| values.length > 28)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +308,22 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
requiresInternetValidation = false;
|
requiresInternetValidation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final IkeTunnelConnectionParams tempIkeTunConnParams;
|
||||||
|
// Assign null directly if the ikeTunConParams field is empty.
|
||||||
|
if (values.length >= 28 && values[27].length() != 0) {
|
||||||
|
final Parcel parcel = Parcel.obtain();
|
||||||
|
final byte[] bytes = HexDump.hexStringToByteArray(values[27]);
|
||||||
|
parcel.unmarshall(bytes, 0, bytes.length);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
final PersistableBundle bundle = (PersistableBundle) parcel.readValue(
|
||||||
|
PersistableBundle.class.getClassLoader());
|
||||||
|
tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
|
||||||
|
} else {
|
||||||
|
tempIkeTunConnParams = null;
|
||||||
|
}
|
||||||
|
|
||||||
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
|
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
|
||||||
excludeLocalRoutes, requiresInternetValidation);
|
excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams);
|
||||||
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) {
|
||||||
@@ -345,6 +375,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
|
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
|
||||||
return profile;
|
return profile;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.d(TAG, "Got exception in decode.", e);
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -406,6 +437,17 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
|
builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
|
||||||
builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
|
builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
|
||||||
|
|
||||||
|
if (ikeTunConnParams != null) {
|
||||||
|
final PersistableBundle bundle =
|
||||||
|
TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams);
|
||||||
|
final Parcel parcel = Parcel.obtain();
|
||||||
|
parcel.writeValue(bundle);
|
||||||
|
final byte[] bytes = parcel.marshall();
|
||||||
|
builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes));
|
||||||
|
} else {
|
||||||
|
builder.append(VALUE_DELIMITER).append("");
|
||||||
|
}
|
||||||
|
|
||||||
return builder.toString().getBytes(StandardCharsets.UTF_8);
|
return builder.toString().getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,7 +528,8 @@ 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, requiresInternetValidation);
|
isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation,
|
||||||
|
ikeTunConnParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks VPN profiles for interior equality. */
|
/** Checks VPN profiles for interior equality. */
|
||||||
@@ -521,7 +564,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
|||||||
&& areAuthParamsInline == other.areAuthParamsInline
|
&& areAuthParamsInline == other.areAuthParamsInline
|
||||||
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
|
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
|
||||||
&& excludeLocalRoutes == other.excludeLocalRoutes
|
&& excludeLocalRoutes == other.excludeLocalRoutes
|
||||||
&& requiresInternetValidation == other.requiresInternetValidation;
|
&& requiresInternetValidation == other.requiresInternetValidation
|
||||||
|
&& Objects.equals(ikeTunConnParams, other.ikeTunConnParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
Reference in New Issue
Block a user