| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.ipsec.ike; |
| |
| import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_APPLICATION_VERSION; |
| import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP4_PCSCF; |
| import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP6_PCSCF; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.annotation.SystemApi; |
| import android.net.eap.EapInfo; |
| |
| import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; |
| import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; |
| import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeAppVersion; |
| import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Pcscf; |
| import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Pcscf; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.net.InetAddress; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| /** |
| * IkeSessionConfiguration represents the negotiated configuration for a {@link IkeSession}. |
| * |
| * <p>Configurations include remote application version and enabled IKE extensions. |
| */ |
| public final class IkeSessionConfiguration { |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({EXTENSION_TYPE_FRAGMENTATION, EXTENSION_TYPE_MOBIKE}) |
| public @interface ExtensionType {} |
| |
| /** IKE Message Fragmentation */ |
| public static final int EXTENSION_TYPE_FRAGMENTATION = 1; |
| /** IKEv2 Mobility and Multihoming Protocol */ |
| public static final int EXTENSION_TYPE_MOBIKE = 2; |
| |
| private static final int VALID_EXTENSION_MIN = EXTENSION_TYPE_FRAGMENTATION; |
| private static final int VALID_EXTENSION_MAX = EXTENSION_TYPE_MOBIKE; |
| |
| private final String mRemoteApplicationVersion; |
| private final IkeSessionConnectionInfo mIkeConnInfo; |
| private final List<InetAddress> mPcscfServers = new ArrayList<>(); |
| private final List<byte[]> mRemoteVendorIds = new ArrayList<>(); |
| private final Set<Integer> mEnabledExtensions = new HashSet<>(); |
| private final EapInfo mEapInfo; |
| |
| /** |
| * Construct an instance of {@link IkeSessionConfiguration}. |
| * |
| * <p>IkeSessionConfigurations may contain negotiated configuration information that is included |
| * in a Configure(Reply) Payload. Thus the input configPayload should always be a |
| * Configure(Reply), and never be a Configure(Request). |
| * |
| * @hide |
| */ |
| public IkeSessionConfiguration( |
| IkeSessionConnectionInfo ikeConnInfo, |
| IkeConfigPayload configPayload, |
| List<byte[]> remoteVendorIds, |
| List<Integer> enabledExtensions, |
| EapInfo eapInfo) { |
| mIkeConnInfo = ikeConnInfo; |
| mRemoteVendorIds.addAll(remoteVendorIds); |
| mEnabledExtensions.addAll(enabledExtensions); |
| mEapInfo = eapInfo; |
| |
| String appVersion = ""; |
| if (configPayload != null) { |
| if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) { |
| throw new IllegalArgumentException( |
| "Cannot build IkeSessionConfiguration with configuration type: " |
| + configPayload.configType); |
| } |
| |
| for (ConfigAttribute attr : configPayload.recognizedAttributeList) { |
| if (attr.isEmptyValue()) continue; |
| switch (attr.attributeType) { |
| case CONFIG_ATTR_APPLICATION_VERSION: |
| ConfigAttributeAppVersion appVersionAttr = (ConfigAttributeAppVersion) attr; |
| appVersion = appVersionAttr.applicationVersion; |
| break; |
| case CONFIG_ATTR_IP4_PCSCF: |
| ConfigAttributeIpv4Pcscf ip4Pcscf = (ConfigAttributeIpv4Pcscf) attr; |
| mPcscfServers.add(ip4Pcscf.getAddress()); |
| break; |
| case CONFIG_ATTR_IP6_PCSCF: |
| ConfigAttributeIpv6Pcscf ip6Pcscf = (ConfigAttributeIpv6Pcscf) attr; |
| mPcscfServers.add(ip6Pcscf.getAddress()); |
| break; |
| default: |
| // Not relevant to IKE session |
| } |
| } |
| } |
| mRemoteApplicationVersion = appVersion; |
| validateOrThrow(); |
| } |
| |
| /** |
| * Construct an instance of {@link IkeSessionConfiguration}. |
| * |
| * @hide |
| */ |
| private IkeSessionConfiguration( |
| IkeSessionConnectionInfo ikeConnInfo, |
| List<InetAddress> pcscfServers, |
| List<byte[]> remoteVendorIds, |
| Set<Integer> enabledExtensions, |
| String remoteApplicationVersion, |
| EapInfo eapInfo) { |
| mIkeConnInfo = ikeConnInfo; |
| mPcscfServers.addAll(pcscfServers); |
| mRemoteVendorIds.addAll(remoteVendorIds); |
| mEnabledExtensions.addAll(enabledExtensions); |
| mRemoteApplicationVersion = remoteApplicationVersion; |
| mEapInfo = eapInfo; |
| |
| validateOrThrow(); |
| } |
| |
| private void validateOrThrow() { |
| String errMsg = " was null"; |
| Objects.requireNonNull(mIkeConnInfo, "ikeConnInfo" + errMsg); |
| Objects.requireNonNull(mPcscfServers, "pcscfServers" + errMsg); |
| Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg); |
| Objects.requireNonNull(mRemoteApplicationVersion, "remoteApplicationVersion" + errMsg); |
| Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg); |
| } |
| |
| /** |
| * Gets remote (server) version information. |
| * |
| * @return application version of the remote server, or an empty string if the remote server did |
| * not provide the application version. |
| */ |
| @NonNull |
| public String getRemoteApplicationVersion() { |
| return mRemoteApplicationVersion; |
| } |
| |
| /** |
| * Returns remote vendor IDs received during IKE Session setup. |
| * |
| * <p>According to the IKEv2 specification (RFC 7296), a vendor ID may indicate the sender is |
| * capable of accepting certain extensions to the protocol, or it may simply identify the |
| * implementation as an aid in debugging. |
| * |
| * @return the vendor IDs of the remote server, or an empty list if no vendor ID is received |
| * during IKE Session setup. |
| */ |
| @NonNull |
| public List<byte[]> getRemoteVendorIds() { |
| return Collections.unmodifiableList(mRemoteVendorIds); |
| } |
| |
| /** |
| * Checks if an IKE extension is enabled. |
| * |
| * <p>An IKE extension is enabled when both sides can support it. This negotiation always |
| * happens in IKE initial exchanges (IKE INIT and IKE AUTH). |
| * |
| * @param extensionType the extension type. |
| * @return {@code true} if this extension is enabled. |
| */ |
| public boolean isIkeExtensionEnabled(@ExtensionType int extensionType) { |
| return mEnabledExtensions.contains(extensionType); |
| } |
| |
| /** |
| * Returns the assigned P_CSCF servers. |
| * |
| * @return the assigned P_CSCF servers, or an empty list when no servers are assigned by the |
| * remote IKE server. |
| * @hide |
| */ |
| @SystemApi |
| @NonNull |
| public List<InetAddress> getPcscfServers() { |
| return Collections.unmodifiableList(mPcscfServers); |
| } |
| |
| /** |
| * Returns the connection information. |
| * |
| * @return the IKE Session connection information. |
| */ |
| @NonNull |
| public IkeSessionConnectionInfo getIkeSessionConnectionInfo() { |
| return mIkeConnInfo; |
| } |
| |
| /** |
| * Retrieves the EAP information. |
| * |
| * @return the EAP information provided by the server during EAP authentication (e.g. next |
| * re-authentication ID), or null if the server did not provide any information that will be |
| * useful after the authentication. |
| */ |
| @Nullable |
| public EapInfo getEapInfo() { |
| return mEapInfo; |
| } |
| |
| /** |
| * This class can be used to incrementally construct a {@link IkeSessionConfiguration}. |
| * |
| * <p>Except for testing, IKE library users normally do not instantiate {@link |
| * IkeSessionConfiguration} themselves but instead get a reference via {@link |
| * IkeSessionCallback} |
| */ |
| public static final class Builder { |
| private final IkeSessionConnectionInfo mIkeConnInfo; |
| private final List<InetAddress> mPcscfServers = new ArrayList<>(); |
| private final List<byte[]> mRemoteVendorIds = new ArrayList<>(); |
| private final Set<Integer> mEnabledExtensions = new HashSet<>(); |
| private String mRemoteApplicationVersion = ""; |
| private EapInfo mEapInfo; |
| |
| /** |
| * Constructs a Builder. |
| * |
| * @param ikeConnInfo the connection information |
| */ |
| public Builder(@NonNull IkeSessionConnectionInfo ikeConnInfo) { |
| Objects.requireNonNull(ikeConnInfo, "ikeConnInfo was null"); |
| mIkeConnInfo = ikeConnInfo; |
| } |
| |
| /** |
| * Adds an assigned P_CSCF server for the {@link IkeSessionConfiguration} being built. |
| * |
| * @param pcscfServer an assigned P_CSCF server |
| * @return Builder this, to facilitate chaining |
| * @hide |
| */ |
| @SystemApi |
| @NonNull |
| public Builder addPcscfServer(@NonNull InetAddress pcscfServer) { |
| Objects.requireNonNull(pcscfServer, "pcscfServer was null"); |
| mPcscfServers.add(pcscfServer); |
| return this; |
| } |
| |
| /** |
| * Clear all P_CSCF servers from the {@link IkeSessionConfiguration} being built. |
| * |
| * @return Builder this, to facilitate chaining |
| * @hide |
| */ |
| @SystemApi |
| @NonNull |
| public Builder clearPcscfServers() { |
| mPcscfServers.clear(); |
| return this; |
| } |
| |
| /** |
| * Adds a remote vendor ID for the {@link IkeSessionConfiguration} being built. |
| * |
| * @param remoteVendorId a remote vendor ID |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder addRemoteVendorId(@NonNull byte[] remoteVendorId) { |
| Objects.requireNonNull(remoteVendorId, "remoteVendorId was null"); |
| mRemoteVendorIds.add(remoteVendorId); |
| return this; |
| } |
| |
| /** |
| * Clears all remote vendor IDs from the {@link IkeSessionConfiguration} being built. |
| * |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder clearRemoteVendorIds() { |
| mRemoteVendorIds.clear(); |
| return this; |
| } |
| |
| /** |
| * Sets the remote application version for the {@link IkeSessionConfiguration} being built. |
| * |
| * @param remoteApplicationVersion the remote application version. Defaults to an empty |
| * string. |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder setRemoteApplicationVersion(@NonNull String remoteApplicationVersion) { |
| Objects.requireNonNull(remoteApplicationVersion, "remoteApplicationVersion was null"); |
| mRemoteApplicationVersion = remoteApplicationVersion; |
| return this; |
| } |
| |
| /** |
| * Clears the remote application version from the {@link IkeSessionConfiguration} being |
| * built. |
| * |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder clearRemoteApplicationVersion() { |
| mRemoteApplicationVersion = ""; |
| return this; |
| } |
| |
| private static void validateExtensionOrThrow(@ExtensionType int extensionType) { |
| if (extensionType >= VALID_EXTENSION_MIN && extensionType <= VALID_EXTENSION_MAX) { |
| return; |
| } |
| throw new IllegalArgumentException("Invalid extension type: " + extensionType); |
| } |
| |
| /** |
| * Marks an IKE extension as enabled for the {@link IkeSessionConfiguration} being built. |
| * |
| * @param extensionType the enabled extension |
| * @return Builder this, to facilitate chaining |
| */ |
| // MissingGetterMatchingBuilder: Use #isIkeExtensionEnabled instead of #getIkeExtension |
| // because #isIkeExtensionEnabled allows callers to check the presence of an IKE extension |
| // more easily |
| @SuppressLint("MissingGetterMatchingBuilder") |
| @NonNull |
| public Builder addIkeExtension(@ExtensionType int extensionType) { |
| validateExtensionOrThrow(extensionType); |
| mEnabledExtensions.add(extensionType); |
| return this; |
| } |
| |
| /** |
| * Clear all enabled IKE extensions from the {@link IkeSessionConfiguration} being built. |
| * |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder clearIkeExtensions() { |
| mEnabledExtensions.clear(); |
| return this; |
| } |
| |
| /** |
| * Sets EapInfo for the {@link IkeSessionConfiguration} being built. |
| * |
| * @return Builder this, to facilitate chaining |
| */ |
| @NonNull |
| public Builder setEapInfo(@Nullable EapInfo eapInfo) { |
| mEapInfo = eapInfo; |
| return this; |
| } |
| |
| /** Constructs an {@link IkeSessionConfiguration} instance. */ |
| @NonNull |
| public IkeSessionConfiguration build() { |
| return new IkeSessionConfiguration( |
| mIkeConnInfo, |
| mPcscfServers, |
| mRemoteVendorIds, |
| mEnabledExtensions, |
| mRemoteApplicationVersion, |
| mEapInfo); |
| } |
| } |
| } |