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