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 android.annotation.NonNull;
19 import android.annotation.SuppressLint;
20 import android.annotation.SystemApi;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.net.IpSecManager;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.util.CloseGuard;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.net.ipsec.ike.IkeSessionStateMachine;
30 
31 import java.util.concurrent.Executor;
32 
33 /**
34  * This class represents an IKE Session management object that allows for keying and management of
35  * {@link IpSecTransform}s.
36  *
37  * <p>An IKE/Child Session represents an IKE/Child SA as well as its rekeyed successors. A Child
38  * Session is bounded by the lifecycle of the IKE Session under which it is set up. Closing an IKE
39  * Session implicitly closes any remaining Child Sessions under it.
40  *
41  * <p>An IKE procedure is one or multiple IKE message exchanges that are used to create, delete or
42  * rekey an IKE Session or Child Session.
43  *
44  * <p>This class provides methods for initiating IKE procedures, such as the Creation and Deletion
45  * of a Child Session, or the Deletion of the IKE session. All procedures (except for IKE deletion)
46  * will be initiated sequentially after IKE Session is set up.
47  *
48  * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol
49  *     Version 2 (IKEv2)</a>
50  * @hide
51  */
52 @SystemApi
53 public final class IkeSession implements AutoCloseable {
54     private final CloseGuard mCloseGuard = new CloseGuard();
55     private final Context mContext;
56 
57     @VisibleForTesting final IkeSessionStateMachine mIkeSessionStateMachine;
58 
59     /**
60      * Constructs a new IKE session.
61      *
62      * <p>This method will immediately return an instance of {@link IkeSession} and asynchronously
63      * initiate the setup procedure of {@link IkeSession} as well as its first Child Session.
64      * Callers will be notified of these two setup results via the callback arguments.
65      *
66      * <p>FEATURE_IPSEC_TUNNELS is required for setting up a tunnel mode Child SA.
67      *
68      * @param context a valid {@link Context} instance.
69      * @param ikeSessionParams the {@link IkeSessionParams} that contains a set of valid {@link
70      *     IkeSession} configurations.
71      * @param firstChildSessionParams the {@link ChildSessionParams} that contains a set of valid
72      *     configurations for the first Child Session.
73      * @param userCbExecutor the {@link Executor} upon which all callbacks will be posted. For
74      *     security and consistency, the callbacks posted to this executor MUST be executed serially
75      *     and in the order they were posted, as guaranteed by executors such as {@link
76      *     ExecutorService.newSingleThreadExecutor()}
77      * @param ikeSessionCallback the {@link IkeSessionCallback} interface to notify callers of state
78      *     changes within the {@link IkeSession}.
79      * @param firstChildSessionCallback the {@link ChildSessionCallback} interface to notify callers
80      *     of state changes within the first Child Session.
81      * @return an instance of {@link IkeSession}.
82      */
IkeSession( @onNull Context context, @NonNull IkeSessionParams ikeSessionParams, @NonNull ChildSessionParams firstChildSessionParams, @NonNull Executor userCbExecutor, @NonNull IkeSessionCallback ikeSessionCallback, @NonNull ChildSessionCallback firstChildSessionCallback)83     public IkeSession(
84             @NonNull Context context,
85             @NonNull IkeSessionParams ikeSessionParams,
86             @NonNull ChildSessionParams firstChildSessionParams,
87             @NonNull Executor userCbExecutor,
88             @NonNull IkeSessionCallback ikeSessionCallback,
89             @NonNull ChildSessionCallback firstChildSessionCallback) {
90         this(
91                 context,
92                 (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE),
93                 ikeSessionParams,
94                 firstChildSessionParams,
95                 userCbExecutor,
96                 ikeSessionCallback,
97                 firstChildSessionCallback);
98     }
99 
100     /** Package private */
101     @VisibleForTesting
IkeSession( Context context, IpSecManager ipSecManager, IkeSessionParams ikeSessionParams, ChildSessionParams firstChildSessionParams, Executor userCbExecutor, IkeSessionCallback ikeSessionCallback, ChildSessionCallback firstChildSessionCallback)102     IkeSession(
103             Context context,
104             IpSecManager ipSecManager,
105             IkeSessionParams ikeSessionParams,
106             ChildSessionParams firstChildSessionParams,
107             Executor userCbExecutor,
108             IkeSessionCallback ikeSessionCallback,
109             ChildSessionCallback firstChildSessionCallback) {
110         this(
111                 IkeThreadHolder.IKE_WORKER_THREAD.getLooper(),
112                 context,
113                 ipSecManager,
114                 ikeSessionParams,
115                 firstChildSessionParams,
116                 userCbExecutor,
117                 ikeSessionCallback,
118                 firstChildSessionCallback);
119     }
120 
121     /** Package private */
122     @VisibleForTesting
IkeSession( Looper looper, Context context, IpSecManager ipSecManager, IkeSessionParams ikeSessionParams, ChildSessionParams firstChildSessionParams, Executor userCbExecutor, IkeSessionCallback ikeSessionCallback, ChildSessionCallback firstChildSessionCallback)123     IkeSession(
124             Looper looper,
125             Context context,
126             IpSecManager ipSecManager,
127             IkeSessionParams ikeSessionParams,
128             ChildSessionParams firstChildSessionParams,
129             Executor userCbExecutor,
130             IkeSessionCallback ikeSessionCallback,
131             ChildSessionCallback firstChildSessionCallback) {
132         mContext = context;
133 
134         if (firstChildSessionParams instanceof TunnelModeChildSessionParams) {
135             checkTunnelFeatureOrThrow(mContext);
136         }
137 
138         mIkeSessionStateMachine =
139                 new IkeSessionStateMachine(
140                         looper,
141                         context,
142                         ipSecManager,
143                         ikeSessionParams,
144                         firstChildSessionParams,
145                         userCbExecutor,
146                         ikeSessionCallback,
147                         firstChildSessionCallback);
148         mIkeSessionStateMachine.openSession();
149 
150         mCloseGuard.open("open");
151     }
152 
153     /** @hide */
154     @Override
finalize()155     public void finalize() {
156         if (mCloseGuard != null) {
157             mCloseGuard.warnIfOpen();
158         }
159     }
160 
checkTunnelFeatureOrThrow(Context context)161     private void checkTunnelFeatureOrThrow(Context context) {
162         // TODO(b/157754168): Also check if OP_MANAGE_IPSEC_TUNNELS is granted when it is exposed
163         if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
164             throw new IllegalStateException(
165                     "Cannot set up tunnel mode Child SA due to FEATURE_IPSEC_TUNNELS missing");
166         }
167     }
168 
169     /** Initialization-on-demand holder */
170     private static class IkeThreadHolder {
171         static final HandlerThread IKE_WORKER_THREAD;
172 
173         static {
174             IKE_WORKER_THREAD = new HandlerThread("IkeWorkerThread");
IKE_WORKER_THREAD.start()175             IKE_WORKER_THREAD.start();
176         }
177     }
178 
179     // TODO: b/133340675 Destroy the worker thread when there is no more alive {@link IkeSession}.
180 
181     /**
182      * Request a new Child Session.
183      *
184      * <p>Users MUST provide a unique {@link ChildSessionCallback} instance for each new Child
185      * Session.
186      *
187      * <p>Upon setup, {@link ChildSessionCallback#onOpened(ChildSessionConfiguration)} will be
188      * fired.
189      *
190      * <p>FEATURE_IPSEC_TUNNELS is required for setting up a tunnel mode Child SA.
191      *
192      * @param childSessionParams the {@link ChildSessionParams} that contains the Child Session
193      *     configurations to negotiate.
194      * @param childSessionCallback the {@link ChildSessionCallback} interface to notify users the
195      *     state changes of the Child Session. It will be posted to the callback {@link Executor} of
196      *     this {@link IkeSession}.
197      * @throws IllegalArgumentException if the ChildSessionCallback is already in use.
198      */
199     // The childSessionCallback will be called on the same executor as was passed in the constructor
200     // for security reasons.
201     @SuppressLint("ExecutorRegistration")
openChildSession( @onNull ChildSessionParams childSessionParams, @NonNull ChildSessionCallback childSessionCallback)202     public void openChildSession(
203             @NonNull ChildSessionParams childSessionParams,
204             @NonNull ChildSessionCallback childSessionCallback) {
205         if (childSessionParams instanceof TunnelModeChildSessionParams) {
206             checkTunnelFeatureOrThrow(mContext);
207         }
208 
209         mIkeSessionStateMachine.openChildSession(childSessionParams, childSessionCallback);
210     }
211 
212     /**
213      * Delete a Child Session.
214      *
215      * <p>Upon closure, {@link ChildSessionCallback#onClosed()} will be fired.
216      *
217      * @param childSessionCallback The {@link ChildSessionCallback} instance that uniquely identify
218      *     the Child Session.
219      * @throws IllegalArgumentException if no Child Session found bound with this callback.
220      */
221     // The childSessionCallback will be called on the same executor as was passed in the constructor
222     // for security reasons.
223     @SuppressLint("ExecutorRegistration")
closeChildSession(@onNull ChildSessionCallback childSessionCallback)224     public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
225         mIkeSessionStateMachine.closeChildSession(childSessionCallback);
226     }
227 
228     /**
229      * Close the IKE session gracefully.
230      *
231      * <p>Implements {@link AutoCloseable#close()}
232      *
233      * <p>Upon closure, {@link IkeSessionCallback#onClosed()} or {@link
234      * IkeSessionCallback#onClosedExceptionally()} will be fired.
235      *
236      * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it.
237      * Users SHOULD stop all outbound traffic that uses these Child Sessions({@link IpSecTransform}
238      * pairs) before calling this method. Otherwise IPsec packets will be dropped due to the lack of
239      * a valid {@link IpSecTransform}.
240      *
241      * <p>Closure of an IKE session will take priority over, and cancel other procedures waiting in
242      * the queue (but will wait for ongoing locally initiated procedures to complete). After sending
243      * the Delete request, the IKE library will wait until a Delete response is received or
244      * retransmission timeout occurs.
245      */
246     @Override
close()247     public void close() {
248         mCloseGuard.close();
249         mIkeSessionStateMachine.closeSession();
250     }
251 
252     /**
253      * Terminate (forcibly close) the IKE session.
254      *
255      * <p>Upon closing, {@link IkeSessionCallback#onClosed()} will be fired.
256      *
257      * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it.
258      * Users SHOULD stop all outbound traffic that uses these Child Sessions({@link IpSecTransform}
259      * pairs) before calling this method. Otherwise IPsec packets will be dropped due to the lack of
260      * a valid {@link IpSecTransform}.
261      *
262      * <p>Forcible closure of an IKE session will take priority over, and cancel other procedures
263      * waiting in the queue. It will also interrupt any ongoing locally initiated procedure.
264      */
kill()265     public void kill() {
266         mCloseGuard.close();
267         mIkeSessionStateMachine.killSession();
268     }
269 }
270