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 com.android.internal.net.ipsec.ike; 17 18 import static android.net.ipsec.ike.IkeManager.getIkeLog; 19 import static android.net.ipsec.ike.SaProposal.DH_GROUP_NONE; 20 import static android.net.ipsec.ike.exceptions.IkeException.wrapAsIkeException; 21 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; 22 23 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.BUNDLE_KEY_CHILD_REMOTE_SPI; 24 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_ALARM_FIRED; 25 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA; 26 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_IKE_AUTH; 27 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; 28 import static com.android.internal.net.ipsec.ike.message.IkeHeader.ExchangeType; 29 import static com.android.internal.net.ipsec.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; 30 import static com.android.internal.net.ipsec.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; 31 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA; 32 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE; 33 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP; 34 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE; 35 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_KE; 36 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NONCE; 37 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; 38 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA; 39 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR; 40 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER; 41 import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_ESP; 42 import static com.android.internal.net.ipsec.ike.utils.IkeAlarm.IkeAlarmConfig; 43 import static com.android.internal.net.ipsec.ike.utils.IkeAlarm.buildIkeAlarmIntent; 44 import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD; 45 import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_REKEY_CHILD; 46 47 import android.annotation.IntDef; 48 import android.annotation.Nullable; 49 import android.app.PendingIntent; 50 import android.net.IpSecManager; 51 import android.net.IpSecManager.ResourceUnavailableException; 52 import android.net.IpSecManager.SecurityParameterIndex; 53 import android.net.IpSecManager.SpiUnavailableException; 54 import android.net.IpSecManager.UdpEncapsulationSocket; 55 import android.net.IpSecTransform; 56 import android.net.ipsec.ike.ChildSaProposal; 57 import android.net.ipsec.ike.ChildSessionCallback; 58 import android.net.ipsec.ike.ChildSessionConfiguration; 59 import android.net.ipsec.ike.ChildSessionParams; 60 import android.net.ipsec.ike.IkeTrafficSelector; 61 import android.net.ipsec.ike.SaProposal; 62 import android.net.ipsec.ike.TunnelModeChildSessionParams; 63 import android.net.ipsec.ike.exceptions.IkeException; 64 import android.net.ipsec.ike.exceptions.IkeProtocolException; 65 import android.net.ipsec.ike.exceptions.InvalidKeException; 66 import android.net.ipsec.ike.exceptions.InvalidSyntaxException; 67 import android.net.ipsec.ike.exceptions.NoValidProposalChosenException; 68 import android.net.ipsec.ike.exceptions.TemporaryFailureException; 69 import android.net.ipsec.ike.exceptions.TsUnacceptableException; 70 import android.os.Bundle; 71 import android.os.Handler; 72 import android.os.Message; 73 import android.util.Pair; 74 import android.util.SparseArray; 75 76 import com.android.internal.annotations.VisibleForTesting; 77 import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; 78 import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequestFactory; 79 import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord; 80 import com.android.internal.net.ipsec.ike.SaRecord.SaLifetimeAlarmScheduler; 81 import com.android.internal.net.ipsec.ike.crypto.IkeCipher; 82 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; 83 import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; 84 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; 85 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; 86 import com.android.internal.net.ipsec.ike.message.IkeDeletePayload; 87 import com.android.internal.net.ipsec.ike.message.IkeKePayload; 88 import com.android.internal.net.ipsec.ike.message.IkeMessage.IkeExchangeSubType; 89 import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; 90 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; 91 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NotifyType; 92 import com.android.internal.net.ipsec.ike.message.IkePayload; 93 import com.android.internal.net.ipsec.ike.message.IkeSaPayload; 94 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.ChildProposal; 95 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; 96 import com.android.internal.net.ipsec.ike.message.IkeTsPayload; 97 import com.android.internal.net.ipsec.ike.shim.ShimUtils; 98 import com.android.internal.net.ipsec.ike.utils.IkeMetrics; 99 import com.android.internal.net.ipsec.ike.utils.IpSecSpiGenerator; 100 import com.android.internal.net.ipsec.ike.utils.RandomnessFactory; 101 import com.android.internal.util.State; 102 103 import java.io.IOException; 104 import java.lang.annotation.Retention; 105 import java.lang.annotation.RetentionPolicy; 106 import java.net.InetAddress; 107 import java.security.GeneralSecurityException; 108 import java.util.ArrayList; 109 import java.util.Arrays; 110 import java.util.LinkedHashSet; 111 import java.util.LinkedList; 112 import java.util.List; 113 import java.util.Set; 114 import java.util.concurrent.Executor; 115 116 /** 117 * ChildSessionStateMachine tracks states and manages exchanges of this Child Session. 118 * 119 * <p>ChildSessionStateMachine has two types of states. One type are states where there is no 120 * ongoing procedure affecting Child Session (non-procedure state), including Initial, Idle and 121 * Receiving. All other states are "procedure" states which are named as follows: 122 * 123 * <pre> 124 * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type]. 125 * - An IKE procedure consists of one or two IKE exchanges: 126 * Procedure Type = {CreateChild | DeleteChild | Info | RekeyChild | SimulRekeyChild}. 127 * - Exchange Initiator indicates whether local or remote peer is the exchange initiator: 128 * Exchange Initiator = {Local | Remote} 129 * - Exchange type defines the function of this exchange. 130 * Exchange Type = {Create | Delete} 131 * </pre> 132 */ 133 public class ChildSessionStateMachine extends AbstractSessionStateMachine { 134 private static final String TAG = "ChildSessionStateMachine"; 135 136 private static final int SPI_NOT_REGISTERED = 0; 137 138 private static final int CMD_GENERAL_BASE = CMD_PRIVATE_BASE; 139 140 /** Receive request for negotiating first Child SA. */ 141 private static final int CMD_HANDLE_FIRST_CHILD_EXCHANGE = CMD_GENERAL_BASE + 1; 142 /** Receive a request from the remote. */ 143 private static final int CMD_HANDLE_RECEIVED_REQUEST = CMD_GENERAL_BASE + 2; 144 /** Receive a reponse from the remote. */ 145 private static final int CMD_HANDLE_RECEIVED_RESPONSE = CMD_GENERAL_BASE + 3; 146 147 private static final SparseArray<String> CMD_TO_STR; 148 149 static { 150 CMD_TO_STR = new SparseArray<>(); CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_EXCHANGE, "Handle First Child")151 CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_EXCHANGE, "Handle First Child"); CMD_TO_STR.put(CMD_HANDLE_RECEIVED_REQUEST, "Rcv request")152 CMD_TO_STR.put(CMD_HANDLE_RECEIVED_REQUEST, "Rcv request"); CMD_TO_STR.put(CMD_HANDLE_RECEIVED_RESPONSE, "Rcv response")153 CMD_TO_STR.put(CMD_HANDLE_RECEIVED_RESPONSE, "Rcv response"); 154 } 155 156 private final int mIkeSessionId; 157 private final Handler mIkeHandler; 158 private final IpSecManager mIpSecManager; 159 160 /** 161 * mIpSecSpiGenerator will be used by all Child SA creations in this Child Session to avoid SPI 162 * collision in test mode. 163 */ 164 private final IpSecSpiGenerator mIpSecSpiGenerator; 165 166 private final LocalRequestFactory mLocalRequestFactory = new LocalRequestFactory(); 167 168 /** User provided configurations. */ 169 @VisibleForTesting final ChildSessionParams mChildSessionParams; 170 171 private final ChildSessionCallback mUserCallback; 172 173 /** Callback to notify IKE Session the state changes. */ 174 private final IChildSessionSmCallback mChildSmCallback; 175 176 // TODO: Also store ChildSessionCallback for notifying users. 177 178 /** Local address assigned on device. */ 179 @VisibleForTesting InetAddress mLocalAddress; 180 /** Remote address configured by users. */ 181 @VisibleForTesting InetAddress mRemoteAddress; 182 183 /** 184 * UDP-Encapsulated socket that allows IPsec traffic to pass through a NAT. Null if UDP 185 * encapsulation is not needed. 186 */ 187 @VisibleForTesting @Nullable UdpEncapsulationSocket mUdpEncapSocket; 188 189 /** Crypto parameters. Updated upon initial negotiation or IKE SA rekey. */ 190 @VisibleForTesting IkeMacPrf mIkePrf; 191 192 @VisibleForTesting byte[] mSkD; 193 194 /** 195 * Negotiated IKE DH group 196 * 197 * <p>First Child SA, and all additional Child SAs that do not have user specified DH group are 198 * set up with crypto keys that are implicitly generated by the negotiated IKE DH group. For 199 * those Child SAs, incoming rekey requests that match the negotiated IKE DH group should also 200 * be acceptable. This for improving the interoperability with other IKE implementations. 201 */ 202 @VisibleForTesting int mIkeDhGroup; 203 204 /** Package private ChildSaProposal that represents the negotiated Child SA proposal. */ 205 @VisibleForTesting ChildSaProposal mSaProposal; 206 207 /** Negotiated local Traffic Selector. */ 208 @VisibleForTesting IkeTrafficSelector[] mLocalTs; 209 /** Negotiated remote Traffic Selector. */ 210 @VisibleForTesting IkeTrafficSelector[] mRemoteTs; 211 212 @VisibleForTesting IkeCipher mChildCipher; 213 @VisibleForTesting IkeMacIntegrity mChildIntegrity; 214 215 /** Package private */ 216 @VisibleForTesting ChildSaRecord mCurrentChildSaRecord; 217 /** Package private */ 218 @VisibleForTesting ChildSaRecord mLocalInitNewChildSaRecord; 219 /** Package private */ 220 @VisibleForTesting ChildSaRecord mRemoteInitNewChildSaRecord; 221 222 /** Package private */ 223 @VisibleForTesting ChildSaRecord mChildSaRecordSurviving; 224 225 @VisibleForTesting final State mKillChildSessionParent = new KillChildSessionParent(); 226 227 @VisibleForTesting final State mInitial = new Initial(); 228 @VisibleForTesting final State mCreateChildLocalCreate = new CreateChildLocalCreate(); 229 @VisibleForTesting final State mIdle = new Idle(); 230 @VisibleForTesting final State mIdleWithDeferredRequest = new IdleWithDeferredRequest(); 231 @VisibleForTesting final State mClosedAndAwaitResponse = new ClosedAndAwaitResponse(); 232 @VisibleForTesting final State mDeleteChildLocalDelete = new DeleteChildLocalDelete(); 233 @VisibleForTesting final State mDeleteChildRemoteDelete = new DeleteChildRemoteDelete(); 234 @VisibleForTesting final State mRekeyChildLocalCreate = new RekeyChildLocalCreate(); 235 @VisibleForTesting final State mMobikeRekeyChildLocalCreate = new MobikeRekeyChildLocalCreate(); 236 @VisibleForTesting final State mRekeyChildRemoteCreate = new RekeyChildRemoteCreate(); 237 @VisibleForTesting final State mRekeyChildLocalDelete = new RekeyChildLocalDelete(); 238 @VisibleForTesting final State mRekeyChildRemoteDelete = new RekeyChildRemoteDelete(); 239 @VisibleForTesting boolean mIsFirstChild; 240 241 /** 242 * Builds a new uninitialized ChildSessionStateMachine 243 * 244 * <p>Upon creation, this state machine will await either the handleFirstChildExchange 245 * (IKE_AUTH), or the createChildSession (Additional child creation beyond the first child) to 246 * be called, both of which must pass keying and SA information. 247 * 248 * <p>This two-stage initialization is required to allow race-free user interaction with the IKE 249 * Session keyed on the child state machine callbacks. 250 * 251 * <p>Package private 252 */ ChildSessionStateMachine( IkeContext ikeContext, ChildSessionStateMachine.Config childSmConfig, ChildSessionCallback userCallback, IChildSessionSmCallback childSmCallback)253 ChildSessionStateMachine( 254 IkeContext ikeContext, 255 ChildSessionStateMachine.Config childSmConfig, 256 ChildSessionCallback userCallback, 257 IChildSessionSmCallback childSmCallback) { 258 super(TAG, ikeContext, childSmConfig.userCbExecutor); 259 260 mIkeSessionId = childSmConfig.ikeSessionId; 261 mIkeHandler = childSmConfig.ikeHandler; 262 mIpSecManager = childSmConfig.ipSecManager; 263 mIpSecSpiGenerator = childSmConfig.ipSecSpiGenerator; 264 265 mChildSessionParams = childSmConfig.sessionParams; 266 mUserCallback = userCallback; 267 mChildSmCallback = childSmCallback; 268 269 addState(mKillChildSessionParent); 270 271 addState(mInitial, mKillChildSessionParent); 272 addState(mCreateChildLocalCreate, mKillChildSessionParent); 273 addState(mIdle, mKillChildSessionParent); 274 addState(mIdleWithDeferredRequest, mKillChildSessionParent); 275 addState(mClosedAndAwaitResponse, mKillChildSessionParent); 276 addState(mDeleteChildLocalDelete, mKillChildSessionParent); 277 addState(mDeleteChildRemoteDelete, mKillChildSessionParent); 278 addState(mRekeyChildLocalCreate, mKillChildSessionParent); 279 addState(mMobikeRekeyChildLocalCreate, mKillChildSessionParent); 280 addState(mRekeyChildRemoteCreate, mKillChildSessionParent); 281 addState(mRekeyChildLocalDelete, mKillChildSessionParent); 282 addState(mRekeyChildRemoteDelete, mKillChildSessionParent); 283 284 setInitialState(mInitial); 285 } 286 287 // Configurations provided by an IKE Session for building the Child Session 288 static class Config { 289 public final int ikeSessionId; 290 public final Handler ikeHandler; 291 public final ChildSessionParams sessionParams; 292 public final IpSecManager ipSecManager; 293 public final IpSecSpiGenerator ipSecSpiGenerator; 294 public final Executor userCbExecutor; 295 Config( int ikeSessionId, Handler ikeHandler, ChildSessionParams sessionParams, IpSecManager ipSecManager, IpSecSpiGenerator ipSecSpiGenerator, Executor userCbExecutor)296 Config( 297 int ikeSessionId, 298 Handler ikeHandler, 299 ChildSessionParams sessionParams, 300 IpSecManager ipSecManager, 301 IpSecSpiGenerator ipSecSpiGenerator, 302 Executor userCbExecutor) { 303 this.ikeSessionId = ikeSessionId; 304 this.ikeHandler = ikeHandler; 305 this.sessionParams = sessionParams; 306 this.ipSecManager = ipSecManager; 307 this.ipSecSpiGenerator = ipSecSpiGenerator; 308 this.userCbExecutor = userCbExecutor; 309 } 310 } 311 312 /** 313 * Interface for ChildSessionStateMachine to notify IkeSessionStateMachine of state changes. 314 */ 315 interface IChildSessionSmCallback { 316 /** Notify that new Child SA is created. */ onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession)317 void onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession); 318 319 /** Notify that a Child SA is deleted. */ onChildSaDeleted(int remoteSpi)320 void onChildSaDeleted(int remoteSpi); 321 322 /** Schedule retry for a Create Child Request on the LocalRequestScheduler. */ scheduleRetryLocalRequest(ChildLocalRequest futureRequest)323 void scheduleRetryLocalRequest(ChildLocalRequest futureRequest); 324 325 /** Notify the IKE Session to send out IKE message for this Child Session. */ onOutboundPayloadsReady( @xchangeType int exchangeType, boolean isResp, List<IkePayload> payloadList, ChildSessionStateMachine childSession)326 void onOutboundPayloadsReady( 327 @ExchangeType int exchangeType, 328 boolean isResp, 329 List<IkePayload> payloadList, 330 ChildSessionStateMachine childSession); 331 332 /** Notify that a Child procedure has been finished. */ onProcedureFinished(ChildSessionStateMachine childSession)333 void onProcedureFinished(ChildSessionStateMachine childSession); 334 335 /** 336 * Notify the IKE Session State Machine that this Child has been fully shut down. 337 * 338 * <p>This method MUST be called after the user callbacks have been fired, and MUST always 339 * be called before the state machine can shut down. 340 */ onChildSessionClosed(ChildSessionCallback userCallbacks)341 void onChildSessionClosed(ChildSessionCallback userCallbacks); 342 343 /** 344 * Notify that a Child procedure has been finished and the IKE Session should close itself 345 * because of a fatal error. 346 * 347 * <p>Child Session may encounter an IKE Session fatal error in three cases with different 348 * handling rules: 349 * 350 * <pre> 351 * - When there is a fatal error in an inbound request, onOutboundPayloadsReady will be 352 * called first to send out an error notification, and then onFatalIkeSessionError() will 353 * be called to locally close the IKE Session. 354 * - When there is a fatal error in an inbound response, onOutboundPayloadsReady will be 355 * called first to send out an IKE Delete message, and then onFatalIkeSessionError() will 356 * be called to locally close the IKE Session. 357 * - When there is a fatal error notification in an inbound response, 358 * onFatalIkeSessionError() will be called to locally close the IKE Session" 359 * </pre> 360 */ onFatalIkeSessionError(Exception exception)361 void onFatalIkeSessionError(Exception exception); 362 } 363 364 /** 365 * Receive requesting and responding payloads for negotiating first Child SA. 366 * 367 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 368 * as an asynchronous job to the ChildStateMachine handler. 369 * 370 * @param reqPayloads SA negotiation related payloads in IKE_AUTH request. 371 * @param respPayloads SA negotiation related payloads in IKE_AUTH response. 372 * @param localAddress The local (outer) address of the Child Session. 373 * @param remoteAddress The remote (outer) address of the Child Session. 374 * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. 375 * @param ikePrf The pseudo-random function to use for key derivation 376 * @param ikeDh The negotiated IKE DH group 377 * @param skD The key for which to derive new keying information from. 378 */ handleFirstChildExchange( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket, IkeMacPrf ikePrf, int ikeDh, byte[] skD)379 public void handleFirstChildExchange( 380 List<IkePayload> reqPayloads, 381 List<IkePayload> respPayloads, 382 InetAddress localAddress, 383 InetAddress remoteAddress, 384 UdpEncapsulationSocket udpEncapSocket, 385 IkeMacPrf ikePrf, 386 int ikeDh, 387 byte[] skD) { 388 389 this.mLocalAddress = localAddress; 390 this.mRemoteAddress = remoteAddress; 391 this.mUdpEncapSocket = udpEncapSocket; 392 this.mIkePrf = ikePrf; 393 this.mIkeDhGroup = ikeDh; 394 this.mSkD = skD; 395 mIsFirstChild = true; 396 397 int spi = registerProvisionalChildAndGetSpi(respPayloads); 398 sendMessage( 399 CMD_HANDLE_FIRST_CHILD_EXCHANGE, 400 new FirstChildNegotiationData(reqPayloads, respPayloads, spi)); 401 } 402 403 /** 404 * Initiate Create Child procedure. 405 * 406 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 407 * as an asynchronous job to the ChildStateMachine handler. 408 * 409 * @param localAddress The local (outer) address from which traffic will originate. 410 * @param remoteAddress The remote (outer) address to which traffic will be sent. 411 * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. 412 * @param ikePrf The pseudo-random function to use for key derivation 413 * @param ikeDh The negotiated IKE DH group 414 * @param skD The key for which to derive new keying information from. 415 */ createChildSession( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket, IkeMacPrf ikePrf, int ikeDh, byte[] skD)416 public void createChildSession( 417 InetAddress localAddress, 418 InetAddress remoteAddress, 419 UdpEncapsulationSocket udpEncapSocket, 420 IkeMacPrf ikePrf, 421 int ikeDh, 422 byte[] skD) { 423 this.mLocalAddress = localAddress; 424 this.mRemoteAddress = remoteAddress; 425 this.mUdpEncapSocket = udpEncapSocket; 426 this.mIkePrf = ikePrf; 427 this.mIkeDhGroup = ikeDh; 428 this.mSkD = skD; 429 mIsFirstChild = false; 430 431 sendMessage(CMD_LOCAL_REQUEST_CREATE_CHILD); 432 } 433 434 /** 435 * Initiate Delete Child procedure. 436 * 437 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 438 * as an asynchronous job to the ChildStateMachine handler. 439 */ deleteChildSession()440 public void deleteChildSession() { 441 sendMessage(CMD_LOCAL_REQUEST_DELETE_CHILD); 442 } 443 444 /** 445 * Initiate Rekey Child procedure. 446 * 447 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 448 * as an asynchronous job to the ChildStateMachine handler. 449 */ rekeyChildSession()450 public void rekeyChildSession() { 451 sendMessage(CMD_LOCAL_REQUEST_REKEY_CHILD); 452 } 453 454 /** 455 * Update IPsec SAs via MOBIKE. 456 * 457 * <p>This method is called synchronously from IkeStateMachine, and may complete synchronously 458 * if kernel MOBIKE can be used. Otherwise, it will fall back to using rekeys to synchronize 459 * IPsec state. 460 * 461 * @param localAddress The local (outer) address from which traffic will originate. 462 * @param remoteAddress The remote (outer) address to which traffic will be sent. 463 * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. 464 */ performMigration( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket)465 public void performMigration( 466 InetAddress localAddress, 467 InetAddress remoteAddress, 468 UdpEncapsulationSocket udpEncapSocket) { 469 470 final UdpEncapsulationSocket oldEncapSocket = mUdpEncapSocket; 471 472 this.mLocalAddress = localAddress; 473 this.mRemoteAddress = remoteAddress; 474 this.mUdpEncapSocket = udpEncapSocket; 475 476 if (oldEncapSocket == mUdpEncapSocket 477 && ShimUtils.getInstance() 478 .supportsSameSocketKernelMigration(mIkeContext.getContext())) { 479 mIpSecManager.startTunnelModeTransformMigration( 480 mCurrentChildSaRecord.getInboundIpSecTransform(), 481 mRemoteAddress, 482 mLocalAddress); 483 mIpSecManager.startTunnelModeTransformMigration( 484 mCurrentChildSaRecord.getOutboundIpSecTransform(), 485 mLocalAddress, 486 mRemoteAddress); 487 executeUserCallback(() -> { 488 mUserCallback.onIpSecTransformsMigrated( 489 mCurrentChildSaRecord.getInboundIpSecTransform(), 490 mCurrentChildSaRecord.getOutboundIpSecTransform()); 491 }); 492 493 mChildSmCallback.onProcedureFinished(ChildSessionStateMachine.this); 494 } else { 495 performRekeyMigration(localAddress, remoteAddress, udpEncapSocket); 496 } 497 } 498 499 /** 500 * Initiate Rekey Child procedure for MOBIKE (instead of migrating IPsec SAs). 501 * 502 * <p>This method should only be used as a fallback mode for devices that do not have 503 * XFRM_MIGRATE kernel support. 504 * 505 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 506 * as an asynchronous job to the ChildStateMachine handler. 507 * 508 * <p>This method works similarly to {@link #rekeyChildSession()} in that it rekeys the Child 509 * SAs associated with this state machine. However, the caller is notified of Child SA creation 510 * via {@link ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform, 511 * android.net.IpSecTransform)}; 512 * 513 * @param localAddress The local (outer) address from which traffic will originate. 514 * @param remoteAddress The remote (outer) address to which traffic will be sent. 515 * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. 516 */ performRekeyMigration( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket)517 public void performRekeyMigration( 518 InetAddress localAddress, 519 InetAddress remoteAddress, 520 UdpEncapsulationSocket udpEncapSocket) { 521 522 this.mLocalAddress = localAddress; 523 this.mRemoteAddress = remoteAddress; 524 this.mUdpEncapSocket = udpEncapSocket; 525 526 sendMessage(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE); 527 } 528 529 /** 530 * Receive a request 531 * 532 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 533 * as an asynchronous job to the ChildStateMachine handler. 534 * 535 * @param exchangeSubtype the exchange subtype of this inbound request. 536 * @param exchangeType the exchange type in the request message. 537 * @param payloadList the Child-procedure-related payload list in the request message that needs 538 * validation. 539 */ receiveRequest( @keExchangeSubType int exchangeSubtype, @ExchangeType int exchangeType, List<IkePayload> payloadList)540 public void receiveRequest( 541 @IkeExchangeSubType int exchangeSubtype, 542 @ExchangeType int exchangeType, 543 List<IkePayload> payloadList) { 544 sendMessage( 545 CMD_HANDLE_RECEIVED_REQUEST, 546 new ReceivedRequest(exchangeSubtype, exchangeType, payloadList)); 547 } 548 549 /** 550 * Receive a response. 551 * 552 * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call 553 * as an asynchronous job to the ChildStateMachine handler. 554 * 555 * @param exchangeType the exchange type in the response message that needs validation. 556 * @param payloadList the Child-procedure-related payload list in the response message that 557 * needs validation. 558 */ receiveResponse(@xchangeType int exchangeType, List<IkePayload> payloadList)559 public void receiveResponse(@ExchangeType int exchangeType, List<IkePayload> payloadList) { 560 if (!isAwaitingCreateResp()) { 561 sendMessage( 562 CMD_HANDLE_RECEIVED_RESPONSE, new ReceivedResponse(exchangeType, payloadList)); 563 } 564 565 // If we are waiting for a Create/RekeyCreate response and the received message contains SA 566 // payload we need to register for this provisional Child. 567 int spi = registerProvisionalChildAndGetSpi(payloadList); 568 sendMessage( 569 CMD_HANDLE_RECEIVED_RESPONSE, 570 new ReceivedCreateResponse(exchangeType, payloadList, spi)); 571 } 572 isAwaitingCreateResp()573 private boolean isAwaitingCreateResp() { 574 return (getCurrentState() == mCreateChildLocalCreate 575 || getCurrentState() == mMobikeRekeyChildLocalCreate 576 || getCurrentState() == mRekeyChildLocalCreate); 577 } 578 579 /** 580 * Update SK_d with provided value when IKE SA is rekeyed. 581 * 582 * <p>It MUST be only called at the end of Rekey IKE procedure, which guarantees this Child 583 * Session is not in Create Child or Rekey Child procedure. 584 * 585 * @param skD the new skD in byte array. 586 */ setSkD(byte[] skD)587 public void setSkD(byte[] skD) { 588 mSkD = skD; 589 } 590 591 /** 592 * Register provisioning ChildSessionStateMachine in IChildSessionSmCallback 593 * 594 * <p>This method is for avoiding CHILD_SA_NOT_FOUND error in IkeSessionStateMachine when remote 595 * peer sends request for delete/rekey this Child SA before ChildSessionStateMachine sends 596 * FirstChildNegotiationData or Create response to itself. 597 */ registerProvisionalChildAndGetSpi(List<IkePayload> respPayloads)598 private int registerProvisionalChildAndGetSpi(List<IkePayload> respPayloads) { 599 IkeSaPayload saPayload = 600 IkePayload.getPayloadForTypeInProvidedList( 601 PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads); 602 603 if (saPayload == null) return SPI_NOT_REGISTERED; 604 605 // IkeSaPayload.Proposal stores SPI in long type so as to be applied to both 8-byte IKE SPI 606 // and 4-byte Child SPI. Here we cast the stored SPI to int to represent a Child SPI. 607 int remoteGenSpi = (int) (saPayload.proposalList.get(0).spi); 608 mChildSmCallback.onChildSaCreated(remoteGenSpi, this); 609 return remoteGenSpi; 610 } 611 replyErrorNotification(@otifyType int notifyType)612 private void replyErrorNotification(@NotifyType int notifyType) { 613 replyErrorNotification(notifyType, new byte[0]); 614 } 615 replyErrorNotification(@otifyType int notifyType, byte[] notifyData)616 private void replyErrorNotification(@NotifyType int notifyType, byte[] notifyData) { 617 List<IkePayload> outPayloads = new ArrayList<>(1); 618 IkeNotifyPayload notifyPayload = new IkeNotifyPayload(notifyType, notifyData); 619 outPayloads.add(notifyPayload); 620 621 mChildSmCallback.onOutboundPayloadsReady( 622 EXCHANGE_TYPE_INFORMATIONAL, true /*isResp*/, outPayloads, this); 623 } 624 sendDeleteIkeRequest()625 private void sendDeleteIkeRequest() { 626 List<IkePayload> outIkePayloads = new ArrayList<>(1); 627 outIkePayloads.add(new IkeDeletePayload()); 628 629 mChildSmCallback.onOutboundPayloadsReady( 630 EXCHANGE_TYPE_INFORMATIONAL, 631 false /*isResp*/, 632 outIkePayloads, 633 ChildSessionStateMachine.this); 634 } 635 636 class OnIpSecSaPairCreatedRunnable implements Runnable { 637 private final IpSecTransform mOut; 638 private final IpSecTransform mIn; 639 OnIpSecSaPairCreatedRunnable(ChildSaRecord childSaRecord)640 OnIpSecSaPairCreatedRunnable(ChildSaRecord childSaRecord) { 641 mOut = childSaRecord.getOutboundIpSecTransform(); 642 mIn = childSaRecord.getInboundIpSecTransform(); 643 } 644 645 @Override run()646 public void run() { 647 mUserCallback.onIpSecTransformCreated(mOut, IpSecManager.DIRECTION_OUT); 648 mUserCallback.onIpSecTransformCreated(mIn, IpSecManager.DIRECTION_IN); 649 } 650 } 651 652 class OnIpSecSaPairDeletedRunnable implements Runnable { 653 private final IpSecTransform mOut; 654 private final IpSecTransform mIn; 655 OnIpSecSaPairDeletedRunnable(ChildSaRecord childSaRecord)656 OnIpSecSaPairDeletedRunnable(ChildSaRecord childSaRecord) { 657 mOut = childSaRecord.getOutboundIpSecTransform(); 658 mIn = childSaRecord.getInboundIpSecTransform(); 659 } 660 661 @Override run()662 public void run() { 663 mUserCallback.onIpSecTransformDeleted(mOut, IpSecManager.DIRECTION_OUT); 664 mUserCallback.onIpSecTransformDeleted(mIn, IpSecManager.DIRECTION_IN); 665 } 666 } 667 668 /** 669 * ReceivedRequest contains exchange subtype and payloads that are extracted from a request 670 * message to the current Child procedure. 671 */ 672 private static class ReceivedRequest { 673 @IkeExchangeSubType public final int exchangeSubtype; 674 @ExchangeType public final int exchangeType; 675 public final List<IkePayload> requestPayloads; 676 ReceivedRequest( @keExchangeSubType int eSubtype, @ExchangeType int eType, List<IkePayload> reqPayloads)677 ReceivedRequest( 678 @IkeExchangeSubType int eSubtype, 679 @ExchangeType int eType, 680 List<IkePayload> reqPayloads) { 681 exchangeSubtype = eSubtype; 682 exchangeType = eType; 683 requestPayloads = reqPayloads; 684 } 685 } 686 687 /** 688 * ReceivedResponse contains exchange type and payloads that are extracted from a response 689 * message to the current Child procedure. 690 */ 691 private static class ReceivedResponse { 692 @ExchangeType public final int exchangeType; 693 public final List<IkePayload> responsePayloads; 694 ReceivedResponse(@xchangeType int eType, List<IkePayload> respPayloads)695 ReceivedResponse(@ExchangeType int eType, List<IkePayload> respPayloads) { 696 exchangeType = eType; 697 responsePayloads = respPayloads; 698 } 699 } 700 701 private static class ReceivedCreateResponse extends ReceivedResponse { 702 public final int registeredSpi; 703 ReceivedCreateResponse(@xchangeType int eType, List<IkePayload> respPayloads, int spi)704 ReceivedCreateResponse(@ExchangeType int eType, List<IkePayload> respPayloads, int spi) { 705 super(eType, respPayloads); 706 registeredSpi = spi; 707 } 708 } 709 710 /** 711 * FirstChildNegotiationData contains payloads for negotiating first Child SA in IKE_AUTH 712 * request and IKE_AUTH response and callback to notify IkeSessionStateMachine the SA 713 * negotiation result. 714 */ 715 private static class FirstChildNegotiationData extends ReceivedCreateResponse { 716 public final List<IkePayload> requestPayloads; 717 FirstChildNegotiationData( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int spi)718 FirstChildNegotiationData( 719 List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int spi) { 720 super(EXCHANGE_TYPE_IKE_AUTH, respPayloads, spi); 721 requestPayloads = reqPayloads; 722 } 723 } 724 725 /** Top level state for handling uncaught exceptions for all subclasses. */ 726 abstract class ExceptionHandler extends ExceptionHandlerBase { 727 @Override cleanUpAndQuit(RuntimeException e)728 protected void cleanUpAndQuit(RuntimeException e) { 729 // TODO: b/140123526 Send a response if exception was caught when processing a request. 730 731 // Clean up all SaRecords. 732 closeAllSaRecords(false /*expectSaClosed*/); 733 734 executeUserCallback( 735 () -> { 736 mUserCallback.onClosedWithException(wrapAsIkeException(e)); 737 }); 738 739 recordMetricsEvent_sessionTerminated(wrapAsIkeException(e)); 740 logWtf("Unexpected exception in " + getCurrentStateName(), e); 741 quitSessionNow(); 742 } 743 744 @Override getCmdString(int cmd)745 protected String getCmdString(int cmd) { 746 return CMD_TO_STR.get(cmd); 747 } 748 } 749 750 /** Called when this StateMachine quits. */ 751 @Override onQuitting()752 protected void onQuitting() { 753 // Clean up all SaRecords. 754 closeAllSaRecords(true /*expectSaClosed*/); 755 756 mChildSmCallback.onProcedureFinished(this); 757 mChildSmCallback.onChildSessionClosed(mUserCallback); 758 } 759 closeAllSaRecords(boolean expectSaClosed)760 private void closeAllSaRecords(boolean expectSaClosed) { 761 closeChildSaRecord(mCurrentChildSaRecord, expectSaClosed); 762 closeChildSaRecord(mLocalInitNewChildSaRecord, expectSaClosed); 763 closeChildSaRecord(mRemoteInitNewChildSaRecord, expectSaClosed); 764 765 mCurrentChildSaRecord = null; 766 mLocalInitNewChildSaRecord = null; 767 mRemoteInitNewChildSaRecord = null; 768 } 769 closeChildSaRecord(ChildSaRecord childSaRecord, boolean expectSaClosed)770 private void closeChildSaRecord(ChildSaRecord childSaRecord, boolean expectSaClosed) { 771 if (childSaRecord == null) return; 772 773 OnIpSecSaPairDeletedRunnable delRunnable = new OnIpSecSaPairDeletedRunnable(childSaRecord); 774 executeUserCallback(delRunnable); 775 776 mChildSmCallback.onChildSaDeleted(childSaRecord.getRemoteSpi()); 777 childSaRecord.close(); 778 779 if (!expectSaClosed) return; 780 781 logWtf( 782 "ChildSaRecord with local SPI: " 783 + childSaRecord.getLocalSpi() 784 + " is not correctly closed."); 785 } 786 handleChildFatalError(Exception error)787 private void handleChildFatalError(Exception error) { 788 IkeException ikeException = wrapAsIkeException(error); 789 loge("Child Session fatal error", ikeException); 790 791 // Clean up all SaRecords and quit 792 closeAllSaRecords(false /*expectSaClosed*/); 793 executeUserCallback( 794 () -> { 795 mUserCallback.onClosedWithException(ikeException); 796 }); 797 798 recordMetricsEvent_sessionTerminated(ikeException); 799 quitSessionNow(); 800 } 801 802 /** 803 * This state handles the request to close Child Session immediately without initiating any 804 * exchange. 805 * 806 * <p>Request for closing Child Session immediately is usually caused by the closing of IKE 807 * Session. All states MUST be a child state of KillChildSessionParent to handle the closing 808 * request. 809 */ 810 private class KillChildSessionParent extends ExceptionHandler { 811 @Override processStateMessage(Message message)812 public boolean processStateMessage(Message message) { 813 switch (message.what) { 814 case CMD_KILL_SESSION: 815 closeAllSaRecords(false /*expectSaClosed*/); 816 executeUserCallback( 817 () -> { 818 mUserCallback.onClosed(); 819 }); 820 821 // ChildSessionTerminated Metrics not recorded; this is a result of the parent 822 // session tearing down. 823 quitSessionNow(); 824 return HANDLED; 825 default: 826 return NOT_HANDLED; 827 } 828 } 829 830 @Override getMetricsStateCode()831 protected @IkeMetrics.IkeState int getMetricsStateCode() { 832 return IkeMetrics.IKE_STATE_CHILD_KILL; 833 } 834 } 835 836 /** 837 * CreateChildLocalCreateBase represents the common information for a locally-initiated initial 838 * Child SA negotiation for setting up this Child Session. 839 */ 840 private abstract class CreateChildLocalCreateBase extends ExceptionHandler { validateAndBuildChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, int registeredSpi)841 protected void validateAndBuildChild( 842 List<IkePayload> reqPayloads, 843 List<IkePayload> respPayloads, 844 @ExchangeType int exchangeType, 845 @ExchangeType int expectedExchangeType, 846 int registeredSpi) { 847 validateAndBuildChild( 848 reqPayloads, 849 respPayloads, 850 registeredSpi, 851 CreateChildSaHelper.validateAndNegotiateInitChild( 852 reqPayloads, 853 respPayloads, 854 exchangeType, 855 expectedExchangeType, 856 mChildSessionParams.isTransportMode(), 857 mIpSecSpiGenerator, 858 mRemoteAddress)); 859 } 860 validateAndBuildChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int registeredSpi, CreateChildResult createChildResult)861 protected void validateAndBuildChild( 862 List<IkePayload> reqPayloads, 863 List<IkePayload> respPayloads, 864 int registeredSpi, 865 CreateChildResult createChildResult) { 866 switch (createChildResult.status) { 867 case CREATE_STATUS_OK: 868 try { 869 setUpNegotiatedResult(createChildResult); 870 871 mCurrentChildSaRecord = 872 ChildSaRecord.makeChildSaRecord( 873 mIkeContext.getContext(), 874 reqPayloads, 875 respPayloads, 876 createChildResult.initSpi, 877 createChildResult.respSpi, 878 mLocalAddress, 879 mRemoteAddress, 880 mUdpEncapSocket, 881 mIkePrf, 882 mChildIntegrity, 883 mChildCipher, 884 mSkD, 885 mChildSessionParams.isTransportMode(), 886 true /*isLocalInit*/, 887 buildSaLifetimeAlarmSched( 888 createChildResult.respSpi.getSpi())); 889 890 ChildSessionConfiguration sessionConfig = 891 buildChildSessionConfigFromResp(createChildResult, respPayloads); 892 893 OnIpSecSaPairCreatedRunnable createRunnable = 894 new OnIpSecSaPairCreatedRunnable(mCurrentChildSaRecord); 895 executeUserCallback( 896 () -> { 897 createRunnable.run(); 898 mUserCallback.onOpened(sessionConfig); 899 }); 900 901 List<Integer> integrityAlgorithms = mSaProposal.getIntegrityAlgorithms(); 902 List<Integer> dhGroups = mSaProposal.getDhGroups(); 903 904 recordMetricsEvent_SaNegotiation( 905 dhGroups.isEmpty() 906 ? IkeMetrics.DH_GROUP_UNSPECIFIED 907 : dhGroups.get(0), 908 mSaProposal.getEncryptionTransforms()[0].id, 909 mSaProposal.getEncryptionTransforms()[0].getSpecifiedKeyLength(), 910 integrityAlgorithms.isEmpty() 911 ? IkeMetrics.INTEGRITY_ALGORITHM_NONE 912 : integrityAlgorithms.get(0), 913 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED, 914 null); 915 916 transitionTo(mIdle); 917 } catch (GeneralSecurityException 918 | ResourceUnavailableException 919 | SpiUnavailableException 920 | IOException e) { 921 // #makeChildSaRecord failed. 922 923 // TODO: Initiate deletion 924 mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi()); 925 handleChildFatalError(e); 926 } finally { 927 // In the successful case the transform in ChildSaRecord has taken ownership 928 // of the SPI (in IpSecService), and will keep it alive. 929 createChildResult.initSpi.close(); 930 createChildResult.respSpi.close(); 931 } 932 break; 933 case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: 934 // TODO: Initiate deletion 935 handleCreationFailAndQuit(registeredSpi, createChildResult.exception); 936 break; 937 case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: 938 handleCreationFailAndQuit(registeredSpi, createChildResult.exception); 939 break; 940 default: 941 cleanUpAndQuit( 942 new IllegalStateException( 943 "Unrecognized status: " + createChildResult.status)); 944 } 945 } 946 setUpNegotiatedResult(CreateChildResult createChildResult)947 private void setUpNegotiatedResult(CreateChildResult createChildResult) { 948 // Build crypto tools using negotiated ChildSaProposal. It is ensured by {@link 949 // IkeSaPayload#getVerifiedNegotiatedChildProposalPair} that the negotiated 950 // ChildSaProposal is valid. The negotiated ChildSaProposal has exactly one encryption 951 // algorithm. When it has a combined-mode encryption algorithm, it either does not have 952 // integrity algorithm or only has one NONE value integrity algorithm. When the 953 // negotiated ChildSaProposal has a normal encryption algorithm, it either does not have 954 // integrity algorithm or has one integrity algorithm with any supported value. 955 956 mSaProposal = createChildResult.negotiatedProposal; 957 mChildCipher = IkeCipher.create(mSaProposal.getEncryptionTransforms()[0]); 958 if (mSaProposal.getIntegrityTransforms().length != 0 959 && mSaProposal.getIntegrityTransforms()[0].id 960 != SaProposal.INTEGRITY_ALGORITHM_NONE) { 961 mChildIntegrity = IkeMacIntegrity.create(mSaProposal.getIntegrityTransforms()[0]); 962 } 963 964 mLocalTs = createChildResult.initTs; 965 mRemoteTs = createChildResult.respTs; 966 } 967 buildChildSessionConfigFromResp( CreateChildResult createChildResult, List<IkePayload> respPayloads)968 private ChildSessionConfiguration buildChildSessionConfigFromResp( 969 CreateChildResult createChildResult, List<IkePayload> respPayloads) { 970 IkeConfigPayload configPayload = 971 IkePayload.getPayloadForTypeInProvidedList( 972 PAYLOAD_TYPE_CP, IkeConfigPayload.class, respPayloads); 973 974 if (mChildSessionParams.isTransportMode() 975 || configPayload == null 976 || configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) { 977 if (configPayload != null) { 978 logw("Unexpected config payload. Config Type: " + configPayload.configType); 979 } 980 981 return new ChildSessionConfiguration( 982 Arrays.asList(createChildResult.initTs), 983 Arrays.asList(createChildResult.respTs)); 984 } else { 985 return new ChildSessionConfiguration( 986 Arrays.asList(createChildResult.initTs), 987 Arrays.asList(createChildResult.respTs), 988 configPayload); 989 } 990 } 991 handleCreationFailAndQuit(int registeredSpi, IkeException exception)992 private void handleCreationFailAndQuit(int registeredSpi, IkeException exception) { 993 if (registeredSpi != SPI_NOT_REGISTERED) { 994 mChildSmCallback.onChildSaDeleted(registeredSpi); 995 } 996 handleChildFatalError(exception); 997 } 998 } 999 getIntentIdentifier(int remoteSpi)1000 private String getIntentIdentifier(int remoteSpi) { 1001 return IkeSessionStateMachine.TAG + "_" + mIkeSessionId + "_" + TAG + "_" + remoteSpi; 1002 } 1003 getIntentIkeSmMsg(int localRequestType, int remoteSpi)1004 private Message getIntentIkeSmMsg(int localRequestType, int remoteSpi) { 1005 Bundle spiBundle = new Bundle(); 1006 spiBundle.putInt(BUNDLE_KEY_CHILD_REMOTE_SPI, remoteSpi); 1007 1008 return mIkeHandler.obtainMessage( 1009 CMD_ALARM_FIRED, mIkeSessionId, localRequestType, spiBundle); 1010 } 1011 buildSaLifetimeAlarmSched(int remoteSpi)1012 private SaLifetimeAlarmScheduler buildSaLifetimeAlarmSched(int remoteSpi) { 1013 Message deleteMsg = getIntentIkeSmMsg(CMD_LOCAL_REQUEST_DELETE_CHILD, remoteSpi); 1014 Message rekeyMsg = getIntentIkeSmMsg(CMD_LOCAL_REQUEST_REKEY_CHILD, remoteSpi); 1015 1016 PendingIntent deleteSaIntent = 1017 buildIkeAlarmIntent( 1018 mIkeContext.getContext(), 1019 ACTION_DELETE_CHILD, 1020 getIntentIdentifier(remoteSpi), 1021 deleteMsg); 1022 PendingIntent rekeySaIntent = 1023 buildIkeAlarmIntent( 1024 mIkeContext.getContext(), 1025 ACTION_REKEY_CHILD, 1026 getIntentIdentifier(remoteSpi), 1027 rekeyMsg); 1028 1029 return new SaLifetimeAlarmScheduler( 1030 new IkeAlarmConfig( 1031 mIkeContext.getContext(), 1032 ACTION_DELETE_CHILD, 1033 mChildSessionParams.getHardLifetimeMsInternal(), 1034 deleteSaIntent, 1035 deleteMsg), 1036 new IkeAlarmConfig( 1037 mIkeContext.getContext(), 1038 ACTION_REKEY_CHILD, 1039 mChildSessionParams.getSoftLifetimeMsInternal(), 1040 rekeySaIntent, 1041 rekeyMsg)); 1042 } 1043 1044 /** Initial state of ChildSessionStateMachine. */ 1045 class Initial extends CreateChildLocalCreateBase { 1046 List<IkePayload> mRequestPayloads; 1047 1048 @Override processStateMessage(Message message)1049 public boolean processStateMessage(Message message) { 1050 switch (message.what) { 1051 case CMD_HANDLE_FIRST_CHILD_EXCHANGE: 1052 FirstChildNegotiationData childNegotiationData = 1053 (FirstChildNegotiationData) message.obj; 1054 mRequestPayloads = childNegotiationData.requestPayloads; 1055 List<IkePayload> respPayloads = childNegotiationData.responsePayloads; 1056 1057 // Negotiate Child SA. The exchangeType has been validated in 1058 // IkeSessionStateMachine. Won't validate it again here. 1059 validateAndBuildChild( 1060 mRequestPayloads, 1061 respPayloads, 1062 EXCHANGE_TYPE_IKE_AUTH, 1063 EXCHANGE_TYPE_IKE_AUTH, 1064 childNegotiationData.registeredSpi); 1065 1066 return HANDLED; 1067 case CMD_LOCAL_REQUEST_CREATE_CHILD: 1068 transitionTo(mCreateChildLocalCreate); 1069 return HANDLED; 1070 case CMD_LOCAL_REQUEST_DELETE_CHILD: 1071 // This may happen when creation has been rescheduled to be after deletion. 1072 executeUserCallback( 1073 () -> { 1074 mUserCallback.onClosed(); 1075 }); 1076 1077 recordMetricsEvent_sessionTerminated(null); 1078 quitSessionNow(); 1079 return HANDLED; 1080 case CMD_FORCE_TRANSITION: 1081 transitionTo((State) message.obj); 1082 return HANDLED; 1083 default: 1084 return NOT_HANDLED; 1085 } 1086 } 1087 1088 @Override exitState()1089 public void exitState() { 1090 CreateChildSaHelper.releaseSpiResources(mRequestPayloads); 1091 } 1092 1093 @Override getMetricsStateCode()1094 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1095 return IkeMetrics.IKE_STATE_CHILD_INITIAL; 1096 } 1097 } 1098 1099 /** 1100 * CreateChildLocalCreate represents the state where Child Session initiates the Create Child 1101 * exchange. 1102 */ 1103 class CreateChildLocalCreate extends CreateChildLocalCreateBase { 1104 private List<IkePayload> mRequestPayloads; 1105 1106 @Override enterState()1107 public void enterState() { 1108 try { 1109 mRequestPayloads = 1110 CreateChildSaHelper.getInitChildCreateReqPayloads( 1111 mIkeContext.getRandomnessFactory(), 1112 mIpSecSpiGenerator, 1113 mLocalAddress, 1114 mChildSessionParams, 1115 false /*isFirstChildSa*/); 1116 1117 final ConfigAttribute[] configAttributes = 1118 CreateChildSaHelper.getConfigAttributes(mChildSessionParams); 1119 if (configAttributes.length > 0) { 1120 mRequestPayloads.add( 1121 new IkeConfigPayload( 1122 false /*isReply*/, Arrays.asList(configAttributes))); 1123 } 1124 1125 mChildSmCallback.onOutboundPayloadsReady( 1126 EXCHANGE_TYPE_CREATE_CHILD_SA, 1127 false /*isResp*/, 1128 mRequestPayloads, 1129 ChildSessionStateMachine.this); 1130 } catch (SpiUnavailableException | ResourceUnavailableException e) { 1131 // Fail to assign SPI 1132 handleChildFatalError(e); 1133 } 1134 } 1135 1136 @Override processStateMessage(Message message)1137 public boolean processStateMessage(Message message) { 1138 switch (message.what) { 1139 case CMD_HANDLE_RECEIVED_RESPONSE: 1140 ReceivedCreateResponse rcvResp = (ReceivedCreateResponse) message.obj; 1141 CreateChildResult createChildResult = 1142 CreateChildSaHelper.validateAndNegotiateInitChild( 1143 mRequestPayloads, 1144 rcvResp.responsePayloads, 1145 rcvResp.exchangeType, 1146 EXCHANGE_TYPE_CREATE_CHILD_SA, 1147 mChildSessionParams.isTransportMode(), 1148 mIpSecSpiGenerator, 1149 mRemoteAddress); 1150 1151 // If the response includes the error notification for TEMPORARY_FAILURE, retry 1152 // creating the Child. 1153 if (isTemporaryFailure(createChildResult)) { 1154 transitionTo(mInitial); 1155 1156 mChildSmCallback.scheduleRetryLocalRequest( 1157 mLocalRequestFactory.getChildLocalRequest( 1158 CMD_LOCAL_REQUEST_CREATE_CHILD, 1159 mUserCallback, 1160 mChildSessionParams)); 1161 return HANDLED; 1162 } 1163 1164 validateAndBuildChild( 1165 mRequestPayloads, 1166 rcvResp.responsePayloads, 1167 rcvResp.registeredSpi, 1168 createChildResult); 1169 return HANDLED; 1170 default: 1171 return NOT_HANDLED; 1172 } 1173 } 1174 1175 @Override exitState()1176 public void exitState() { 1177 CreateChildSaHelper.releaseSpiResources(mRequestPayloads); 1178 } 1179 1180 @Override getMetricsStateCode()1181 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1182 return IkeMetrics.IKE_STATE_CHILD_CREATE_LOCAL_CREATE; 1183 } 1184 isTemporaryFailure(CreateChildResult createChildResult)1185 private boolean isTemporaryFailure(CreateChildResult createChildResult) { 1186 if (createChildResult.status != CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY) { 1187 return false; 1188 } 1189 return createChildResult.exception instanceof TemporaryFailureException; 1190 } 1191 } 1192 1193 /** 1194 * Idle represents a state when there is no ongoing IKE exchange affecting established Child SA. 1195 */ 1196 class Idle extends ExceptionHandler { 1197 @Override enterState()1198 public void enterState() { 1199 maybeNotifyIkeSessionStateMachine(); 1200 } 1201 maybeNotifyIkeSessionStateMachine()1202 protected void maybeNotifyIkeSessionStateMachine() { 1203 mChildSmCallback.onProcedureFinished(ChildSessionStateMachine.this); 1204 } 1205 1206 @Override processStateMessage(Message message)1207 public boolean processStateMessage(Message message) { 1208 switch (message.what) { 1209 case CMD_LOCAL_REQUEST_DELETE_CHILD: 1210 transitionTo(mDeleteChildLocalDelete); 1211 return HANDLED; 1212 case CMD_LOCAL_REQUEST_REKEY_CHILD: 1213 transitionTo(mRekeyChildLocalCreate); 1214 return HANDLED; 1215 case CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE: 1216 transitionTo(mMobikeRekeyChildLocalCreate); 1217 return HANDLED; 1218 case CMD_HANDLE_RECEIVED_REQUEST: 1219 ReceivedRequest req = (ReceivedRequest) message.obj; 1220 switch (req.exchangeSubtype) { 1221 case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: 1222 deferMessage(message); 1223 transitionTo(mDeleteChildRemoteDelete); 1224 return HANDLED; 1225 case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: 1226 deferMessage(message); 1227 transitionTo(mRekeyChildRemoteCreate); 1228 return HANDLED; 1229 default: 1230 return NOT_HANDLED; 1231 } 1232 case CMD_FORCE_TRANSITION: // Testing command 1233 transitionTo((State) message.obj); 1234 return HANDLED; 1235 default: 1236 return NOT_HANDLED; 1237 } 1238 } 1239 1240 @Override getMetricsStateCode()1241 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1242 return IkeMetrics.IKE_STATE_CHILD_IDLE; 1243 } 1244 } 1245 1246 /** 1247 * This class is for handling the case when the previous procedure was finished by a new request 1248 * 1249 * <p>This state is the destination state when Child Session receives a new procedure request in 1250 * Rekey Delete. When entering this state, Child Session will process the deferred request as 1251 * Idle state does but will not notify IKE Session that Child Session has finished all the 1252 * procedures. It prevents IKE Session from going back to Idle state when its Child Session is 1253 * still busy. 1254 */ 1255 class IdleWithDeferredRequest extends Idle { 1256 @Override maybeNotifyIkeSessionStateMachine()1257 public void maybeNotifyIkeSessionStateMachine() { 1258 // Do not notify IkeSessionStateMachine because Child Session needs to process the 1259 // deferred request and start a new procedure 1260 } 1261 1262 @Override getMetricsStateCode()1263 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1264 return IkeMetrics.IKE_STATE_CHILD_IDLE_WITH_DEFERRED_REQUEST; 1265 } 1266 } 1267 1268 /** 1269 * This class represents the state that Child Session was closed by the remote while waiting for 1270 * a response. 1271 * 1272 * <p>This state is the destination state when Child Session receives a Delete request while 1273 * waitng for a Rekey Create response. When that happens, Child Session should close all IPsec 1274 * SAs and notify the user immediately to prevent security risk. Child Session also needs to 1275 * continue waiting for the response and keep its parent IKE Session retransmitting the request, 1276 * as required by the IKE spec. 1277 */ 1278 private class ClosedAndAwaitResponse extends ExceptionHandler { 1279 @Override processStateMessage(Message message)1280 public boolean processStateMessage(Message message) { 1281 switch (message.what) { 1282 case CMD_HANDLE_RECEIVED_RESPONSE: 1283 // Do not need to verify the response since the Child Session is already closed 1284 1285 // Metrics not recorded, since already closed. Metrics recorded at the same 1286 // time that user callbacks are fired. 1287 quitSessionNow(); 1288 return HANDLED; 1289 default: 1290 return NOT_HANDLED; 1291 } 1292 } 1293 1294 @Override getMetricsStateCode()1295 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1296 return IkeMetrics.IKE_STATE_CHILD_CLOSE_AND_AWAIT_RESPONSE; 1297 } 1298 } 1299 1300 /** 1301 * DeleteResponderBase represents all states after Child Session is established 1302 * 1303 * <p>All post-init states share common functionality of being able to respond to Delete Child 1304 * requests. 1305 */ 1306 private abstract class DeleteResponderBase extends ExceptionHandler { 1307 /** 1308 * Check if the payload list has a Delete Payload that includes the remote SPI of the input 1309 * ChildSaRecord. 1310 */ hasRemoteChildSpiForDelete( List<IkePayload> payloads, ChildSaRecord expectedRecord)1311 protected boolean hasRemoteChildSpiForDelete( 1312 List<IkePayload> payloads, ChildSaRecord expectedRecord) { 1313 List<IkeDeletePayload> delPayloads = 1314 IkePayload.getPayloadListForTypeInProvidedList( 1315 PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, payloads); 1316 1317 for (IkeDeletePayload delPayload : delPayloads) { 1318 for (int spi : delPayload.spisToDelete) { 1319 if (spi == expectedRecord.getRemoteSpi()) return true; 1320 } 1321 } 1322 return false; 1323 } 1324 1325 /** 1326 * Build and send payload list that has a Delete Payload that includes the local SPI of the 1327 * input ChildSaRecord. 1328 */ sendDeleteChild(ChildSaRecord childSaRecord, boolean isResp)1329 protected void sendDeleteChild(ChildSaRecord childSaRecord, boolean isResp) { 1330 List<IkePayload> outIkePayloads = new ArrayList<>(1); 1331 outIkePayloads.add(new IkeDeletePayload(new int[] {childSaRecord.getLocalSpi()})); 1332 1333 mChildSmCallback.onOutboundPayloadsReady( 1334 EXCHANGE_TYPE_INFORMATIONAL, 1335 isResp, 1336 outIkePayloads, 1337 ChildSessionStateMachine.this); 1338 } 1339 1340 /** 1341 * Helper method for responding to a session deletion request 1342 * 1343 * <p>Note that this method expects that the session is keyed on the mCurrentChildSaRecord 1344 * and closing this Child SA indicates that the remote wishes to end the session as a whole. 1345 * As such, this should not be used in rekey cases where there is any ambiguity as to which 1346 * Child SA the session is reliant upon. 1347 * 1348 * <p>Note that this method will also quit the state machine 1349 */ handleDeleteSessionRequest(List<IkePayload> payloads)1350 protected void handleDeleteSessionRequest(List<IkePayload> payloads) { 1351 if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) { 1352 cleanUpAndQuit( 1353 new IllegalStateException( 1354 "Found no remote SPI for mCurrentChildSaRecord in a Delete Child" 1355 + " request.")); 1356 } else { 1357 sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/); 1358 closeSessionAndNotifyUser(true /* quitStateMachine */); 1359 } 1360 } 1361 closeSessionAndNotifyUser(boolean quitStateMachine)1362 protected void closeSessionAndNotifyUser(boolean quitStateMachine) { 1363 OnIpSecSaPairDeletedRunnable delRunnable = 1364 new OnIpSecSaPairDeletedRunnable(mCurrentChildSaRecord); 1365 executeUserCallback( 1366 () -> { 1367 delRunnable.run(); 1368 mUserCallback.onClosed(); 1369 }); 1370 recordMetricsEvent_sessionTerminated(null); 1371 1372 mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi()); 1373 mCurrentChildSaRecord.close(); 1374 mCurrentChildSaRecord = null; 1375 1376 if (quitStateMachine) { 1377 quitSessionNow(); 1378 } 1379 } 1380 } 1381 1382 /** 1383 * DeleteBase abstracts deletion handling for all states initiating and responding to a Delete 1384 * Child exchange 1385 * 1386 * <p>All subclasses of this state share common functionality that a deletion request is sent, 1387 * and the response is received. 1388 */ 1389 private abstract class DeleteBase extends DeleteResponderBase { 1390 /** Validate payload types in Delete Child response. */ validateDeleteRespPayloadAndExchangeType( List<IkePayload> respPayloads, @ExchangeType int exchangeType)1391 protected void validateDeleteRespPayloadAndExchangeType( 1392 List<IkePayload> respPayloads, @ExchangeType int exchangeType) 1393 throws IkeProtocolException { 1394 1395 if (exchangeType != EXCHANGE_TYPE_INFORMATIONAL) { 1396 throw new InvalidSyntaxException( 1397 "Unexpected exchange type in Delete Child response: " + exchangeType); 1398 } 1399 1400 for (IkePayload payload : respPayloads) { 1401 handlePayload: 1402 switch (payload.payloadType) { 1403 case PAYLOAD_TYPE_DELETE: 1404 // A Delete Payload is only required when it is not simultaneous deletion. 1405 // Included Child SPIs are verified in the subclass to make sure the remote 1406 // side is deleting the right SAs. 1407 break handlePayload; 1408 case PAYLOAD_TYPE_NOTIFY: 1409 IkeNotifyPayload notify = (IkeNotifyPayload) payload; 1410 if (!notify.isErrorNotify()) { 1411 logw( 1412 "Unexpected or unknown status notification in Delete Child" 1413 + " response: " 1414 + notify.notifyType); 1415 break handlePayload; 1416 } 1417 1418 throw notify.validateAndBuildIkeException(); 1419 default: 1420 logw( 1421 "Unexpected payload type in Delete Child response: " 1422 + payload.payloadType); 1423 } 1424 } 1425 } 1426 } 1427 1428 /** 1429 * DeleteChildLocalDelete represents the state where Child Session initiates the Delete Child 1430 * exchange. 1431 */ 1432 class DeleteChildLocalDelete extends DeleteBase { 1433 private boolean mSimulDeleteDetected = false; 1434 1435 @Override enterState()1436 public void enterState() { 1437 mSimulDeleteDetected = false; 1438 sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/); 1439 } 1440 1441 @Override processStateMessage(Message message)1442 public boolean processStateMessage(Message message) { 1443 switch (message.what) { 1444 case CMD_HANDLE_RECEIVED_RESPONSE: 1445 try { 1446 ReceivedResponse resp = (ReceivedResponse) message.obj; 1447 validateDeleteRespPayloadAndExchangeType( 1448 resp.responsePayloads, resp.exchangeType); 1449 1450 boolean currentSaSpiFound = 1451 hasRemoteChildSpiForDelete( 1452 resp.responsePayloads, mCurrentChildSaRecord); 1453 if (!currentSaSpiFound && !mSimulDeleteDetected) { 1454 throw new InvalidSyntaxException( 1455 "Found no remote SPI in received Delete response."); 1456 } else if (currentSaSpiFound && mSimulDeleteDetected) { 1457 // As required by the RFC 7296, in simultaneous delete case, the remote 1458 // side MUST NOT include SPI of mCurrentChildSaRecord. However, to 1459 // provide better interoperatibility, IKE library will keep IKE Session 1460 // alive and continue the deleting process. 1461 logw( 1462 "Found remote SPI in the Delete response in a simultaneous" 1463 + " deletion case"); 1464 } 1465 1466 closeSessionAndNotifyUser(true /* quitStateMachine */); 1467 } catch (IkeProtocolException e) { 1468 // Shut down Child Session and notify users the error. 1469 handleChildFatalError(e); 1470 } 1471 return HANDLED; 1472 case CMD_HANDLE_RECEIVED_REQUEST: 1473 ReceivedRequest req = (ReceivedRequest) message.obj; 1474 switch (req.exchangeSubtype) { 1475 case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: 1476 // It has been verified in IkeSessionStateMachine that the incoming 1477 // request can be ONLY for mCurrentChildSaRecord at this point. 1478 if (!hasRemoteChildSpiForDelete( 1479 req.requestPayloads, mCurrentChildSaRecord)) { 1480 // Program error 1481 cleanUpAndQuit( 1482 new IllegalStateException( 1483 "Found no remote SPI for mCurrentChildSaRecord in" 1484 + " a Delete request")); 1485 1486 } else { 1487 mChildSmCallback.onOutboundPayloadsReady( 1488 EXCHANGE_TYPE_INFORMATIONAL, 1489 true /*isResp*/, 1490 new LinkedList<>(), 1491 ChildSessionStateMachine.this); 1492 mSimulDeleteDetected = true; 1493 } 1494 return HANDLED; 1495 case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: 1496 replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); 1497 return HANDLED; 1498 default: 1499 cleanUpAndQuit( 1500 new IllegalStateException( 1501 "Invalid exchange subtype for Child Session: " 1502 + req.exchangeSubtype)); 1503 return HANDLED; 1504 } 1505 default: 1506 return NOT_HANDLED; 1507 } 1508 } 1509 1510 @Override getMetricsStateCode()1511 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1512 return IkeMetrics.IKE_STATE_CHILD_DELETE_LOCAL_DELETE; 1513 } 1514 } 1515 1516 /** 1517 * DeleteChildRemoteDelete represents the state where Child Session receives the Delete Child 1518 * request. 1519 */ 1520 class DeleteChildRemoteDelete extends DeleteResponderBase { 1521 @Override processStateMessage(Message message)1522 public boolean processStateMessage(Message message) { 1523 switch (message.what) { 1524 case CMD_HANDLE_RECEIVED_REQUEST: 1525 ReceivedRequest req = (ReceivedRequest) message.obj; 1526 if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { 1527 handleDeleteSessionRequest(req.requestPayloads); 1528 return HANDLED; 1529 } 1530 return NOT_HANDLED; 1531 default: 1532 return NOT_HANDLED; 1533 } 1534 } 1535 1536 @Override getMetricsStateCode()1537 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1538 return IkeMetrics.IKE_STATE_CHILD_DELETE_REMOTE_DELETE; 1539 } 1540 } 1541 1542 /** 1543 * RekeyChildLocalCreate represents the state where Child Session initiates the Rekey Child 1544 * exchange. 1545 * 1546 * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have 1547 * different Traffic Selectors and algorithms than the old one." 1548 */ 1549 class RekeyChildLocalCreate extends DeleteResponderBase { 1550 private List<IkePayload> mRequestPayloads; 1551 1552 @Override enterState()1553 public void enterState() { 1554 try { 1555 ChildSaProposal saProposal = mSaProposal; 1556 if (mIsFirstChild) { 1557 saProposal = addDhGroupsFromChildSessionParamsIfAbsent(); 1558 } 1559 1560 // Build request with negotiated proposal and TS. 1561 mRequestPayloads = 1562 CreateChildSaHelper.getRekeyChildCreateReqPayloads( 1563 mIkeContext.getRandomnessFactory(), 1564 mIpSecSpiGenerator, 1565 mLocalAddress, 1566 saProposal, 1567 mLocalTs, 1568 mRemoteTs, 1569 mCurrentChildSaRecord.getLocalSpi(), 1570 mChildSessionParams.isTransportMode()); 1571 mChildSmCallback.onOutboundPayloadsReady( 1572 EXCHANGE_TYPE_CREATE_CHILD_SA, 1573 false /*isResp*/, 1574 mRequestPayloads, 1575 ChildSessionStateMachine.this); 1576 } catch (SpiUnavailableException | ResourceUnavailableException e) { 1577 loge("Fail to assign Child SPI. Schedule a retry for rekey Child"); 1578 mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); 1579 transitionTo(mIdle); 1580 } 1581 } 1582 1583 @Override processStateMessage(Message message)1584 public boolean processStateMessage(Message message) { 1585 switch (message.what) { 1586 case CMD_HANDLE_RECEIVED_REQUEST: 1587 ReceivedRequest req = (ReceivedRequest) message.obj; 1588 1589 if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { 1590 // Handle Delete request, notify users and do state transition to continue 1591 // waiting for the response 1592 sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/); 1593 closeSessionAndNotifyUser(false /* quitStateMachine */); 1594 transitionTo(mClosedAndAwaitResponse); 1595 } else { 1596 replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); 1597 } 1598 return HANDLED; 1599 case CMD_HANDLE_RECEIVED_RESPONSE: 1600 ReceivedCreateResponse resp = (ReceivedCreateResponse) message.obj; 1601 CreateChildResult createChildResult = 1602 CreateChildSaHelper.validateAndNegotiateRekeyChildResp( 1603 mRequestPayloads, 1604 resp.responsePayloads, 1605 resp.exchangeType, 1606 EXCHANGE_TYPE_CREATE_CHILD_SA, 1607 mChildSessionParams.isTransportMode(), 1608 mCurrentChildSaRecord, 1609 mIpSecSpiGenerator, 1610 mRemoteAddress); 1611 1612 switch (createChildResult.status) { 1613 case CREATE_STATUS_OK: 1614 try { 1615 // Do not need to update TS because they are not changed. 1616 mSaProposal = createChildResult.negotiatedProposal; 1617 1618 mLocalInitNewChildSaRecord = 1619 ChildSaRecord.makeChildSaRecord( 1620 mIkeContext.getContext(), 1621 mRequestPayloads, 1622 resp.responsePayloads, 1623 createChildResult.initSpi, 1624 createChildResult.respSpi, 1625 mLocalAddress, 1626 mRemoteAddress, 1627 mUdpEncapSocket, 1628 mIkePrf, 1629 mChildIntegrity, 1630 mChildCipher, 1631 mSkD, 1632 mChildSessionParams.isTransportMode(), 1633 true /*isLocalInit*/, 1634 buildSaLifetimeAlarmSched( 1635 createChildResult.respSpi.getSpi())); 1636 1637 List<Integer> integrityAlgorithms = 1638 mSaProposal.getIntegrityAlgorithms(); 1639 List<Integer> dhGroups = mSaProposal.getDhGroups(); 1640 1641 recordMetricsEvent_SaNegotiation( 1642 dhGroups.isEmpty() 1643 ? IkeMetrics.DH_GROUP_UNSPECIFIED 1644 : dhGroups.get(0), 1645 mSaProposal.getEncryptionTransforms()[0].id, 1646 mSaProposal.getEncryptionTransforms()[0] 1647 .getSpecifiedKeyLength(), 1648 integrityAlgorithms.isEmpty() 1649 ? IkeMetrics.INTEGRITY_ALGORITHM_NONE 1650 : integrityAlgorithms.get(0), 1651 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED, 1652 null); 1653 1654 notifyCallerForLocalChildSaRekey(); 1655 1656 transitionTo(mRekeyChildLocalDelete); 1657 } catch (GeneralSecurityException 1658 | ResourceUnavailableException 1659 | SpiUnavailableException 1660 | IOException e) { 1661 // #makeChildSaRecord failed 1662 handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e); 1663 } finally { 1664 // In the successful case the transform in ChildSaRecord has taken 1665 // ownership of the SPI (in IpSecService), and will keep it alive. 1666 createChildResult.initSpi.close(); 1667 createChildResult.respSpi.close(); 1668 } 1669 break; 1670 case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: 1671 handleProcessRespOrSaCreationFailAndQuit( 1672 resp.registeredSpi, createChildResult.exception); 1673 break; 1674 case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: 1675 handleErrorNotify(createChildResult.exception); 1676 transitionTo(mIdle); 1677 break; 1678 default: 1679 cleanUpAndQuit( 1680 new IllegalStateException( 1681 "Unrecognized status: " + createChildResult.status)); 1682 } 1683 return HANDLED; 1684 default: 1685 return NOT_HANDLED; 1686 } 1687 } 1688 notifyCallerForLocalChildSaRekey()1689 protected void notifyCallerForLocalChildSaRekey() { 1690 OnIpSecSaPairCreatedRunnable createRunnable = 1691 new OnIpSecSaPairCreatedRunnable(mLocalInitNewChildSaRecord); 1692 executeUserCallback(createRunnable); 1693 } 1694 handleProcessRespOrSaCreationFailAndQuit( int registeredSpi, Exception exception)1695 protected void handleProcessRespOrSaCreationFailAndQuit( 1696 int registeredSpi, Exception exception) { 1697 // We don't retry rekey if failure was caused by invalid response or SA creation error. 1698 // Reason is there is no way to notify the remote side the old SA is still alive but the 1699 // new one has failed. Sending delete request for new SA indicates the rekey has 1700 // finished and the new SA has died. 1701 1702 // TODO: Initiate deletion on newly created SA 1703 if (registeredSpi != SPI_NOT_REGISTERED) { 1704 mChildSmCallback.onChildSaDeleted(registeredSpi); 1705 } 1706 handleChildFatalError(exception); 1707 } 1708 handleErrorNotify(Exception exception)1709 protected void handleErrorNotify(Exception exception) { 1710 loge("Received error notification for rekey Child. Schedule a retry"); 1711 mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); 1712 } 1713 1714 @Override exitState()1715 public void exitState() { 1716 CreateChildSaHelper.releaseSpiResources(mRequestPayloads); 1717 } 1718 1719 @Override getMetricsStateCode()1720 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1721 return IkeMetrics.IKE_STATE_CHILD_REKEY_LOCAL_CREATE; 1722 } 1723 } 1724 1725 /** 1726 * MobikeRekeyChildLocalCreate represents the state where Child Session initiates the Rekey 1727 * Child exchange for MOBIKE-enabled IKE Sessions. 1728 * 1729 * <p>MobikeRekeyChildLocalCreate behaves similarly to RekeyChildLocalCreate except: 1730 * 1731 * <ul> 1732 * <li>It notifies the caller of Child SA creation via {@link 1733 * ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform, 1734 * android.net.IpSecTransform)}. . 1735 * <li>It tears down IKE SA immediately if any error occurs. In this case the origin Child SA 1736 * has lost connectivity due to the changed IP addresses, and renewing the Child SA by 1737 * MOBIKE rekey has failed. IKE module will handle this failure by tearing down the IKE 1738 * Session and notifying the caller. In this way, the caller can immediately re-establish 1739 * the IKE session if needed. 1740 * </ul> 1741 * 1742 * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have 1743 * different Traffic Selectors and algorithms than the old one." 1744 */ 1745 class MobikeRekeyChildLocalCreate extends RekeyChildLocalCreate { 1746 @Override notifyCallerForLocalChildSaRekey()1747 protected void notifyCallerForLocalChildSaRekey() { 1748 IpSecTransform inTransform = mLocalInitNewChildSaRecord.getInboundIpSecTransform(); 1749 IpSecTransform outTransform = mLocalInitNewChildSaRecord.getOutboundIpSecTransform(); 1750 executeUserCallback( 1751 () -> mUserCallback.onIpSecTransformsMigrated(inTransform, outTransform)); 1752 } 1753 1754 @Override handleProcessRespOrSaCreationFailAndQuit( int registeredSpi, Exception exception)1755 protected void handleProcessRespOrSaCreationFailAndQuit( 1756 int registeredSpi, Exception exception) { 1757 sendDeleteIkeRequest(); 1758 1759 recordMetricsEvent_sessionTerminated(wrapAsIkeException(exception)); 1760 mChildSmCallback.onFatalIkeSessionError(exception); 1761 } 1762 1763 @Override handleErrorNotify(Exception exception)1764 protected void handleErrorNotify(Exception exception) { 1765 loge("Received error notification for rekey Child. Tear down IKE SA"); 1766 sendDeleteIkeRequest(); 1767 1768 recordMetricsEvent_sessionTerminated(wrapAsIkeException(exception)); 1769 mChildSmCallback.onFatalIkeSessionError(exception); 1770 } 1771 1772 @Override getMetricsStateCode()1773 protected @IkeMetrics.IkeState int getMetricsStateCode() { 1774 return IkeMetrics.IKE_STATE_CHILD_MOBIKE_REKEY_LOCAL_CREATE; 1775 } 1776 } 1777 addDhGroupsFromChildSessionParamsIfAbsent()1778 private ChildSaProposal addDhGroupsFromChildSessionParamsIfAbsent() { 1779 // DH groups are excluded for the first child. Add dh groups from child session params in 1780 // this case. 1781 if (mSaProposal.getDhGroups().size() != 0) { 1782 return mSaProposal; 1783 } 1784 1785 Set<DhGroupTransform> dhGroupSet = new LinkedHashSet<>(); 1786 for (SaProposal saProposal : mChildSessionParams.getSaProposals()) { 1787 if (!mSaProposal.isNegotiatedFromExceptDhGroup(saProposal)) continue; 1788 dhGroupSet.addAll(Arrays.asList(saProposal.getDhGroupTransforms())); 1789 } 1790 1791 DhGroupTransform[] dhGroups = new DhGroupTransform[dhGroupSet.size()]; 1792 dhGroupSet.toArray(dhGroups); 1793 1794 return new ChildSaProposal( 1795 mSaProposal.getEncryptionTransforms(), 1796 mSaProposal.getIntegrityTransforms(), 1797 dhGroups, 1798 mSaProposal.getEsnTransforms()); 1799 } 1800 1801 /** 1802 * RekeyChildRemoteCreate represents the state where Child Session receives a Rekey Child 1803 * request. 1804 * 1805 * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have 1806 * different Traffic Selectors and algorithms than the old one." 1807 * 1808 * <p>Errors in this exchange with no specific protocol error code will all be classified to use 1809 * NO_PROPOSAL_CHOSEN. The reason that we don't use NO_ADDITIONAL_SAS is because it indicates 1810 * "responder is unwilling to accept any more Child SAs on this IKE SA.", according to RFC 7296. 1811 * Sending this error may mislead the remote peer. 1812 */ 1813 class RekeyChildRemoteCreate extends ExceptionHandler { 1814 @Override processStateMessage(Message message)1815 public boolean processStateMessage(Message message) { 1816 switch (message.what) { 1817 case CMD_HANDLE_RECEIVED_REQUEST: 1818 ReceivedRequest req = (ReceivedRequest) message.obj; 1819 1820 if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_REKEY_CHILD) { 1821 handleCreateChildRequest(req); 1822 return HANDLED; 1823 } 1824 1825 return NOT_HANDLED; 1826 default: 1827 return NOT_HANDLED; 1828 } 1829 } 1830 handleCreateChildRequest(ReceivedRequest req)1831 private void handleCreateChildRequest(ReceivedRequest req) { 1832 List<IkePayload> reqPayloads = null; 1833 List<IkePayload> respPayloads = null; 1834 try { 1835 reqPayloads = req.requestPayloads; 1836 1837 // Build a rekey response payload list with our previously selected proposal, 1838 // against which we will validate the received request. It is guaranteed in 1839 // IkeSessionStateMachine#getIkeExchangeSubType that a SA Payload is included in the 1840 // inbound request payload list. 1841 IkeSaPayload reqSaPayload = 1842 IkePayload.getPayloadForTypeInProvidedList( 1843 PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); 1844 1845 IkeKePayload reqKePayload = 1846 IkePayload.getPayloadForTypeInProvidedList( 1847 PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads); 1848 1849 ChildSaProposal saProposal = mSaProposal; 1850 if (reqKePayload != null) { 1851 saProposal = 1852 reqSaPayload.getNegotiatedChildProposalWithDh( 1853 mSaProposal, 1854 mChildSessionParams.getChildSaProposals(), 1855 reqKePayload.dhGroup, 1856 mIkeDhGroup); 1857 } 1858 1859 1860 byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(saProposal); 1861 1862 respPayloads = 1863 CreateChildSaHelper.getRekeyChildCreateRespPayloads( 1864 mIkeContext.getRandomnessFactory(), 1865 mIpSecSpiGenerator, 1866 mLocalAddress, 1867 respProposalNumber, 1868 saProposal, 1869 mLocalTs, 1870 mRemoteTs, 1871 mCurrentChildSaRecord.getLocalSpi(), 1872 mChildSessionParams.isTransportMode()); 1873 } catch (NoValidProposalChosenException | InvalidKeException e) { 1874 handleCreationFailureAndBackToIdle(e); 1875 return; 1876 } catch (SpiUnavailableException | ResourceUnavailableException e) { 1877 handleCreationFailureAndBackToIdle( 1878 new NoValidProposalChosenException("Fail to assign inbound SPI", e)); 1879 return; 1880 } 1881 1882 CreateChildResult createChildResult = 1883 CreateChildSaHelper.validateAndNegotiateRekeyChildRequest( 1884 reqPayloads, 1885 respPayloads, 1886 req.exchangeType /*exchangeType*/, 1887 EXCHANGE_TYPE_CREATE_CHILD_SA /*expectedExchangeType*/, 1888 mChildSessionParams.isTransportMode(), 1889 mIpSecSpiGenerator, 1890 mRemoteAddress); 1891 1892 switch (createChildResult.status) { 1893 case CREATE_STATUS_OK: 1894 try { 1895 // Do not need to update TS because they are not changed. 1896 mSaProposal = createChildResult.negotiatedProposal; 1897 1898 mRemoteInitNewChildSaRecord = 1899 ChildSaRecord.makeChildSaRecord( 1900 mIkeContext.getContext(), 1901 reqPayloads, 1902 respPayloads, 1903 createChildResult.initSpi, 1904 createChildResult.respSpi, 1905 mLocalAddress, 1906 mRemoteAddress, 1907 mUdpEncapSocket, 1908 mIkePrf, 1909 mChildIntegrity, 1910 mChildCipher, 1911 mSkD, 1912 mChildSessionParams.isTransportMode(), 1913 false /*isLocalInit*/, 1914 buildSaLifetimeAlarmSched( 1915 createChildResult.initSpi.getSpi())); 1916 1917 mChildSmCallback.onChildSaCreated( 1918 mRemoteInitNewChildSaRecord.getRemoteSpi(), 1919 ChildSessionStateMachine.this); 1920 1921 // To avoid traffic loss, outbound transform should only be applied once 1922 // the remote has (implicitly) acknowledged our response via the 1923 // delete-old-SA request. This will be performed in the finishRekey() 1924 // method. 1925 IpSecTransform inTransform = 1926 mRemoteInitNewChildSaRecord.getInboundIpSecTransform(); 1927 executeUserCallback( 1928 () -> { 1929 mUserCallback.onIpSecTransformCreated( 1930 inTransform, IpSecManager.DIRECTION_IN); 1931 }); 1932 1933 mChildSmCallback.onOutboundPayloadsReady( 1934 EXCHANGE_TYPE_CREATE_CHILD_SA, 1935 true /*isResp*/, 1936 respPayloads, 1937 ChildSessionStateMachine.this); 1938 1939 List<Integer> integrityAlgorithms = mSaProposal.getIntegrityAlgorithms(); 1940 List<Integer> dhGroups = mSaProposal.getDhGroups(); 1941 1942 recordMetricsEvent_SaNegotiation( 1943 dhGroups.isEmpty() 1944 ? IkeMetrics.DH_GROUP_UNSPECIFIED 1945 : dhGroups.get(0), 1946 mSaProposal.getEncryptionTransforms()[0].id, 1947 mSaProposal.getEncryptionTransforms()[0].getSpecifiedKeyLength(), 1948 integrityAlgorithms.isEmpty() 1949 ? IkeMetrics.INTEGRITY_ALGORITHM_NONE 1950 : integrityAlgorithms.get(0), 1951 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED, 1952 null); 1953 1954 transitionTo(mRekeyChildRemoteDelete); 1955 } catch (GeneralSecurityException 1956 | ResourceUnavailableException 1957 | SpiUnavailableException 1958 | IOException e) { 1959 // #makeChildSaRecord failed. 1960 handleCreationFailureAndBackToIdle( 1961 new NoValidProposalChosenException( 1962 "Error in Child SA creation", e)); 1963 } finally { 1964 // In the successful case the transform in ChildSaRecord has taken ownership 1965 // of the SPI (in IpSecService), and will keep it alive. 1966 createChildResult.initSpi.close(); 1967 createChildResult.respSpi.close(); 1968 } 1969 break; 1970 case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: 1971 IkeException error = createChildResult.exception; 1972 if (error instanceof IkeProtocolException) { 1973 handleCreationFailureAndBackToIdle((IkeProtocolException) error); 1974 } else { 1975 handleCreationFailureAndBackToIdle( 1976 new NoValidProposalChosenException( 1977 "Error in validating Create Child request", error)); 1978 } 1979 break; 1980 case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: 1981 cleanUpAndQuit( 1982 new IllegalStateException( 1983 "Unexpected processing status in Create Child request: " 1984 + createChildResult.status)); 1985 break; 1986 default: 1987 cleanUpAndQuit( 1988 new IllegalStateException( 1989 "Unrecognized status: " + createChildResult.status)); 1990 } 1991 } 1992 handleCreationFailureAndBackToIdle(IkeProtocolException e)1993 private void handleCreationFailureAndBackToIdle(IkeProtocolException e) { 1994 loge("Received invalid Rekey Child request. Reject with error notification", e); 1995 1996 ArrayList<IkePayload> payloads = new ArrayList<>(1); 1997 payloads.add(e.buildNotifyPayload()); 1998 mChildSmCallback.onOutboundPayloadsReady( 1999 EXCHANGE_TYPE_CREATE_CHILD_SA, 2000 true /*isResp*/, 2001 payloads, 2002 ChildSessionStateMachine.this); 2003 2004 transitionTo(mIdle); 2005 } 2006 2007 @Override getMetricsStateCode()2008 protected @IkeMetrics.IkeState int getMetricsStateCode() { 2009 return IkeMetrics.IKE_STATE_CHILD_REKEY_REMOTE_CREATE; 2010 } 2011 } 2012 2013 /** 2014 * RekeyChildDeleteBase represents common behaviours of deleting stage during rekeying Child SA. 2015 */ 2016 abstract class RekeyChildDeleteBase extends DeleteBase { 2017 @Override processStateMessage(Message message)2018 public boolean processStateMessage(Message message) { 2019 switch (message.what) { 2020 case CMD_HANDLE_RECEIVED_REQUEST: 2021 try { 2022 if (isOnNewSa((ReceivedRequest) message.obj)) { 2023 finishRekey(); 2024 deferMessage(message); 2025 transitionTo(mIdleWithDeferredRequest); 2026 return HANDLED; 2027 } 2028 return NOT_HANDLED; 2029 } catch (IllegalStateException e) { 2030 cleanUpAndQuit(e); 2031 return HANDLED; 2032 } 2033 default: 2034 return NOT_HANDLED; 2035 } 2036 } 2037 isOnNewSa(ReceivedRequest req)2038 private boolean isOnNewSa(ReceivedRequest req) { 2039 switch (req.exchangeSubtype) { 2040 case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: 2041 return hasRemoteChildSpiForDelete(req.requestPayloads, mChildSaRecordSurviving); 2042 case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: 2043 return CreateChildSaHelper.hasRemoteChildSpiForRekey( 2044 req.requestPayloads, mChildSaRecordSurviving); 2045 default: 2046 throw new IllegalStateException( 2047 "Invalid exchange subtype for Child Session: " + req.exchangeSubtype); 2048 } 2049 } 2050 2051 // Rekey timer for old SA will be cancelled as part of the closing of the SA. finishRekey()2052 protected void finishRekey() { 2053 OnIpSecSaPairDeletedRunnable delRunnable = 2054 new OnIpSecSaPairDeletedRunnable(mCurrentChildSaRecord); 2055 executeUserCallback(delRunnable); 2056 2057 mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi()); 2058 mCurrentChildSaRecord.close(); 2059 2060 mCurrentChildSaRecord = mChildSaRecordSurviving; 2061 2062 mLocalInitNewChildSaRecord = null; 2063 mRemoteInitNewChildSaRecord = null; 2064 mChildSaRecordSurviving = null; 2065 } 2066 } 2067 2068 /** 2069 * RekeyChildLocalDelete represents the deleting stage of a locally-initiated Rekey Child 2070 * procedure. 2071 */ 2072 class RekeyChildLocalDelete extends RekeyChildDeleteBase { 2073 private boolean mSimulDeleteDetected; 2074 2075 @Override enterState()2076 public void enterState() { 2077 mSimulDeleteDetected = false; 2078 mChildSaRecordSurviving = mLocalInitNewChildSaRecord; 2079 sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/); 2080 } 2081 2082 @Override processStateMessage(Message message)2083 public boolean processStateMessage(Message message) { 2084 if (super.processStateMessage(message) == HANDLED) { 2085 return HANDLED; 2086 } 2087 2088 switch (message.what) { 2089 case CMD_HANDLE_RECEIVED_REQUEST: 2090 ReceivedRequest req = (ReceivedRequest) message.obj; 2091 2092 if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { 2093 // Reply with empty message during simultaneous deleting and keep waiting 2094 // for Delete response. 2095 mChildSmCallback.onOutboundPayloadsReady( 2096 EXCHANGE_TYPE_INFORMATIONAL, 2097 true /*isResp*/, 2098 new ArrayList<>(), 2099 ChildSessionStateMachine.this); 2100 mSimulDeleteDetected = true; 2101 } else { 2102 replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); 2103 } 2104 return HANDLED; 2105 case CMD_HANDLE_RECEIVED_RESPONSE: 2106 try { 2107 ReceivedResponse resp = (ReceivedResponse) message.obj; 2108 validateDeleteRespPayloadAndExchangeType( 2109 resp.responsePayloads, resp.exchangeType); 2110 2111 boolean currentSaSpiFound = 2112 hasRemoteChildSpiForDelete( 2113 resp.responsePayloads, mCurrentChildSaRecord); 2114 if (!mSimulDeleteDetected && !currentSaSpiFound) { 2115 loge( 2116 "Found no remote SPI for current SA in received Delete" 2117 + " response. Shutting down old SA and finishing rekey."); 2118 } 2119 } catch (IkeProtocolException e) { 2120 loge( 2121 "Received Delete response with invalid syntax or error" 2122 + " notifications. Shutting down old SA and finishing rekey.", 2123 e); 2124 } 2125 finishRekey(); 2126 transitionTo(mIdle); 2127 return HANDLED; 2128 default: 2129 return NOT_HANDLED; 2130 } 2131 } 2132 2133 @Override getMetricsStateCode()2134 protected @IkeMetrics.IkeState int getMetricsStateCode() { 2135 return IkeMetrics.IKE_STATE_CHILD_REKEY_LOCAL_DELETE; 2136 } 2137 } 2138 2139 /** 2140 * RekeyChildRemoteDelete represents the deleting stage of a remotely-initiated Rekey Child 2141 * procedure. 2142 */ 2143 class RekeyChildRemoteDelete extends RekeyChildDeleteBase { 2144 @Override enterState()2145 public void enterState() { 2146 mChildSaRecordSurviving = mRemoteInitNewChildSaRecord; 2147 sendMessageDelayed(TIMEOUT_REKEY_REMOTE_DELETE, REKEY_DELETE_TIMEOUT_MS); 2148 } 2149 2150 @Override processStateMessage(Message message)2151 public boolean processStateMessage(Message message) { 2152 if (super.processStateMessage(message) == HANDLED) { 2153 return HANDLED; 2154 } 2155 2156 switch (message.what) { 2157 case CMD_HANDLE_RECEIVED_REQUEST: 2158 ReceivedRequest req = (ReceivedRequest) message.obj; 2159 2160 if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { 2161 handleDeleteRequest(req.requestPayloads); 2162 2163 } else { 2164 replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); 2165 } 2166 return HANDLED; 2167 case TIMEOUT_REKEY_REMOTE_DELETE: 2168 // Receiving this signal means the remote side has received the outbound 2169 // Rekey-Create response since no retransmissions were received during the 2170 // waiting time. IKE library will assume the remote side has set up the new 2171 // Child SA and finish the rekey procedure. Users should be warned there is 2172 // a risk that the remote side failed to set up the new Child SA and all 2173 // outbound IPsec traffic protected by new Child SA will be dropped. 2174 2175 // TODO:Consider finishing rekey procedure if the IKE Session receives a new 2176 // request. Since window size is one, receiving a new request indicates the 2177 // remote side has received the outbound Rekey-Create response 2178 2179 finishRekey(); 2180 transitionTo(mIdle); 2181 return HANDLED; 2182 default: 2183 return NOT_HANDLED; 2184 } 2185 } 2186 handleDeleteRequest(List<IkePayload> payloads)2187 private void handleDeleteRequest(List<IkePayload> payloads) { 2188 if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) { 2189 // Request received on incorrect SA 2190 cleanUpAndQuit( 2191 new IllegalStateException( 2192 "Found no remote SPI for current SA in received Delete" 2193 + " response.")); 2194 } else { 2195 sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/); 2196 finishRekey(); 2197 transitionTo(mIdle); 2198 } 2199 } 2200 2201 @Override finishRekey()2202 protected void finishRekey() { 2203 IpSecTransform outTransform = mRemoteInitNewChildSaRecord.getOutboundIpSecTransform(); 2204 executeUserCallback( 2205 () -> { 2206 mUserCallback.onIpSecTransformCreated( 2207 outTransform, IpSecManager.DIRECTION_OUT); 2208 }); 2209 2210 super.finishRekey(); 2211 } 2212 2213 @Override exitState()2214 public void exitState() { 2215 removeMessages(TIMEOUT_REKEY_REMOTE_DELETE); 2216 } 2217 2218 @Override getMetricsStateCode()2219 protected @IkeMetrics.IkeState int getMetricsStateCode() { 2220 return IkeMetrics.IKE_STATE_CHILD_REKEY_REMOTE_DELETE; 2221 } 2222 } 2223 2224 @Override getMetricsSessionType()2225 protected @IkeMetrics.IkeSessionType int getMetricsSessionType() { 2226 return IkeMetrics.IKE_SESSION_TYPE_CHILD; 2227 } 2228 2229 /** 2230 * Package private helper class to generate IKE SA creation payloads, in both request and 2231 * response directions. 2232 */ 2233 static class CreateChildSaHelper { 2234 /** Create payload list for creating the initial Child SA for this Child Session. */ getInitChildCreateReqPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, ChildSessionParams childSessionParams, boolean isFirstChildSa)2235 public static List<IkePayload> getInitChildCreateReqPayloads( 2236 RandomnessFactory randomFactory, 2237 IpSecSpiGenerator ipSecSpiGenerator, 2238 InetAddress localAddress, 2239 ChildSessionParams childSessionParams, 2240 boolean isFirstChildSa) 2241 throws SpiUnavailableException, ResourceUnavailableException { 2242 2243 ChildSaProposal[] saProposals = childSessionParams.getSaProposalsInternal(); 2244 2245 if (isFirstChildSa) { 2246 for (int i = 0; i < saProposals.length; i++) { 2247 saProposals[i] = 2248 childSessionParams.getSaProposalsInternal()[i] 2249 .getCopyWithoutDhTransform(); 2250 } 2251 } 2252 2253 List<IkePayload> payloadList = 2254 getChildCreatePayloads( 2255 IkeSaPayload.createChildSaRequestPayload( 2256 saProposals, ipSecSpiGenerator, localAddress), 2257 childSessionParams.getInboundTrafficSelectorsInternal(), 2258 childSessionParams.getOutboundTrafficSelectorsInternal(), 2259 childSessionParams.isTransportMode(), 2260 isFirstChildSa, 2261 randomFactory); 2262 2263 return payloadList; 2264 } 2265 getConfigAttributes(ChildSessionParams params)2266 public static ConfigAttribute[] getConfigAttributes(ChildSessionParams params) { 2267 if (!params.isTransportMode()) { 2268 return ((TunnelModeChildSessionParams) params).getConfigurationAttributesInternal(); 2269 } 2270 return new ConfigAttribute[0]; 2271 } 2272 2273 /** Create payload list as a rekey Child Session request. */ getRekeyChildCreateReqPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, ChildSaProposal currentProposal, IkeTrafficSelector[] currentLocalTs, IkeTrafficSelector[] currentRemoteTs, int localSpi, boolean isTransport)2274 public static List<IkePayload> getRekeyChildCreateReqPayloads( 2275 RandomnessFactory randomFactory, 2276 IpSecSpiGenerator ipSecSpiGenerator, 2277 InetAddress localAddress, 2278 ChildSaProposal currentProposal, 2279 IkeTrafficSelector[] currentLocalTs, 2280 IkeTrafficSelector[] currentRemoteTs, 2281 int localSpi, 2282 boolean isTransport) 2283 throws SpiUnavailableException, ResourceUnavailableException { 2284 List<IkePayload> payloads = 2285 getChildCreatePayloads( 2286 IkeSaPayload.createChildSaRequestPayload( 2287 new ChildSaProposal[] {currentProposal}, 2288 ipSecSpiGenerator, 2289 localAddress), 2290 currentLocalTs, 2291 currentRemoteTs, 2292 isTransport, 2293 false /*isFirstChildSa*/, 2294 randomFactory); 2295 2296 payloads.add( 2297 new IkeNotifyPayload( 2298 PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0])); 2299 return payloads; 2300 } 2301 2302 /** Create payload list as a rekey Child Session response. */ getRekeyChildCreateRespPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, byte proposalNumber, ChildSaProposal currentProposal, IkeTrafficSelector[] currentLocalTs, IkeTrafficSelector[] currentRemoteTs, int localSpi, boolean isTransport)2303 public static List<IkePayload> getRekeyChildCreateRespPayloads( 2304 RandomnessFactory randomFactory, 2305 IpSecSpiGenerator ipSecSpiGenerator, 2306 InetAddress localAddress, 2307 byte proposalNumber, 2308 ChildSaProposal currentProposal, 2309 IkeTrafficSelector[] currentLocalTs, 2310 IkeTrafficSelector[] currentRemoteTs, 2311 int localSpi, 2312 boolean isTransport) 2313 throws SpiUnavailableException, ResourceUnavailableException { 2314 List<IkePayload> payloads = 2315 getChildCreatePayloads( 2316 IkeSaPayload.createChildSaResponsePayload( 2317 proposalNumber, 2318 currentProposal, 2319 ipSecSpiGenerator, 2320 localAddress), 2321 currentRemoteTs /*initTs*/, 2322 currentLocalTs /*respTs*/, 2323 isTransport, 2324 false /*isFirstChildSa*/, 2325 randomFactory); 2326 2327 payloads.add( 2328 new IkeNotifyPayload( 2329 PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0])); 2330 return payloads; 2331 } 2332 2333 /** Create payload list for creating a new Child SA. */ getChildCreatePayloads( IkeSaPayload saPayload, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs, boolean isTransport, boolean isFirstChildSa, RandomnessFactory randomFactory)2334 private static List<IkePayload> getChildCreatePayloads( 2335 IkeSaPayload saPayload, 2336 IkeTrafficSelector[] initTs, 2337 IkeTrafficSelector[] respTs, 2338 boolean isTransport, 2339 boolean isFirstChildSa, 2340 RandomnessFactory randomFactory) 2341 throws ResourceUnavailableException { 2342 List<IkePayload> payloadList = new ArrayList<>(5); 2343 2344 payloadList.add(saPayload); 2345 payloadList.add(new IkeTsPayload(true /*isInitiator*/, initTs)); 2346 payloadList.add(new IkeTsPayload(false /*isInitiator*/, respTs)); 2347 2348 if (!isFirstChildSa) { 2349 payloadList.add(new IkeNoncePayload(randomFactory)); 2350 } 2351 2352 DhGroupTransform[] dhGroups = 2353 ((ChildProposal) saPayload.proposalList.get(0)) 2354 .saProposal.getDhGroupTransforms(); 2355 if (dhGroups.length != 0 && dhGroups[0].id != DH_GROUP_NONE) { 2356 payloadList.add( 2357 IkeKePayload.createOutboundKePayload(dhGroups[0].id, randomFactory)); 2358 } 2359 2360 if (isTransport) payloadList.add(new IkeNotifyPayload(NOTIFY_TYPE_USE_TRANSPORT_MODE)); 2361 2362 return payloadList; 2363 } 2364 2365 /** 2366 * Validate the received response of initial Create Child SA exchange and return the 2367 * negotiation result. 2368 */ validateAndNegotiateInitChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2369 public static CreateChildResult validateAndNegotiateInitChild( 2370 List<IkePayload> reqPayloads, 2371 List<IkePayload> respPayloads, 2372 @ExchangeType int exchangeType, 2373 @ExchangeType int expectedExchangeType, 2374 boolean expectTransport, 2375 IpSecSpiGenerator ipSecSpiGenerator, 2376 InetAddress remoteAddress) { 2377 2378 return validateAndNegotiateChild( 2379 reqPayloads, 2380 respPayloads, 2381 exchangeType, 2382 expectedExchangeType, 2383 true /*isLocalInit*/, 2384 expectTransport, 2385 ipSecSpiGenerator, 2386 remoteAddress); 2387 } 2388 2389 /** 2390 * Validate the received rekey-create request against locally built response (based on 2391 * previously negotiated Child SA) and return the negotiation result. 2392 */ validateAndNegotiateRekeyChildRequest( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2393 public static CreateChildResult validateAndNegotiateRekeyChildRequest( 2394 List<IkePayload> reqPayloads, 2395 List<IkePayload> respPayloads, 2396 @ExchangeType int exchangeType, 2397 @ExchangeType int expectedExchangeType, 2398 boolean expectTransport, 2399 IpSecSpiGenerator ipSecSpiGenerator, 2400 InetAddress remoteAddress) { 2401 2402 // It is guaranteed that a Rekey-Notify Payload with remote SPI of current Child SA is 2403 // included in the reqPayloads. So we won't validate it again here. 2404 return validateAndNegotiateChild( 2405 reqPayloads, 2406 respPayloads, 2407 exchangeType, 2408 expectedExchangeType, 2409 false /*isLocalInit*/, 2410 expectTransport, 2411 ipSecSpiGenerator, 2412 remoteAddress); 2413 } 2414 2415 /** 2416 * Validate the received rekey-create response against locally built request and previously 2417 * negotiated Child SA, and return the negotiation result. 2418 */ validateAndNegotiateRekeyChildResp( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, ChildSaRecord expectedChildRecord, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2419 public static CreateChildResult validateAndNegotiateRekeyChildResp( 2420 List<IkePayload> reqPayloads, 2421 List<IkePayload> respPayloads, 2422 @ExchangeType int exchangeType, 2423 @ExchangeType int expectedExchangeType, 2424 boolean expectTransport, 2425 ChildSaRecord expectedChildRecord, 2426 IpSecSpiGenerator ipSecSpiGenerator, 2427 InetAddress remoteAddress) { 2428 // Validate rest of payloads and negotiate Child SA. 2429 CreateChildResult childResult = 2430 validateAndNegotiateChild( 2431 reqPayloads, 2432 respPayloads, 2433 exchangeType, 2434 expectedExchangeType, 2435 true /*isLocalInit*/, 2436 expectTransport, 2437 ipSecSpiGenerator, 2438 remoteAddress); 2439 2440 // TODO: Validate new Child SA does not have different Traffic Selectors 2441 2442 return childResult; 2443 } 2444 2445 /** 2446 * Check if SPI of Child SA that is expected to be rekeyed is included in the provided 2447 * payload list. 2448 */ hasRemoteChildSpiForRekey( List<IkePayload> payloads, ChildSaRecord expectedRecord)2449 public static boolean hasRemoteChildSpiForRekey( 2450 List<IkePayload> payloads, ChildSaRecord expectedRecord) { 2451 List<IkeNotifyPayload> notifyPayloads = 2452 IkePayload.getPayloadListForTypeInProvidedList( 2453 IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, payloads); 2454 2455 boolean hasExpectedRekeyNotify = false; 2456 for (IkeNotifyPayload notifyPayload : notifyPayloads) { 2457 if (notifyPayload.notifyType == NOTIFY_TYPE_REKEY_SA 2458 && notifyPayload.spi == expectedRecord.getRemoteSpi()) { 2459 hasExpectedRekeyNotify = true; 2460 break; 2461 } 2462 } 2463 2464 return hasExpectedRekeyNotify; 2465 } 2466 releaseSpiResources(List<IkePayload> reqPayloads)2467 public static void releaseSpiResources(List<IkePayload> reqPayloads) { 2468 if (reqPayloads == null) { 2469 return; 2470 } 2471 2472 IkeSaPayload saPayload = 2473 IkePayload.getPayloadForTypeInProvidedList( 2474 IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); 2475 if (saPayload != null) { 2476 saPayload.releaseChildSpiResourcesIfExists(); 2477 } 2478 } 2479 2480 /** Validate the received payload list and negotiate Child SA. */ validateAndNegotiateChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean isLocalInit, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2481 private static CreateChildResult validateAndNegotiateChild( 2482 List<IkePayload> reqPayloads, 2483 List<IkePayload> respPayloads, 2484 @ExchangeType int exchangeType, 2485 @ExchangeType int expectedExchangeType, 2486 boolean isLocalInit, 2487 boolean expectTransport, 2488 IpSecSpiGenerator ipSecSpiGenerator, 2489 InetAddress remoteAddress) { 2490 List<IkePayload> inboundPayloads = isLocalInit ? respPayloads : reqPayloads; 2491 2492 try { 2493 validatePayloadAndExchangeType( 2494 inboundPayloads, 2495 isLocalInit /*isResp*/, 2496 exchangeType, 2497 expectedExchangeType); 2498 } catch (InvalidSyntaxException e) { 2499 return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e); 2500 } 2501 2502 List<IkeNotifyPayload> notifyPayloads = 2503 IkePayload.getPayloadListForTypeInProvidedList( 2504 IkePayload.PAYLOAD_TYPE_NOTIFY, 2505 IkeNotifyPayload.class, 2506 inboundPayloads); 2507 2508 boolean hasTransportNotify = false; 2509 for (IkeNotifyPayload notify : notifyPayloads) { 2510 if (notify.isErrorNotify()) { 2511 try { 2512 IkeProtocolException exception = notify.validateAndBuildIkeException(); 2513 if (isLocalInit) { 2514 return new CreateChildResult( 2515 CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY, exception); 2516 } else { 2517 logw("Received unexpected error notification: " + notify.notifyType); 2518 } 2519 } catch (InvalidSyntaxException e) { 2520 return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e); 2521 } 2522 } 2523 2524 switch (notify.notifyType) { 2525 case IkeNotifyPayload.NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE: 2526 // TODO: Store it as part of negotiation results that can be retrieved 2527 // by users. 2528 break; 2529 case IkeNotifyPayload.NOTIFY_TYPE_IPCOMP_SUPPORTED: 2530 // Ignore 2531 break; 2532 case IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE: 2533 hasTransportNotify = true; 2534 break; 2535 case IkeNotifyPayload.NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED: 2536 // Ignore 2537 break; 2538 case IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA: 2539 // Handled in Rekey State. Ignore here. 2540 break; 2541 default: 2542 // Unknown and unexpected status notifications are ignored as per RFC7296. 2543 logw( 2544 "Received unknown or unexpected status notifications with notify" 2545 + " type: " 2546 + notify.notifyType); 2547 } 2548 } 2549 2550 Pair<ChildProposal, ChildProposal> childProposalPair = null; 2551 try { 2552 IkeSaPayload reqSaPayload = 2553 IkePayload.getPayloadForTypeInProvidedList( 2554 IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); 2555 IkeSaPayload respSaPayload = 2556 IkePayload.getPayloadForTypeInProvidedList( 2557 IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads); 2558 2559 // This method either throws exception or returns non-null pair that contains two 2560 // valid {@link ChildProposal} both with a {@link SecurityParameterIndex} allocated 2561 // inside. 2562 childProposalPair = 2563 IkeSaPayload.getVerifiedNegotiatedChildProposalPair( 2564 reqSaPayload, respSaPayload, ipSecSpiGenerator, remoteAddress); 2565 ChildSaProposal saProposal = childProposalPair.second.saProposal; 2566 2567 validateKePayloads(inboundPayloads, isLocalInit /*isResp*/, saProposal); 2568 2569 if (expectTransport != hasTransportNotify) { 2570 throw new NoValidProposalChosenException( 2571 "Failed the negotiation on Child SA mode (conflicting modes chosen)."); 2572 } 2573 2574 Pair<IkeTrafficSelector[], IkeTrafficSelector[]> tsPair = 2575 validateAndGetNegotiatedTsPair(reqPayloads, respPayloads); 2576 2577 return new CreateChildResult( 2578 childProposalPair.first.getChildSpiResource(), 2579 childProposalPair.second.getChildSpiResource(), 2580 saProposal, 2581 tsPair.first, 2582 tsPair.second); 2583 } catch (IkeProtocolException 2584 | ResourceUnavailableException 2585 | SpiUnavailableException e) { 2586 if (childProposalPair != null) { 2587 childProposalPair.first.getChildSpiResource().close(); 2588 childProposalPair.second.getChildSpiResource().close(); 2589 } 2590 2591 if (e instanceof InvalidSyntaxException) { 2592 return new CreateChildResult( 2593 CREATE_STATUS_CHILD_ERROR_INVALID_MSG, (InvalidSyntaxException) e); 2594 } else if (e instanceof IkeProtocolException) { 2595 return new CreateChildResult( 2596 CREATE_STATUS_CHILD_ERROR_INVALID_MSG, 2597 new InvalidSyntaxException( 2598 "Processing error in received Create Child response", e)); 2599 } else { 2600 return new CreateChildResult( 2601 CREATE_STATUS_CHILD_ERROR_INVALID_MSG, wrapAsIkeException(e)); 2602 } 2603 } 2604 } 2605 2606 // Validate syntax to make sure all necessary payloads exist and exchange type is correct. validatePayloadAndExchangeType( List<IkePayload> inboundPayloads, boolean isResp, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType)2607 private static void validatePayloadAndExchangeType( 2608 List<IkePayload> inboundPayloads, 2609 boolean isResp, 2610 @ExchangeType int exchangeType, 2611 @ExchangeType int expectedExchangeType) 2612 throws InvalidSyntaxException { 2613 boolean hasSaPayload = false; 2614 boolean hasKePayload = false; 2615 boolean hasNoncePayload = false; 2616 boolean hasTsInitPayload = false; 2617 boolean hasTsRespPayload = false; 2618 boolean hasErrorNotify = false; 2619 2620 for (IkePayload payload : inboundPayloads) { 2621 switch (payload.payloadType) { 2622 case PAYLOAD_TYPE_SA: 2623 hasSaPayload = true; 2624 break; 2625 case PAYLOAD_TYPE_KE: 2626 // Could not decide if KE Payload MUST or MUST NOT be included until SA 2627 // negotiation is done. 2628 hasKePayload = true; 2629 break; 2630 case PAYLOAD_TYPE_NONCE: 2631 hasNoncePayload = true; 2632 break; 2633 case PAYLOAD_TYPE_TS_INITIATOR: 2634 hasTsInitPayload = true; 2635 break; 2636 case PAYLOAD_TYPE_TS_RESPONDER: 2637 hasTsRespPayload = true; 2638 break; 2639 case PAYLOAD_TYPE_NOTIFY: 2640 if (((IkeNotifyPayload) payload).isErrorNotify()) hasErrorNotify = true; 2641 // Do not have enough context to handle all notifications. Handle them 2642 // together in higher layer. 2643 break; 2644 case PAYLOAD_TYPE_CP: 2645 // Handled in child creation state. Note Child Session can only handle 2646 // Config Payload in initial creation and can only handle a Config Reply. 2647 // For interoperability, Config Payloads received in rekey creation 2648 // or with other config types will be ignored. 2649 break; 2650 default: 2651 logw( 2652 "Received unexpected payload in Create Child SA message. Payload" 2653 + " type: " 2654 + payload.payloadType); 2655 } 2656 } 2657 2658 // Do not need to check exchange type of a request because it has been already verified 2659 // in IkeSessionStateMachine 2660 if (isResp 2661 && exchangeType != expectedExchangeType 2662 && exchangeType != EXCHANGE_TYPE_INFORMATIONAL) { 2663 throw new InvalidSyntaxException("Received invalid exchange type: " + exchangeType); 2664 } 2665 2666 if (exchangeType == EXCHANGE_TYPE_INFORMATIONAL 2667 && (hasSaPayload 2668 || hasKePayload 2669 || hasNoncePayload 2670 || hasTsInitPayload 2671 || hasTsRespPayload)) { 2672 logw( 2673 "Unexpected payload found in an INFORMATIONAL message: SA, KE, Nonce," 2674 + " TS-Initiator or TS-Responder"); 2675 } 2676 2677 if (isResp 2678 && !hasErrorNotify 2679 && (!hasSaPayload 2680 || !hasNoncePayload 2681 || !hasTsInitPayload 2682 || !hasTsRespPayload)) { 2683 throw new InvalidSyntaxException( 2684 "SA, Nonce, TS-Initiator or TS-Responder missing."); 2685 } 2686 } 2687 2688 private static Pair<IkeTrafficSelector[], IkeTrafficSelector[]> validateAndGetNegotiatedTsPair( List<IkePayload> reqPayloads, List<IkePayload> respPayloads)2689 validateAndGetNegotiatedTsPair( 2690 List<IkePayload> reqPayloads, List<IkePayload> respPayloads) 2691 throws TsUnacceptableException { 2692 IkeTrafficSelector[] initTs = 2693 validateAndGetNegotiatedTs(reqPayloads, respPayloads, true /*isInitTs*/); 2694 IkeTrafficSelector[] respTs = 2695 validateAndGetNegotiatedTs(reqPayloads, respPayloads, false /*isInitTs*/); 2696 2697 return new Pair<IkeTrafficSelector[], IkeTrafficSelector[]>(initTs, respTs); 2698 } 2699 validateAndGetNegotiatedTs( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, boolean isInitTs)2700 private static IkeTrafficSelector[] validateAndGetNegotiatedTs( 2701 List<IkePayload> reqPayloads, List<IkePayload> respPayloads, boolean isInitTs) 2702 throws TsUnacceptableException { 2703 int tsType = isInitTs ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER; 2704 IkeTsPayload reqPayload = 2705 IkePayload.getPayloadForTypeInProvidedList( 2706 tsType, IkeTsPayload.class, reqPayloads); 2707 IkeTsPayload respPayload = 2708 IkePayload.getPayloadForTypeInProvidedList( 2709 tsType, IkeTsPayload.class, respPayloads); 2710 2711 if (!reqPayload.contains(respPayload)) { 2712 throw new TsUnacceptableException(); 2713 } 2714 2715 // It is guaranteed by decoding inbound TS Payload and constructing outbound TS Payload 2716 // that each TS Payload has at least one IkeTrafficSelector. 2717 return respPayload.trafficSelectors; 2718 } 2719 2720 @VisibleForTesting validateKePayloads( List<IkePayload> inboundPayloads, boolean isResp, ChildSaProposal negotiatedProposal)2721 static void validateKePayloads( 2722 List<IkePayload> inboundPayloads, 2723 boolean isResp, 2724 ChildSaProposal negotiatedProposal) 2725 throws IkeProtocolException { 2726 DhGroupTransform[] dhTransforms = negotiatedProposal.getDhGroupTransforms(); 2727 2728 if (dhTransforms.length > 1) { 2729 throw new IllegalArgumentException( 2730 "Found multiple DH Group Transforms in the negotiated SA proposal"); 2731 } 2732 boolean expectKePayload = 2733 dhTransforms.length == 1 && dhTransforms[0].id != DH_GROUP_NONE; 2734 2735 IkeKePayload kePayload = 2736 IkePayload.getPayloadForTypeInProvidedList( 2737 PAYLOAD_TYPE_KE, IkeKePayload.class, inboundPayloads); 2738 2739 if (expectKePayload && (kePayload == null || dhTransforms[0].id != kePayload.dhGroup)) { 2740 if (isResp) { 2741 throw new InvalidSyntaxException( 2742 "KE Payload missing or has mismatched DH Group with the negotiated" 2743 + " proposal."); 2744 } else { 2745 throw new InvalidKeException(dhTransforms[0].id); 2746 } 2747 2748 } else if (!expectKePayload && kePayload != null && isResp) { 2749 // It is valid when the remote request proposed multiple DH Groups with a KE 2750 // payload, and the responder chose DH_GROUP_NONE. 2751 throw new InvalidSyntaxException("Received unexpected KE Payload."); 2752 } 2753 } 2754 logw(String s)2755 private static void logw(String s) { 2756 getIkeLog().w(TAG, s); 2757 } 2758 } 2759 2760 @Retention(RetentionPolicy.SOURCE) 2761 @IntDef({ 2762 CREATE_STATUS_OK, 2763 CREATE_STATUS_CHILD_ERROR_INVALID_MSG, 2764 CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY 2765 }) 2766 @interface CreateStatus {} 2767 2768 /** The Child SA negotiation succeeds. */ 2769 private static final int CREATE_STATUS_OK = 0; 2770 /** The inbound message is invalid in Child negotiation but is non-fatal for IKE Session. */ 2771 private static final int CREATE_STATUS_CHILD_ERROR_INVALID_MSG = 1; 2772 /** The inbound message includes error notification that failed the Child negotiation. */ 2773 private static final int CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY = 2; 2774 2775 private static class CreateChildResult { 2776 @CreateStatus public final int status; 2777 public final SecurityParameterIndex initSpi; 2778 public final SecurityParameterIndex respSpi; 2779 public final ChildSaProposal negotiatedProposal; 2780 public final IkeTrafficSelector[] initTs; 2781 public final IkeTrafficSelector[] respTs; 2782 public final IkeException exception; 2783 CreateChildResult( @reateStatus int status, SecurityParameterIndex initSpi, SecurityParameterIndex respSpi, ChildSaProposal negotiatedProposal, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs, IkeException exception)2784 private CreateChildResult( 2785 @CreateStatus int status, 2786 SecurityParameterIndex initSpi, 2787 SecurityParameterIndex respSpi, 2788 ChildSaProposal negotiatedProposal, 2789 IkeTrafficSelector[] initTs, 2790 IkeTrafficSelector[] respTs, 2791 IkeException exception) { 2792 this.status = status; 2793 this.initSpi = initSpi; 2794 this.respSpi = respSpi; 2795 this.negotiatedProposal = negotiatedProposal; 2796 this.initTs = initTs; 2797 this.respTs = respTs; 2798 this.exception = exception; 2799 } 2800 2801 /* Construct a CreateChildResult instance for a successful case. */ CreateChildResult( SecurityParameterIndex initSpi, SecurityParameterIndex respSpi, ChildSaProposal negotiatedProposal, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs)2802 CreateChildResult( 2803 SecurityParameterIndex initSpi, 2804 SecurityParameterIndex respSpi, 2805 ChildSaProposal negotiatedProposal, 2806 IkeTrafficSelector[] initTs, 2807 IkeTrafficSelector[] respTs) { 2808 this( 2809 CREATE_STATUS_OK, 2810 initSpi, 2811 respSpi, 2812 negotiatedProposal, 2813 initTs, 2814 respTs, 2815 null /*exception*/); 2816 } 2817 2818 /** Construct a CreateChildResult instance for an error case. */ CreateChildResult(@reateStatus int status, IkeException exception)2819 CreateChildResult(@CreateStatus int status, IkeException exception) { 2820 this( 2821 status, 2822 null /*initSpi*/, 2823 null /*respSpi*/, 2824 null /*negotiatedProposal*/, 2825 null /*initTs*/, 2826 null /*respTs*/, 2827 exception); 2828 } 2829 } 2830 } 2831