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