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