• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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