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 17 package android.net.ipsec.ike; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.SuppressLint; 22 import android.annotation.SystemApi; 23 import android.net.InetAddresses; 24 import android.os.PersistableBundle; 25 26 import com.android.server.vcn.util.PersistableBundleUtils; 27 28 import java.net.InetAddress; 29 import java.util.Arrays; 30 import java.util.LinkedList; 31 import java.util.List; 32 import java.util.Objects; 33 import java.util.concurrent.TimeUnit; 34 35 /** 36 * ChildSessionParams is an abstract class that represents proposed configurations for negotiating a 37 * Child Session. 38 * 39 * <p>Note that references to negotiated configurations will be held, and the same parameters will 40 * be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors. 41 * 42 * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE 43 * payload in a request will use the first DH group from the first user provided SA proposal (or the 44 * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on 45 * the SA proposal negotiation result. 46 * 47 * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group 48 * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library 49 * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs 50 * to have the same DH group as that of the IKE Session, then they need to explicitly set the same 51 * DH Group in ChildSessionParams. 52 * 53 * <p>@see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams} 54 */ 55 public abstract class ChildSessionParams { 56 /** @hide */ 57 protected static final int CHILD_HARD_LIFETIME_SEC_MINIMUM = 300; // 5 minutes 58 /** @hide */ 59 protected static final int CHILD_HARD_LIFETIME_SEC_MAXIMUM = 14400; // 4 hours 60 /** @hide */ 61 protected static final int CHILD_HARD_LIFETIME_SEC_DEFAULT = 7200; // 2 hours 62 63 /** @hide */ 64 protected static final int CHILD_SOFT_LIFETIME_SEC_MINIMUM = 120; // 2 minutes 65 /** @hide */ 66 protected static final int CHILD_SOFT_LIFETIME_SEC_DEFAULT = 3600; // 1 hour 67 68 /** @hide */ 69 protected static final int CHILD_LIFETIME_MARGIN_SEC_MINIMUM = 70 (int) TimeUnit.MINUTES.toSeconds(1L); 71 72 @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV4; 73 @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV6; 74 75 static { 76 DEFAULT_TRAFFIC_SELECTOR_IPV4 = 77 buildDefaultTrafficSelector( 78 IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE); 79 DEFAULT_TRAFFIC_SELECTOR_IPV6 = 80 buildDefaultTrafficSelector( 81 IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE); 82 } 83 84 private static final String IS_TRANPORT_KEY = "mIsTransport"; 85 /** @hide */ 86 protected static final String INBOUND_TS_KEY = "mInboundTrafficSelectors"; 87 /** @hide */ 88 protected static final String OUTBOUND_TS_KEY = "mOutboundTrafficSelectors"; 89 /** @hide */ 90 protected static final String SA_PROPOSALS_KEY = "mSaProposals"; 91 /** @hide */ 92 protected static final String HARD_LIFETIME_SEC_KEY = "mHardLifetimeSec"; 93 /** @hide */ 94 protected static final String SOFT_LIFETIME_SEC_KEY = "mSoftLifetimeSec"; 95 96 @NonNull private final IkeTrafficSelector[] mInboundTrafficSelectors; 97 @NonNull private final IkeTrafficSelector[] mOutboundTrafficSelectors; 98 @NonNull private final ChildSaProposal[] mSaProposals; 99 100 private final int mHardLifetimeSec; 101 private final int mSoftLifetimeSec; 102 103 private final boolean mIsTransport; 104 105 /** @hide */ ChildSessionParams( IkeTrafficSelector[] inboundTs, IkeTrafficSelector[] outboundTs, ChildSaProposal[] proposals, int hardLifetimeSec, int softLifetimeSec, boolean isTransport)106 protected ChildSessionParams( 107 IkeTrafficSelector[] inboundTs, 108 IkeTrafficSelector[] outboundTs, 109 ChildSaProposal[] proposals, 110 int hardLifetimeSec, 111 int softLifetimeSec, 112 boolean isTransport) { 113 mInboundTrafficSelectors = inboundTs; 114 mOutboundTrafficSelectors = outboundTs; 115 mSaProposals = proposals; 116 mHardLifetimeSec = hardLifetimeSec; 117 mSoftLifetimeSec = softLifetimeSec; 118 mIsTransport = isTransport; 119 } 120 121 /** 122 * Constructs this object by deserializing a PersistableBundle 123 * 124 * @hide 125 */ 126 @NonNull fromPersistableBundle(@onNull PersistableBundle in)127 public static ChildSessionParams fromPersistableBundle(@NonNull PersistableBundle in) { 128 Objects.requireNonNull(in, "PersistableBundle is null"); 129 130 if (in.getBoolean(IS_TRANPORT_KEY)) { 131 return TransportModeChildSessionParams.fromPersistableBundle(in); 132 } else { 133 return TunnelModeChildSessionParams.fromPersistableBundle(in); 134 } 135 } 136 137 /** 138 * Serializes this object to a PersistableBundle 139 * 140 * @hide 141 */ 142 @NonNull toPersistableBundle()143 public PersistableBundle toPersistableBundle() { 144 final PersistableBundle result = new PersistableBundle(); 145 146 result.putBoolean(IS_TRANPORT_KEY, mIsTransport); 147 148 PersistableBundle saProposalBundle = 149 PersistableBundleUtils.fromList( 150 Arrays.asList(mSaProposals), ChildSaProposal::toPersistableBundle); 151 result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle); 152 153 PersistableBundle inTsBundle = 154 PersistableBundleUtils.fromList( 155 Arrays.asList(mInboundTrafficSelectors), 156 IkeTrafficSelector::toPersistableBundle); 157 result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle); 158 159 PersistableBundle outTsBundle = 160 PersistableBundleUtils.fromList( 161 Arrays.asList(mOutboundTrafficSelectors), 162 IkeTrafficSelector::toPersistableBundle); 163 result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle); 164 165 result.putInt(HARD_LIFETIME_SEC_KEY, mHardLifetimeSec); 166 result.putInt(SOFT_LIFETIME_SEC_KEY, mSoftLifetimeSec); 167 return result; 168 } 169 170 /** @hide */ getProposalsFromPersistableBundle(PersistableBundle in)171 protected static List<ChildSaProposal> getProposalsFromPersistableBundle(PersistableBundle in) { 172 PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY); 173 Objects.requireNonNull(proposalBundle, "Value for key " + SA_PROPOSALS_KEY + " was null"); 174 return PersistableBundleUtils.toList( 175 proposalBundle, ChildSaProposal::fromPersistableBundle); 176 } 177 178 /** @hide */ getTsFromPersistableBundle( PersistableBundle in, String key)179 protected static List<IkeTrafficSelector> getTsFromPersistableBundle( 180 PersistableBundle in, String key) { 181 PersistableBundle tsBundle = in.getPersistableBundle(key); 182 Objects.requireNonNull(tsBundle, "Value for key " + key + " was null"); 183 return PersistableBundleUtils.toList(tsBundle, IkeTrafficSelector::fromPersistableBundle); 184 } 185 186 /** 187 * Retrieves configured inbound traffic selectors 188 * 189 * <p>@see {@link 190 * TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)} or 191 * {@link 192 * TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)} 193 */ 194 @NonNull getInboundTrafficSelectors()195 public List<IkeTrafficSelector> getInboundTrafficSelectors() { 196 return Arrays.asList(mInboundTrafficSelectors); 197 } 198 199 /** 200 * Retrieves configured outbound traffic selectors 201 * 202 * <p>@see {@link 203 * TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)} or 204 * {@link 205 * TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)} 206 */ 207 @NonNull getOutboundTrafficSelectors()208 public List<IkeTrafficSelector> getOutboundTrafficSelectors() { 209 return Arrays.asList(mOutboundTrafficSelectors); 210 } 211 212 /** 213 * Retrieves all ChildSaProposals configured 214 * 215 * @deprecated Callers should use {@link #getChildSaProposals()}. This method is deprecated 216 * because its name does not match the return type, 217 * @hide 218 */ 219 @Deprecated 220 @SystemApi 221 @NonNull getSaProposals()222 public List<ChildSaProposal> getSaProposals() { 223 return getChildSaProposals(); 224 } 225 226 /** Retrieves all ChildSaProposals configured */ 227 @NonNull getChildSaProposals()228 public List<ChildSaProposal> getChildSaProposals() { 229 return Arrays.asList(mSaProposals); 230 } 231 232 /** Retrieves hard lifetime in seconds */ 233 // Use "second" because smaller unit won't make sense to describe a rekey interval. 234 @SuppressLint("MethodNameUnits") 235 @IntRange(from = CHILD_HARD_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM) getHardLifetimeSeconds()236 public int getHardLifetimeSeconds() { 237 return mHardLifetimeSec; 238 } 239 240 /** Retrieves soft lifetime in seconds */ 241 // Use "second" because smaller unit won't make sense to describe a rekey interval. 242 @SuppressLint("MethodNameUnits") 243 @IntRange(from = CHILD_SOFT_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM) getSoftLifetimeSeconds()244 public int getSoftLifetimeSeconds() { 245 return mSoftLifetimeSec; 246 } 247 248 /** @hide */ getInboundTrafficSelectorsInternal()249 public IkeTrafficSelector[] getInboundTrafficSelectorsInternal() { 250 return Arrays.copyOf(mInboundTrafficSelectors, mInboundTrafficSelectors.length); 251 } 252 253 /** @hide */ getOutboundTrafficSelectorsInternal()254 public IkeTrafficSelector[] getOutboundTrafficSelectorsInternal() { 255 return Arrays.copyOf(mOutboundTrafficSelectors, mOutboundTrafficSelectors.length); 256 } 257 258 /** @hide */ getSaProposalsInternal()259 public ChildSaProposal[] getSaProposalsInternal() { 260 return Arrays.copyOf(mSaProposals, mSaProposals.length); 261 } 262 263 /** @hide */ getHardLifetimeMsInternal()264 public long getHardLifetimeMsInternal() { 265 return TimeUnit.SECONDS.toMillis((long) mHardLifetimeSec); 266 } 267 268 /** @hide */ getSoftLifetimeMsInternal()269 public long getSoftLifetimeMsInternal() { 270 return TimeUnit.SECONDS.toMillis((long) mSoftLifetimeSec); 271 } 272 273 /** @hide */ isTransportMode()274 public boolean isTransportMode() { 275 return mIsTransport; 276 } 277 278 @Override hashCode()279 public int hashCode() { 280 return Objects.hash( 281 Arrays.hashCode(mInboundTrafficSelectors), 282 Arrays.hashCode(mOutboundTrafficSelectors), 283 Arrays.hashCode(mSaProposals), 284 mHardLifetimeSec, 285 mSoftLifetimeSec, 286 mIsTransport); 287 } 288 289 @Override equals(Object o)290 public boolean equals(Object o) { 291 if (!(o instanceof ChildSessionParams)) { 292 return false; 293 } 294 295 ChildSessionParams other = (ChildSessionParams) o; 296 297 return Arrays.equals(mInboundTrafficSelectors, other.mInboundTrafficSelectors) 298 && Arrays.equals(mOutboundTrafficSelectors, other.mOutboundTrafficSelectors) 299 && Arrays.equals(mSaProposals, other.mSaProposals) 300 && mHardLifetimeSec == other.mHardLifetimeSec 301 && mSoftLifetimeSec == other.mSoftLifetimeSec 302 && mIsTransport == other.mIsTransport; 303 } 304 305 /** 306 * This class represents common information for Child Session Parameters Builders. 307 * 308 * @hide 309 */ 310 protected abstract static class Builder { 311 @NonNull protected final List<IkeTrafficSelector> mInboundTsList = new LinkedList<>(); 312 @NonNull protected final List<IkeTrafficSelector> mOutboundTsList = new LinkedList<>(); 313 @NonNull protected final List<SaProposal> mSaProposalList = new LinkedList<>(); 314 315 protected int mHardLifetimeSec = CHILD_HARD_LIFETIME_SEC_DEFAULT; 316 protected int mSoftLifetimeSec = CHILD_SOFT_LIFETIME_SEC_DEFAULT; 317 318 /** Package private constructor */ Builder()319 Builder() {} 320 321 /** Package private constructor */ Builder(@onNull ChildSessionParams childParams)322 Builder(@NonNull ChildSessionParams childParams) { 323 Objects.requireNonNull(childParams, "childParams was null"); 324 325 mInboundTsList.addAll(childParams.getInboundTrafficSelectors()); 326 mOutboundTsList.addAll(childParams.getOutboundTrafficSelectors()); 327 mSaProposalList.addAll(childParams.getSaProposals()); 328 mHardLifetimeSec = childParams.getHardLifetimeSeconds(); 329 mSoftLifetimeSec = childParams.getSoftLifetimeSeconds(); 330 } 331 addProposal(@onNull ChildSaProposal proposal)332 protected void addProposal(@NonNull ChildSaProposal proposal) { 333 mSaProposalList.add(proposal); 334 } 335 addInboundTs(@onNull IkeTrafficSelector trafficSelector)336 protected void addInboundTs(@NonNull IkeTrafficSelector trafficSelector) { 337 mInboundTsList.add(trafficSelector); 338 } 339 addOutboundTs(@onNull IkeTrafficSelector trafficSelector)340 protected void addOutboundTs(@NonNull IkeTrafficSelector trafficSelector) { 341 mOutboundTsList.add(trafficSelector); 342 } 343 validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec)344 protected void validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec) { 345 if (hardLifetimeSec < CHILD_HARD_LIFETIME_SEC_MINIMUM 346 || hardLifetimeSec > CHILD_HARD_LIFETIME_SEC_MAXIMUM 347 || softLifetimeSec < CHILD_SOFT_LIFETIME_SEC_MINIMUM 348 || hardLifetimeSec - softLifetimeSec < CHILD_LIFETIME_MARGIN_SEC_MINIMUM) { 349 throw new IllegalArgumentException("Invalid lifetime value"); 350 } 351 } 352 validateOrThrow()353 protected void validateOrThrow() { 354 if (mSaProposalList.isEmpty()) { 355 throw new IllegalArgumentException( 356 "ChildSessionParams requires at least one Child SA proposal."); 357 } 358 } 359 addDefaultTsIfNotConfigured()360 protected void addDefaultTsIfNotConfigured() { 361 if (mInboundTsList.isEmpty()) { 362 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4); 363 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6); 364 } 365 366 if (mOutboundTsList.isEmpty()) { 367 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4); 368 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6); 369 } 370 } 371 } 372 buildDefaultTrafficSelector( @keTrafficSelector.TrafficSelectorType int tsType)373 private static IkeTrafficSelector buildDefaultTrafficSelector( 374 @IkeTrafficSelector.TrafficSelectorType int tsType) { 375 int startPort = IkeTrafficSelector.PORT_NUMBER_MIN; 376 int endPort = IkeTrafficSelector.PORT_NUMBER_MAX; 377 InetAddress startAddress = null; 378 InetAddress endAddress = null; 379 switch (tsType) { 380 case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: 381 startAddress = InetAddresses.parseNumericAddress("0.0.0.0"); 382 endAddress = InetAddresses.parseNumericAddress("255.255.255.255"); 383 break; 384 case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: 385 startAddress = InetAddresses.parseNumericAddress("::"); 386 endAddress = 387 InetAddresses.parseNumericAddress( 388 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); 389 break; 390 default: 391 throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType); 392 } 393 394 return new IkeTrafficSelector(tsType, startPort, endPort, startAddress, endAddress); 395 } 396 } 397