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