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 package android.net.ipsec.ike;
17 
18 import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_NONE;
19 import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_UDP;
20 import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV4;
21 import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV6;
22 import static android.net.ipsec.ike.IkeSessionParams.IKE_NATT_KEEPALIVE_DELAY_SEC_MAX;
23 import static android.net.ipsec.ike.IkeSessionParams.IKE_NATT_KEEPALIVE_DELAY_SEC_MIN;
24 
25 import android.annotation.FlaggedApi;
26 import android.annotation.IntRange;
27 import android.annotation.NonNull;
28 import android.annotation.SuppressLint;
29 import android.annotation.SystemApi;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.net.IpSecManager;
33 import android.net.Network;
34 import android.net.ipsec.ike.exceptions.IkeException;
35 import android.os.HandlerThread;
36 import android.os.Looper;
37 import android.util.CloseGuard;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.net.ipsec.ike.IkeSessionStateMachine;
41 
42 import java.io.PrintWriter;
43 import java.util.Objects;
44 import java.util.concurrent.Executor;
45 
46 /**
47  * This class represents an IKE Session management object that allows for keying and management of
48  * {@link android.net.IpSecTransform}s.
49  *
50  * <p>An IKE/Child Session represents an IKE/Child SA as well as its rekeyed successors. A Child
51  * Session is bounded by the lifecycle of the IKE Session under which it is set up. Closing an IKE
52  * Session implicitly closes any remaining Child Sessions under it.
53  *
54  * <p>An IKE procedure is one or multiple IKE message exchanges that are used to create, delete or
55  * rekey an IKE Session or Child Session.
56  *
57  * <p>This class provides methods for initiating IKE procedures, such as the Creation and Deletion
58  * of a Child Session, or the Deletion of the IKE session. All procedures (except for IKE deletion)
59  * will be initiated sequentially after IKE Session is set up.
60  *
61  * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol
62  *     Version 2 (IKEv2)</a>
63  */
64 public final class IkeSession implements AutoCloseable {
65     private final CloseGuard mCloseGuard = new CloseGuard();
66     private final Context mContext;
67 
68     /**
69      * Attribution tag for IWLAN callers
70      *
71      * @hide
72      */
73     public static final String CONTEXT_ATTRIBUTION_TAG_IWLAN = "IWLAN";
74 
75     /**
76      * Attribution tag for VCN callers
77      *
78      * @hide
79      */
80     public static final String CONTEXT_ATTRIBUTION_TAG_VCN = "VCN";
81 
82     /**
83      * Attribution tag for VPN callers
84      *
85      * @hide
86      */
87     public static final String CONTEXT_ATTRIBUTION_TAG_VPN = "VPN_MANAGER";
88 
89     @VisibleForTesting final IkeSessionStateMachine mIkeSessionStateMachine;
90 
91     /**
92      * Constructs a new IKE session.
93      *
94      * <p>This method will immediately return an instance of {@link IkeSession} and asynchronously
95      * initiate the setup procedure of {@link IkeSession} as well as its first Child Session.
96      * Callers will be notified of these two setup results via the callback arguments.
97      *
98      * <p>FEATURE_IPSEC_TUNNELS is required for setting up a tunnel mode Child SA.
99      *
100      * @param context a valid {@link Context} instance.
101      * @param ikeSessionParams the {@link IkeSessionParams} that contains a set of valid {@link
102      *     IkeSession} configurations.
103      * @param firstChildSessionParams the {@link ChildSessionParams} that contains a set of valid
104      *     configurations for the first Child Session.
105      * @param userCbExecutor the {@link Executor} upon which all callbacks will be posted. For
106      *     security and consistency, the callbacks posted to this executor MUST be executed serially
107      *     and in the order they were posted, as guaranteed by executors such as {@link
108      *     java.util.concurrent.Executors#newSingleThreadExecutor()}
109      * @param ikeSessionCallback the {@link IkeSessionCallback} interface to notify callers of state
110      *     changes within the {@link IkeSession}.
111      * @param firstChildSessionCallback the {@link ChildSessionCallback} interface to notify callers
112      *     of state changes within the first Child Session.
113      * @return an instance of {@link IkeSession}.
114      */
IkeSession( @onNull Context context, @NonNull IkeSessionParams ikeSessionParams, @NonNull ChildSessionParams firstChildSessionParams, @NonNull Executor userCbExecutor, @NonNull IkeSessionCallback ikeSessionCallback, @NonNull ChildSessionCallback firstChildSessionCallback)115     public IkeSession(
116             @NonNull Context context,
117             @NonNull IkeSessionParams ikeSessionParams,
118             @NonNull ChildSessionParams firstChildSessionParams,
119             @NonNull Executor userCbExecutor,
120             @NonNull IkeSessionCallback ikeSessionCallback,
121             @NonNull ChildSessionCallback firstChildSessionCallback) {
122         this(
123                 context,
124                 (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE),
125                 ikeSessionParams,
126                 firstChildSessionParams,
127                 userCbExecutor,
128                 ikeSessionCallback,
129                 firstChildSessionCallback);
130     }
131 
132     /** Package private */
133     @VisibleForTesting
IkeSession( Context context, IpSecManager ipSecManager, IkeSessionParams ikeSessionParams, ChildSessionParams firstChildSessionParams, Executor userCbExecutor, IkeSessionCallback ikeSessionCallback, ChildSessionCallback firstChildSessionCallback)134     IkeSession(
135             Context context,
136             IpSecManager ipSecManager,
137             IkeSessionParams ikeSessionParams,
138             ChildSessionParams firstChildSessionParams,
139             Executor userCbExecutor,
140             IkeSessionCallback ikeSessionCallback,
141             ChildSessionCallback firstChildSessionCallback) {
142         this(
143                 IkeThreadHolder.IKE_WORKER_THREAD.getLooper(),
144                 context,
145                 ipSecManager,
146                 ikeSessionParams,
147                 firstChildSessionParams,
148                 userCbExecutor,
149                 ikeSessionCallback,
150                 firstChildSessionCallback);
151     }
152 
153     /** Package private */
154     @VisibleForTesting
IkeSession( Looper looper, Context context, IpSecManager ipSecManager, IkeSessionParams ikeSessionParams, ChildSessionParams firstChildSessionParams, Executor userCbExecutor, IkeSessionCallback ikeSessionCallback, ChildSessionCallback firstChildSessionCallback)155     IkeSession(
156             Looper looper,
157             Context context,
158             IpSecManager ipSecManager,
159             IkeSessionParams ikeSessionParams,
160             ChildSessionParams firstChildSessionParams,
161             Executor userCbExecutor,
162             IkeSessionCallback ikeSessionCallback,
163             ChildSessionCallback firstChildSessionCallback) {
164         mContext = context;
165 
166         if (firstChildSessionParams instanceof TunnelModeChildSessionParams) {
167             checkTunnelFeatureOrThrow(mContext);
168         }
169 
170         mIkeSessionStateMachine =
171                 new IkeSessionStateMachine(
172                         looper,
173                         context,
174                         ipSecManager,
175                         ikeSessionParams,
176                         firstChildSessionParams,
177                         userCbExecutor,
178                         ikeSessionCallback,
179                         firstChildSessionCallback);
180         mIkeSessionStateMachine.openSession();
181 
182         mCloseGuard.open("open");
183     }
184 
185     /** @hide */
186     @Override
finalize()187     public void finalize() {
188         if (mCloseGuard != null) {
189             mCloseGuard.warnIfOpen();
190         }
191     }
192 
checkTunnelFeatureOrThrow(Context context)193     private void checkTunnelFeatureOrThrow(Context context) {
194         // TODO(b/157754168): Also check if OP_MANAGE_IPSEC_TUNNELS is granted when it is exposed
195         if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
196             throw new IllegalStateException(
197                     "Cannot set up tunnel mode Child SA due to FEATURE_IPSEC_TUNNELS missing");
198         }
199     }
200 
201     /** Initialization-on-demand holder */
202     private static class IkeThreadHolder {
203         static final HandlerThread IKE_WORKER_THREAD;
204 
205         static {
206             IKE_WORKER_THREAD = new HandlerThread("IkeWorkerThread");
IKE_WORKER_THREAD.start()207             IKE_WORKER_THREAD.start();
208         }
209     }
210 
211     // TODO: b/133340675 Destroy the worker thread when there is no more alive {@link IkeSession}.
212 
213     /**
214      * Request a new Child Session.
215      *
216      * <p>Users MUST provide a unique {@link ChildSessionCallback} instance for each new Child
217      * Session.
218      *
219      * <p>Upon setup, {@link ChildSessionCallback#onOpened(ChildSessionConfiguration)} will be
220      * fired.
221      *
222      * <p>FEATURE_IPSEC_TUNNELS is required for setting up a tunnel mode Child SA.
223      *
224      * @param childSessionParams the {@link ChildSessionParams} that contains the Child Session
225      *     configurations to negotiate.
226      * @param childSessionCallback the {@link ChildSessionCallback} interface to notify users the
227      *     state changes of the Child Session. It will be posted to the callback {@link Executor} of
228      *     this {@link IkeSession}.
229      * @throws IllegalArgumentException if the ChildSessionCallback is already in use.
230      */
231     // The childSessionCallback will be called on the same executor as was passed in the constructor
232     // for security reasons.
233     @SuppressLint("ExecutorRegistration")
openChildSession( @onNull ChildSessionParams childSessionParams, @NonNull ChildSessionCallback childSessionCallback)234     public void openChildSession(
235             @NonNull ChildSessionParams childSessionParams,
236             @NonNull ChildSessionCallback childSessionCallback) {
237         if (childSessionParams instanceof TunnelModeChildSessionParams) {
238             checkTunnelFeatureOrThrow(mContext);
239         }
240 
241         mIkeSessionStateMachine.openChildSession(childSessionParams, childSessionCallback);
242     }
243 
244     /**
245      * Delete a Child Session.
246      *
247      * <p>Upon closure, {@link ChildSessionCallback#onClosed()} will be fired.
248      *
249      * @param childSessionCallback The {@link ChildSessionCallback} instance that uniquely identify
250      *     the Child Session.
251      * @throws IllegalArgumentException if no Child Session found bound with this callback.
252      */
253     // The childSessionCallback will be called on the same executor as was passed in the constructor
254     // for security reasons.
255     @SuppressLint("ExecutorRegistration")
closeChildSession(@onNull ChildSessionCallback childSessionCallback)256     public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
257         mIkeSessionStateMachine.closeChildSession(childSessionCallback);
258     }
259 
260     /**
261      * Close the IKE session gracefully.
262      *
263      * <p>Implements {@link AutoCloseable#close()}
264      *
265      * <p>Upon closure, {@link IkeSessionCallback#onClosed()} or {@link
266      * IkeSessionCallback#onClosedWithException(IkeException)} will be fired.
267      *
268      * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it.
269      * Users SHOULD stop all outbound traffic that uses these Child Sessions ({@link
270      * android.net.IpSecTransform} pairs) before calling this method. Otherwise IPsec packets will
271      * be dropped due to the lack of a valid {@link android.net.IpSecTransform}.
272      *
273      * <p>Closure of an IKE session will take priority over, and cancel other procedures waiting in
274      * the queue (but will wait for ongoing locally initiated procedures to complete). After sending
275      * the Delete request, the IKE library will wait until a Delete response is received or
276      * retransmission timeout occurs.
277      */
278     @Override
close()279     public void close() {
280         mCloseGuard.close();
281         mIkeSessionStateMachine.closeSession();
282     }
283 
284     /**
285      * Terminate (forcibly close) the IKE session.
286      *
287      * <p>Upon closing, {@link IkeSessionCallback#onClosed()} will be fired.
288      *
289      * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it.
290      * Users SHOULD stop all outbound traffic that uses these Child Sessions ({@link
291      * android.net.IpSecTransform} pairs) before calling this method. Otherwise IPsec packets will
292      * be dropped due to the lack of a valid {@link android.net.IpSecTransform}.
293      *
294      * <p>Forcible closure of an IKE session will take priority over, and cancel other procedures
295      * waiting in the queue. It will also interrupt any ongoing locally initiated procedure.
296      */
kill()297     public void kill() {
298         mCloseGuard.close();
299         mIkeSessionStateMachine.killSession();
300     }
301 
302     /**
303      * Update the IkeSession's underlying Network to use the specified Network.
304      *
305      * @see #setNetwork(Network, int, int)
306      * @hide
307      */
308     @SystemApi
setNetwork(@onNull Network network)309     public void setNetwork(@NonNull Network network) {
310         setNetwork(network, IkeSessionParams.ESP_IP_VERSION_AUTO,
311                 IkeSessionParams.ESP_ENCAP_TYPE_AUTO,
312                 IkeSessionParams.NATT_KEEPALIVE_INTERVAL_AUTO);
313     }
314 
315     /**
316      * Update the IkeSession's underlying Network, protocol preference and keepalive delay.
317      *
318      * <p>Updating the IkeSession's Network also updates the Network for any Child Sessions created
319      * with this IkeSession. To perform the update, callers must implement:
320      *
321      * <ul>
322      *   <li>{@link IkeSessionCallback#onIkeSessionConnectionInfoChanged(IkeSessionConnectionInfo)}:
323      *       This call will be triggered once the IKE Session has been updated. The implementation
324      *       MUST migrate all IpSecTunnelInterface instances associated with this IkeSession via
325      *       {@link android.net.IpSecManager.IpSecTunnelInterface#setUnderlyingNetwork(Network)}
326      *   <li>{@link ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform,
327      *       android.net.IpSecTransform)}: This call will be triggered once a Child Session has been
328      *       updated. The implementation MUST re-apply the migrated transforms to the {@link
329      *       android.net.IpSecManager.IpSecTunnelInterface} associated with this
330      *       ChildSessionCallback, via {@link android.net.IpSecManager#applyTunnelModeTransform(
331      *       android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform)}.
332      * </ul>
333      *
334      * <p>In order for Network migration to be possible, the following must be true:
335      *
336      * <ul>
337      *   <li>the {@link IkeSessionParams} for this IkeSession must be configured with {@link
338      *       IkeSessionParams#IKE_OPTION_MOBIKE} (set via {@link
339      *       IkeSessionParams.Builder#addIkeOption(int)}), and
340      *   <li>the IkeSession must have been started with the Network specified via {@link
341      *       IkeSessionParams.Builder#setNetwork(Network)}.
342      * </ul>
343      *
344      * <p>As MOBIKE support is negotiated, callers are advised to check for MOBIKE support in {@link
345      * IkeSessionConfiguration} before calling this method to update the network. Failure to do so
346      * may cause this call to be ignored.
347      *
348      * @see <a href="https://tools.ietf.org/html/rfc4555">RFC 4555, IKEv2 Mobility and Multihoming
349      *     Protocol (MOBIKE)</a>
350      * @param network the Network to use for this IkeSession
351      * @param ipVersion the IP version to use for ESP packets
352      * @param encapType the encapsulation type to use for ESP packets
353      * @param keepaliveDelaySeconds the keepalive delay in seconds, or NATT_KEEPALIVE_INTERVAL_AUTO
354      *                              to choose the value automatically based on the network.
355      * @throws IllegalStateException if {@link IkeSessionParams#IKE_OPTION_MOBIKE} is not configured
356      *     in IkeSessionParams, or if the Network was not specified in IkeSessionParams.
357      * @throws UnsupportedOperationException if the provided option is not supported.
358      * @see IkeSessionParams#getNattKeepAliveDelaySeconds()
359      * @hide
360      */
361     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
setNetwork( @onNull Network network, @IkeSessionParams.EspIpVersion int ipVersion, @IkeSessionParams.EspEncapType int encapType, @IntRange( from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN, to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX) int keepaliveDelaySeconds)362     public void setNetwork(
363             @NonNull Network network,
364             @IkeSessionParams.EspIpVersion int ipVersion,
365             @IkeSessionParams.EspEncapType int encapType,
366             // Is there a way to specify an intrange + a sentinel value ?
367             @IntRange(
368                     from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN,
369                     to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX)
370             int keepaliveDelaySeconds) {
371         if ((ipVersion ==  ESP_IP_VERSION_IPV4 && encapType == ESP_ENCAP_TYPE_NONE)
372                 || (ipVersion == ESP_IP_VERSION_IPV6 && encapType == ESP_ENCAP_TYPE_UDP)) {
373             throw new UnsupportedOperationException("Sending packets with IPv4 ESP or IPv6 UDP"
374                     + " are not supported");
375         }
376 
377         mIkeSessionStateMachine.setNetwork(Objects.requireNonNull(network),
378                 ipVersion, encapType, keepaliveDelaySeconds);
379     }
380 
381     /**
382      * Inform the session that it is used to supply the passed network.
383      *
384      * This will be used by the session when it needs to perform actions that depend on what
385      * network this session is underpinning. In particular, this can be used to turn off
386      * keepalives when there are no connections open on the underpinned network, if the
387      * {@link IkeSessionParams#IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF} option is turned on.
388      *
389      * @param underpinnedNetwork the network underpinned by this session.
390      * @hide
391      */
392     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
setUnderpinnedNetwork(@onNull Network underpinnedNetwork)393     public void setUnderpinnedNetwork(@NonNull Network underpinnedNetwork) {
394         mIkeSessionStateMachine.setUnderpinnedNetwork(Objects.requireNonNull(underpinnedNetwork));
395     }
396 
397     /**
398      * Request to check liveness of peer.
399      *
400      * <p>This method returns immediately and asynchronously,
401      *
402      * <p>The liveness check determines whether a peer is alive by executing a new on-demand DPD
403      * task or joining an existing running task depending on the situation.
404      *
405      * <ul>
406      *   <li>If there is no running task when a liveness check request is called, a new on-demand
407      *       DPD task is started. The on-demand DPD (Dead Peer Detection) is used for checking
408      *       liveness of peer in this case. This method adds an on-demand DPD request to the work
409      *       queue to check that the peer is alive. The on-demand DPD uses retransmit timeouts from
410      *       {@link IkeSessionParams#getLivenessRetransmissionTimeoutsMillis}.
411      *   <li>If any IKE message is already in progress when a client requests a liveness check, the
412      *       liveness check request is joined to an existing running task. And then the liveness
413      *       check runs in the background. When a running task receives a valid IKE message packet
414      *       from a peer, it can verify that the peer is alive in the background without triggering
415      *       an on-demand DPD task. A running task uses retransmit timeouts from {@link
416      *       IkeSessionParams#getRetransmissionTimeoutsMillis}.
417      * </ul>
418      *
419      * <p>The client is notified of the progress or result statuses of the liveness check via {@link
420      * IkeSessionCallback#onLivenessStatusChanged}. These statuses are notified after a liveness
421      * check request is started. By notifying {@link
422      * IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_SUCCESS} or {@link
423      * IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_FAILURE}, the liveness check request is
424      * done and no further status notification is made until the next {@link
425      * IkeSession#requestLivenessCheck}. The status notifications to the client are as follows.
426      *
427      * <ul>
428      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_ON_DEMAND_STARTED}: This
429      *       status is called when liveness checking is started with a new on-demand DPD task.
430      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_ON_DEMAND_ONGOING}: This
431      *       status is called when liveness checking is already running in an on-demand DPD task.
432      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_BACKGROUND_STARTED}: This
433      *       status is called when liveness checking is started in the background and has joined an
434      *       existing running task.
435      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_BACKGROUND_ONGOING}: This
436      *       status is called when liveness checking is already running in the background by joining
437      *       an existing running task.
438      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_SUCCESS}: This status is
439      *       called when the peer's liveness is proven. Once this status is notified, the liveness
440      *       check request is done and no further status notification is made until the next {@link
441      *       IkeSession#requestLivenessCheck}.
442      *   <li>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_FAILURE}: This state is called
443      *       when the peer is determined as dead for a liveness check request. After this status is
444      *       notified, the IkeSession will be closed immediately by calling {@link
445      *       IkeSessionCallback#onClosedWithException} with {@link
446      *       android.net.ipsec.ike.exceptions.IkeTimeoutException} in the {@link
447      *       IkeException#getCause()}.
448      * </ul>
449      *
450      * <p>If a valid IKE message response is received from the peer, the IkeSession remains as
451      * connected and periodic DPD reschedules by {@link IkeSessionParams#getDpdDelaySeconds}
452      *
453      * <p>If the liveness check request couldn't get any a peer's valid response in retransmission
454      * timeout, The IkeSession will be closed. Session closing is also notified to {@link
455      * IkeSessionCallback#onClosedWithException} with {@link
456      * android.net.ipsec.ike.exceptions.IkeTimeoutException} cause.
457      *
458      * @hide
459      */
460     @SystemApi
461     @FlaggedApi("com.android.ipsec.flags.liveness_check_api")
requestLivenessCheck()462     public void requestLivenessCheck() {
463         mIkeSessionStateMachine.requestLivenessCheck();
464     }
465 
466     /**
467      * Dumps the state of {@link IkeSession} information for the clients
468      *
469      * @param pw Print writer
470      */
471     @FlaggedApi("com.android.ipsec.flags.dumpsys_api")
dump(@onNull PrintWriter pw)472     public void dump(@NonNull PrintWriter pw) {
473         // TODO(b/336409878): Add @RequiresPermission annotation.
474         mContext.enforceCallingOrSelfPermission(
475                 android.Manifest.permission.DUMP, mContext.getAttributionTag());
476 
477         // Please make sure that the dump is thread-safe
478         // so the client won't get a crash or exception when adding codes to the dump.
479         pw.println();
480         pw.println("IkeSession:");
481         pw.println("------------------------------");
482         // Dump ike state machine.
483         if (mIkeSessionStateMachine != null) {
484             pw.println();
485             mIkeSessionStateMachine.dump(pw);
486         }
487         pw.println("------------------------------");
488     }
489 }
490