1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.ipsec.ike;
18 
19 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_APPLICATION_VERSION;
20 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP4_PCSCF;
21 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP6_PCSCF;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.SuppressLint;
27 import android.annotation.SystemApi;
28 import android.net.eap.EapInfo;
29 
30 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload;
31 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute;
32 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeAppVersion;
33 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Pcscf;
34 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Pcscf;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.net.InetAddress;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.Set;
45 
46 /**
47  * IkeSessionConfiguration represents the negotiated configuration for a {@link IkeSession}.
48  *
49  * <p>Configurations include remote application version and enabled IKE extensions.
50  */
51 public final class IkeSessionConfiguration {
52     /** @hide */
53     @Retention(RetentionPolicy.SOURCE)
54     @IntDef({EXTENSION_TYPE_FRAGMENTATION, EXTENSION_TYPE_MOBIKE})
55     public @interface ExtensionType {}
56 
57     /** IKE Message Fragmentation */
58     public static final int EXTENSION_TYPE_FRAGMENTATION = 1;
59     /** IKEv2 Mobility and Multihoming Protocol */
60     public static final int EXTENSION_TYPE_MOBIKE = 2;
61 
62     private static final int VALID_EXTENSION_MIN = EXTENSION_TYPE_FRAGMENTATION;
63     private static final int VALID_EXTENSION_MAX = EXTENSION_TYPE_MOBIKE;
64 
65     private final String mRemoteApplicationVersion;
66     private final IkeSessionConnectionInfo mIkeConnInfo;
67     private final List<InetAddress> mPcscfServers = new ArrayList<>();
68     private final List<byte[]> mRemoteVendorIds = new ArrayList<>();
69     private final Set<Integer> mEnabledExtensions = new HashSet<>();
70     private final EapInfo mEapInfo;
71 
72     /**
73      * Construct an instance of {@link IkeSessionConfiguration}.
74      *
75      * <p>IkeSessionConfigurations may contain negotiated configuration information that is included
76      * in a Configure(Reply) Payload. Thus the input configPayload should always be a
77      * Configure(Reply), and never be a Configure(Request).
78      *
79      * @hide
80      */
IkeSessionConfiguration( IkeSessionConnectionInfo ikeConnInfo, IkeConfigPayload configPayload, List<byte[]> remoteVendorIds, List<Integer> enabledExtensions, EapInfo eapInfo)81     public IkeSessionConfiguration(
82             IkeSessionConnectionInfo ikeConnInfo,
83             IkeConfigPayload configPayload,
84             List<byte[]> remoteVendorIds,
85             List<Integer> enabledExtensions,
86             EapInfo eapInfo) {
87         mIkeConnInfo = ikeConnInfo;
88         mRemoteVendorIds.addAll(remoteVendorIds);
89         mEnabledExtensions.addAll(enabledExtensions);
90         mEapInfo = eapInfo;
91 
92         String appVersion = "";
93         if (configPayload != null) {
94             if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) {
95                 throw new IllegalArgumentException(
96                         "Cannot build IkeSessionConfiguration with configuration type: "
97                                 + configPayload.configType);
98             }
99 
100             for (ConfigAttribute attr : configPayload.recognizedAttributeList) {
101                 if (attr.isEmptyValue()) continue;
102                 switch (attr.attributeType) {
103                     case CONFIG_ATTR_APPLICATION_VERSION:
104                         ConfigAttributeAppVersion appVersionAttr = (ConfigAttributeAppVersion) attr;
105                         appVersion = appVersionAttr.applicationVersion;
106                         break;
107                     case CONFIG_ATTR_IP4_PCSCF:
108                         ConfigAttributeIpv4Pcscf ip4Pcscf = (ConfigAttributeIpv4Pcscf) attr;
109                         mPcscfServers.add(ip4Pcscf.getAddress());
110                         break;
111                     case CONFIG_ATTR_IP6_PCSCF:
112                         ConfigAttributeIpv6Pcscf ip6Pcscf = (ConfigAttributeIpv6Pcscf) attr;
113                         mPcscfServers.add(ip6Pcscf.getAddress());
114                         break;
115                     default:
116                         // Not relevant to IKE session
117                 }
118             }
119         }
120         mRemoteApplicationVersion = appVersion;
121         validateOrThrow();
122     }
123 
124     /**
125      * Construct an instance of {@link IkeSessionConfiguration}.
126      *
127      * @hide
128      */
IkeSessionConfiguration( IkeSessionConnectionInfo ikeConnInfo, List<InetAddress> pcscfServers, List<byte[]> remoteVendorIds, Set<Integer> enabledExtensions, String remoteApplicationVersion, EapInfo eapInfo)129     private IkeSessionConfiguration(
130             IkeSessionConnectionInfo ikeConnInfo,
131             List<InetAddress> pcscfServers,
132             List<byte[]> remoteVendorIds,
133             Set<Integer> enabledExtensions,
134             String remoteApplicationVersion,
135             EapInfo eapInfo) {
136         mIkeConnInfo = ikeConnInfo;
137         mPcscfServers.addAll(pcscfServers);
138         mRemoteVendorIds.addAll(remoteVendorIds);
139         mEnabledExtensions.addAll(enabledExtensions);
140         mRemoteApplicationVersion = remoteApplicationVersion;
141         mEapInfo = eapInfo;
142 
143         validateOrThrow();
144     }
145 
validateOrThrow()146     private void validateOrThrow() {
147         String errMsg = " was null";
148         Objects.requireNonNull(mIkeConnInfo, "ikeConnInfo" + errMsg);
149         Objects.requireNonNull(mPcscfServers, "pcscfServers" + errMsg);
150         Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg);
151         Objects.requireNonNull(mRemoteApplicationVersion, "remoteApplicationVersion" + errMsg);
152         Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg);
153     }
154 
155     /**
156      * Gets remote (server) version information.
157      *
158      * @return application version of the remote server, or an empty string if the remote server did
159      *     not provide the application version.
160      */
161     @NonNull
getRemoteApplicationVersion()162     public String getRemoteApplicationVersion() {
163         return mRemoteApplicationVersion;
164     }
165 
166     /**
167      * Returns remote vendor IDs received during IKE Session setup.
168      *
169      * <p>According to the IKEv2 specification (RFC 7296), a vendor ID may indicate the sender is
170      * capable of accepting certain extensions to the protocol, or it may simply identify the
171      * implementation as an aid in debugging.
172      *
173      * @return the vendor IDs of the remote server, or an empty list if no vendor ID is received
174      *     during IKE Session setup.
175      */
176     @NonNull
getRemoteVendorIds()177     public List<byte[]> getRemoteVendorIds() {
178         return Collections.unmodifiableList(mRemoteVendorIds);
179     }
180 
181     /**
182      * Checks if an IKE extension is enabled.
183      *
184      * <p>An IKE extension is enabled when both sides can support it. This negotiation always
185      * happens in IKE initial exchanges (IKE INIT and IKE AUTH).
186      *
187      * @param extensionType the extension type.
188      * @return {@code true} if this extension is enabled.
189      */
isIkeExtensionEnabled(@xtensionType int extensionType)190     public boolean isIkeExtensionEnabled(@ExtensionType int extensionType) {
191         return mEnabledExtensions.contains(extensionType);
192     }
193 
194     /**
195      * Returns the assigned P_CSCF servers.
196      *
197      * @return the assigned P_CSCF servers, or an empty list when no servers are assigned by the
198      *     remote IKE server.
199      * @hide
200      */
201     @SystemApi
202     @NonNull
getPcscfServers()203     public List<InetAddress> getPcscfServers() {
204         return Collections.unmodifiableList(mPcscfServers);
205     }
206 
207     /**
208      * Returns the connection information.
209      *
210      * @return the IKE Session connection information.
211      */
212     @NonNull
getIkeSessionConnectionInfo()213     public IkeSessionConnectionInfo getIkeSessionConnectionInfo() {
214         return mIkeConnInfo;
215     }
216 
217     /**
218      * Retrieves the EAP information.
219      *
220      * @return the EAP information provided by the server during EAP authentication (e.g. next
221      *    re-authentication ID), or null if the server did not provide any information that will be
222      *    useful after the authentication.
223      */
224     @Nullable
getEapInfo()225     public EapInfo getEapInfo() {
226         return mEapInfo;
227     }
228 
229     /**
230      * This class can be used to incrementally construct a {@link IkeSessionConfiguration}.
231      *
232      * <p>Except for testing, IKE library users normally do not instantiate {@link
233      * IkeSessionConfiguration} themselves but instead get a reference via {@link
234      * IkeSessionCallback}
235      */
236     public static final class Builder {
237         private final IkeSessionConnectionInfo mIkeConnInfo;
238         private final List<InetAddress> mPcscfServers = new ArrayList<>();
239         private final List<byte[]> mRemoteVendorIds = new ArrayList<>();
240         private final Set<Integer> mEnabledExtensions = new HashSet<>();
241         private String mRemoteApplicationVersion = "";
242         private EapInfo mEapInfo;
243 
244         /**
245          * Constructs a Builder.
246          *
247          * @param ikeConnInfo the connection information
248          */
Builder(@onNull IkeSessionConnectionInfo ikeConnInfo)249         public Builder(@NonNull IkeSessionConnectionInfo ikeConnInfo) {
250             Objects.requireNonNull(ikeConnInfo, "ikeConnInfo was null");
251             mIkeConnInfo = ikeConnInfo;
252         }
253 
254         /**
255          * Adds an assigned P_CSCF server for the {@link IkeSessionConfiguration} being built.
256          *
257          * @param pcscfServer an assigned P_CSCF server
258          * @return Builder this, to facilitate chaining
259          * @hide
260          */
261         @SystemApi
262         @NonNull
addPcscfServer(@onNull InetAddress pcscfServer)263         public Builder addPcscfServer(@NonNull InetAddress pcscfServer) {
264             Objects.requireNonNull(pcscfServer, "pcscfServer was null");
265             mPcscfServers.add(pcscfServer);
266             return this;
267         }
268 
269         /**
270          * Clear all P_CSCF servers from the {@link IkeSessionConfiguration} being built.
271          *
272          * @return Builder this, to facilitate chaining
273          * @hide
274          */
275         @SystemApi
276         @NonNull
clearPcscfServers()277         public Builder clearPcscfServers() {
278             mPcscfServers.clear();
279             return this;
280         }
281 
282         /**
283          * Adds a remote vendor ID for the {@link IkeSessionConfiguration} being built.
284          *
285          * @param remoteVendorId a remote vendor ID
286          * @return Builder this, to facilitate chaining
287          */
288         @NonNull
addRemoteVendorId(@onNull byte[] remoteVendorId)289         public Builder addRemoteVendorId(@NonNull byte[] remoteVendorId) {
290             Objects.requireNonNull(remoteVendorId, "remoteVendorId was null");
291             mRemoteVendorIds.add(remoteVendorId);
292             return this;
293         }
294 
295         /**
296          * Clears all remote vendor IDs from the {@link IkeSessionConfiguration} being built.
297          *
298          * @return Builder this, to facilitate chaining
299          */
300         @NonNull
clearRemoteVendorIds()301         public Builder clearRemoteVendorIds() {
302             mRemoteVendorIds.clear();
303             return this;
304         }
305 
306         /**
307          * Sets the remote application version for the {@link IkeSessionConfiguration} being built.
308          *
309          * @param remoteApplicationVersion the remote application version. Defaults to an empty
310          *     string.
311          * @return Builder this, to facilitate chaining
312          */
313         @NonNull
setRemoteApplicationVersion(@onNull String remoteApplicationVersion)314         public Builder setRemoteApplicationVersion(@NonNull String remoteApplicationVersion) {
315             Objects.requireNonNull(remoteApplicationVersion, "remoteApplicationVersion was null");
316             mRemoteApplicationVersion = remoteApplicationVersion;
317             return this;
318         }
319 
320         /**
321          * Clears the remote application version from the {@link IkeSessionConfiguration} being
322          * built.
323          *
324          * @return Builder this, to facilitate chaining
325          */
326         @NonNull
clearRemoteApplicationVersion()327         public Builder clearRemoteApplicationVersion() {
328             mRemoteApplicationVersion = "";
329             return this;
330         }
331 
validateExtensionOrThrow(@xtensionType int extensionType)332         private static void validateExtensionOrThrow(@ExtensionType int extensionType) {
333             if (extensionType >= VALID_EXTENSION_MIN && extensionType <= VALID_EXTENSION_MAX) {
334                 return;
335             }
336             throw new IllegalArgumentException("Invalid extension type: " + extensionType);
337         }
338 
339         /**
340          * Marks an IKE extension as enabled for the {@link IkeSessionConfiguration} being built.
341          *
342          * @param extensionType the enabled extension
343          * @return Builder this, to facilitate chaining
344          */
345         // MissingGetterMatchingBuilder: Use #isIkeExtensionEnabled instead of #getIkeExtension
346         // because #isIkeExtensionEnabled allows callers to check the presence of an IKE extension
347         // more easily
348         @SuppressLint("MissingGetterMatchingBuilder")
349         @NonNull
addIkeExtension(@xtensionType int extensionType)350         public Builder addIkeExtension(@ExtensionType int extensionType) {
351             validateExtensionOrThrow(extensionType);
352             mEnabledExtensions.add(extensionType);
353             return this;
354         }
355 
356         /**
357          * Clear all enabled IKE extensions from the {@link IkeSessionConfiguration} being built.
358          *
359          * @return Builder this, to facilitate chaining
360          */
361         @NonNull
clearIkeExtensions()362         public Builder clearIkeExtensions() {
363             mEnabledExtensions.clear();
364             return this;
365         }
366 
367         /**
368          * Sets EapInfo for the {@link IkeSessionConfiguration} being built.
369          *
370          * @return Builder this, to facilitate chaining
371          */
372         @NonNull
setEapInfo(@ullable EapInfo eapInfo)373         public Builder setEapInfo(@Nullable EapInfo eapInfo) {
374             mEapInfo = eapInfo;
375             return this;
376         }
377 
378         /** Constructs an {@link IkeSessionConfiguration} instance. */
379         @NonNull
build()380         public IkeSessionConfiguration build() {
381             return new IkeSessionConfiguration(
382                     mIkeConnInfo,
383                     mPcscfServers,
384                     mRemoteVendorIds,
385                     mEnabledExtensions,
386                     mRemoteApplicationVersion,
387                     mEapInfo);
388         }
389     }
390 }
391