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