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