diff options
| -rw-r--r-- | core/java/android/net/Ikev2VpnProfile.java | 285 | ||||
| -rw-r--r-- | core/java/com/android/internal/net/VpnProfile.java | 62 |
2 files changed, 185 insertions, 162 deletions
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 3abe83bd3373..1b503b11816f 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -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.CRYPT_AES_CBC; 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.util.Preconditions.checkStringNotEmpty; @@ -40,6 +34,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.content.pm.PackageManager; +import android.net.ipsec.ike.IkeDerAsn1DnIdentification; import android.net.ipsec.ike.IkeFqdnIdentification; import android.net.ipsec.ike.IkeIdentification; import android.net.ipsec.ike.IkeIpv4AddrIdentification; @@ -119,8 +114,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms); } - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private final byte[] mPresharedKey; @@ -146,8 +141,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private Ikev2VpnProfile( int type, - @NonNull String serverAddr, - @NonNull String userIdentity, + @Nullable String serverAddr, + @Nullable String userIdentity, @Nullable byte[] presharedKey, @Nullable X509Certificate serverRootCaCert, @Nullable String username, @@ -165,8 +160,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @Nullable IkeTunnelConnectionParams ikeTunConnParams) { 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"); mServerAddr = serverAddr; @@ -191,18 +184,12 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mIsMetered = isMetered; mMaxMtu = maxMtu; mIsRestrictedToTestNetworks = restrictToTestNetworks; - mIkeTunConnParams = ikeTunConnParams; 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 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (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); } + // 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) { case TYPE_IKEV2_IPSEC_USER_PASS: checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username"); @@ -286,22 +282,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** Retrieves the server address string. */ @NonNull public String getServerAddr() { - return mServerAddr; + if (mIkeTunConnParams == null) return mServerAddr; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return ikeSessionParams.getServerHostname(); } /** Retrieves the user identity. */ @NonNull public String getUserIdentity() { - return mUserIdentity; + if (mIkeTunConnParams == null) return mUserIdentity; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return getUserIdentityFromIkeSession(ikeSessionParams); } /** * 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 public byte[] getPresharedKey() { + if (mIkeTunConnParams != null) return null; + 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. * * <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 public X509Certificate getServerRootCaCert() { + if (mIkeTunConnParams != null) return null; + return mServerRootCaCert; } - /** * 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 public String getUsername() { + if (mIkeTunConnParams != null) return null; + return mUsername; } /** * 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 public String getPassword() { + if (mIkeTunConnParams != null) return null; + return mPassword; } /** * 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 public PrivateKey getRsaPrivateKey() { + if (mIkeTunConnParams != null) return null; + 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 public X509Certificate getUserCert() { + if (mIkeTunConnParams != null) return null; + return mUserCert; } @@ -358,9 +379,14 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { 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 public List<String> getAllowedAlgorithms() { + if (mIkeTunConnParams != null) return new ArrayList<>(); + return mAllowedAlgorithms; } @@ -455,18 +481,25 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */, - mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation); - profile.type = mType; - profile.server = mServerAddr; - profile.ipsecIdentifier = mUserIdentity; + mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation, + mIkeTunConnParams); + + profile.server = getServerAddr(); + profile.ipsecIdentifier = getUserIdentity(); profile.proxy = mProxyInfo; - profile.setAllowedAlgorithms(mAllowedAlgorithms); profile.isBypassable = mIsBypassable; profile.isMetered = mIsMetered; profile.maxMtu = mMaxMtu; profile.areAuthParamsInline = 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) { case TYPE_IKEV2_IPSEC_USER_PASS: profile.username = mUsername; @@ -516,10 +549,47 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile) throws GeneralSecurityException { - // TODO: Build the VpnProfile from mIkeTunConnParams if it exists. - final Builder builder = new Builder(profile.server, profile.ipsecIdentifier); + final Builder builder; + if (profile.ikeTunConnParams == null) { + builder = new Builder(profile.server, profile.ipsecIdentifier); + builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); + + switch (profile.type) { + case TYPE_IKEV2_IPSEC_USER_PASS: + builder.setAuthUsernamePassword( + profile.username, + profile.password, + certificateFromPemString(profile.ipsecCaCert)); + break; + case TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); + break; + case TYPE_IKEV2_IPSEC_RSA: + final PrivateKey key; + if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { + final String alias = + profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); + key = getPrivateKeyFromAndroidKeystore(alias); + } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { + key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); + } else { + throw new IllegalArgumentException("Invalid RSA private key prefix"); + } + + final X509Certificate userCert = + certificateFromPemString(profile.ipsecUserCert); + final X509Certificate serverRootCa = + certificateFromPemString(profile.ipsecCaCert); + builder.setAuthDigitalSignature(userCert, key, serverRootCa); + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + } else { + builder = new Builder(profile.ikeTunConnParams); + } + builder.setProxy(profile.proxy); - builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); builder.setBypassable(profile.isBypassable); builder.setMetered(profile.isMetered); builder.setMaxMtu(profile.maxMtu); @@ -527,36 +597,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { builder.restrictToTestNetworks(); } - switch (profile.type) { - case TYPE_IKEV2_IPSEC_USER_PASS: - builder.setAuthUsernamePassword( - profile.username, - profile.password, - certificateFromPemString(profile.ipsecCaCert)); - break; - case TYPE_IKEV2_IPSEC_PSK: - builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); - break; - case TYPE_IKEV2_IPSEC_RSA: - final PrivateKey key; - if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { - final String alias = - profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); - key = getPrivateKeyFromAndroidKeystore(alias); - } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { - key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); - } else { - throw new IllegalArgumentException("Invalid RSA private key prefix"); - } - - final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert); - final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert); - builder.setAuthDigitalSignature(userCert, key, serverRootCa); - break; - default: - throw new IllegalArgumentException("Invalid auth method set"); - } - if (profile.excludeLocalRoutes && !profile.isBypassable) { 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, - @NonNull String message) { + @NonNull String field) { if (constructedFromIkeTunConParams) { - throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams " - + "should not set " + message); - } - } - - 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"); + throw new IllegalArgumentException( + field + " can't be set with IkeTunnelConnectionParams builder"); } } - @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 private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) { final IkeIdentification ident = params.getLocalIdentification(); @@ -768,6 +739,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress(); } else if (ident instanceof IkeIpv6AddrIdentification) { return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress(); + } else if (ident instanceof IkeDerAsn1DnIdentification) { + throw new IllegalArgumentException("Unspported ASN.1 encoded identities"); } else { 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 */ public static final class Builder { private int mType = -1; - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private byte[] mPresharedKey; @@ -831,19 +804,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams"); mIkeTunConnParams = ikeTunConnParams; - - final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); - 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); + mServerAddr = null; + mUserIdentity = null; } 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. * + * <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 pass the password to be used for EAP-MSCHAPv2 authentication * @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 * 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 key the PrivateKey instance associated with the user ceritificate, used for * 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. * + * <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 * @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 * 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}, * 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) public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); + checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames"); validateAllowedAlgorithms(algorithmNames); mAllowedAlgorithms = algorithmNames; diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index bd3e898a631c..87973814d7e3 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -22,12 +22,17 @@ import android.net.Ikev2VpnProfile; import android.net.PlatformVpnProfile; import android.net.ProxyInfo; import android.net.Uri; +import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.text.TextUtils; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.HexDump; import com.android.net.module.util.ProxyUtils; 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_PSK = 7; 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. 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 requiresInternetValidation; // 26 + public final IkeTunnelConnectionParams ikeTunConnParams; // 27 // Helper fields. @UnsupportedAppUsage public transient boolean saveLogin = false; public VpnProfile(String key) { - this(key, false, false, false); + this(key, false, false, false, null); } 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, - boolean requiresInternetValidation) { + boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) { this.key = key; this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; this.excludeLocalRoutes = excludeLocalRoutes; this.requiresInternetValidation = requiresInternetValidation; + this.ikeTunConnParams = ikeTunConnParams; } @UnsupportedAppUsage @@ -195,6 +203,10 @@ public final class VpnProfile implements Cloneable, Parcelable { isRestrictedToTestNetworks = in.readBoolean(); excludeLocalRoutes = 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(excludeLocalRoutes); 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); + // Acceptable numbers of values are: // 14-19: Standard profile, with option for serverCert, proxy // 24: Standard profile with serverCert, proxy and platform-VPN parameters // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks // 26: ...and excludeLocalRoutes - // (26 can only be found on dogfood devices) // 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) - || values.length > 27)) { + || values.length > 28)) { return null; } @@ -292,8 +308,22 @@ public final class VpnProfile implements Cloneable, Parcelable { 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, - excludeLocalRoutes, requiresInternetValidation); + excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams); profile.name = values[0]; profile.type = Integer.parseInt(values[1]); 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(); return profile; } catch (Exception e) { + Log.d(TAG, "Got exception in decode.", e); // ignore } return null; @@ -406,6 +437,17 @@ public final class VpnProfile implements Cloneable, Parcelable { builder.append(VALUE_DELIMITER).append(excludeLocalRoutes); 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); } @@ -486,7 +528,8 @@ public final class VpnProfile implements Cloneable, Parcelable { key, type, server, username, password, dnsServers, searchDomains, routes, mppe, l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, - isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation); + isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation, + ikeTunConnParams); } /** Checks VPN profiles for interior equality. */ @@ -521,7 +564,8 @@ public final class VpnProfile implements Cloneable, Parcelable { && areAuthParamsInline == other.areAuthParamsInline && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks && excludeLocalRoutes == other.excludeLocalRoutes - && requiresInternetValidation == other.requiresInternetValidation; + && requiresInternetValidation == other.requiresInternetValidation + && Objects.equals(ikeTunConnParams, other.ikeTunConnParams); } @NonNull |