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 
25 import java.net.InetAddress;
26 import java.util.Arrays;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.concurrent.TimeUnit;
30 
31 /**
32  * ChildSessionParams is an abstract class that represents proposed configurations for negotiating a
33  * Child Session.
34  *
35  * <p>Note that references to negotiated configurations will be held, and the same parameters will
36  * be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors.
37  *
38  * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE
39  * payload in a request will use the first DH group from the first user provided SA proposal (or the
40  * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on
41  * the SA proposal negotiation result.
42  *
43  * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group
44  * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library
45  * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs
46  * to have the same DH group as that of the IKE Session, then they need to explicitly set the same
47  * DH Group in ChildSessionParams.
48  *
49  * @see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams}
50  * @hide
51  */
52 @SystemApi
53 public abstract class ChildSessionParams {
54     /** @hide */
55     protected static final int CHILD_HARD_LIFETIME_SEC_MINIMUM = 300; // 5 minutes
56     /** @hide */
57     protected static final int CHILD_HARD_LIFETIME_SEC_MAXIMUM = 14400; // 4 hours
58     /** @hide */
59     protected static final int CHILD_HARD_LIFETIME_SEC_DEFAULT = 7200; // 2 hours
60 
61     /** @hide */
62     protected static final int CHILD_SOFT_LIFETIME_SEC_MINIMUM = 120; // 2 minutes
63     /** @hide */
64     protected static final int CHILD_SOFT_LIFETIME_SEC_DEFAULT = 3600; // 1 hour
65 
66     /** @hide */
67     protected static final int CHILD_LIFETIME_MARGIN_SEC_MINIMUM =
68             (int) TimeUnit.MINUTES.toSeconds(1L);
69 
70     @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV4;
71     @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV6;
72 
73     static {
74         DEFAULT_TRAFFIC_SELECTOR_IPV4 =
75                 buildDefaultTrafficSelector(
76                         IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE);
77         DEFAULT_TRAFFIC_SELECTOR_IPV6 =
78                 buildDefaultTrafficSelector(
79                         IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE);
80     }
81 
82     @NonNull private final IkeTrafficSelector[] mInboundTrafficSelectors;
83     @NonNull private final IkeTrafficSelector[] mOutboundTrafficSelectors;
84     @NonNull private final ChildSaProposal[] mSaProposals;
85 
86     private final int mHardLifetimeSec;
87     private final int mSoftLifetimeSec;
88 
89     private final boolean mIsTransport;
90 
91     /** @hide */
ChildSessionParams( IkeTrafficSelector[] inboundTs, IkeTrafficSelector[] outboundTs, ChildSaProposal[] proposals, int hardLifetimeSec, int softLifetimeSec, boolean isTransport)92     protected ChildSessionParams(
93             IkeTrafficSelector[] inboundTs,
94             IkeTrafficSelector[] outboundTs,
95             ChildSaProposal[] proposals,
96             int hardLifetimeSec,
97             int softLifetimeSec,
98             boolean isTransport) {
99         mInboundTrafficSelectors = inboundTs;
100         mOutboundTrafficSelectors = outboundTs;
101         mSaProposals = proposals;
102         mHardLifetimeSec = hardLifetimeSec;
103         mSoftLifetimeSec = softLifetimeSec;
104         mIsTransport = isTransport;
105     }
106 
107     /**
108      * Retrieves configured inbound traffic selectors
109      *
110      * <p>@see {@link
111      * TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)} or
112      * {@link
113      * TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)}
114      */
115     @NonNull
getInboundTrafficSelectors()116     public List<IkeTrafficSelector> getInboundTrafficSelectors() {
117         return Arrays.asList(mInboundTrafficSelectors);
118     }
119 
120     /**
121      * Retrieves configured outbound traffic selectors
122      *
123      * <p>@see {@link
124      * TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)} or
125      * {@link
126      * TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)}
127      */
128     @NonNull
getOutboundTrafficSelectors()129     public List<IkeTrafficSelector> getOutboundTrafficSelectors() {
130         return Arrays.asList(mOutboundTrafficSelectors);
131     }
132 
133     /** Retrieves all ChildSaProposals configured */
134     @NonNull
getSaProposals()135     public List<ChildSaProposal> getSaProposals() {
136         return Arrays.asList(mSaProposals);
137     }
138 
139     /** Retrieves hard lifetime in seconds */
140     // Use "second" because smaller unit won't make sense to describe a rekey interval.
141     @SuppressLint("MethodNameUnits")
142     @IntRange(from = CHILD_HARD_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM)
getHardLifetimeSeconds()143     public int getHardLifetimeSeconds() {
144         return mHardLifetimeSec;
145     }
146 
147     /** Retrieves soft lifetime in seconds */
148     // Use "second" because smaller unit won't make sense to describe a rekey interval.
149     @SuppressLint("MethodNameUnits")
150     @IntRange(from = CHILD_SOFT_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM)
getSoftLifetimeSeconds()151     public int getSoftLifetimeSeconds() {
152         return mSoftLifetimeSec;
153     }
154 
155     /** @hide */
getInboundTrafficSelectorsInternal()156     public IkeTrafficSelector[] getInboundTrafficSelectorsInternal() {
157         return mInboundTrafficSelectors;
158     }
159 
160     /** @hide */
getOutboundTrafficSelectorsInternal()161     public IkeTrafficSelector[] getOutboundTrafficSelectorsInternal() {
162         return mOutboundTrafficSelectors;
163     }
164 
165     /** @hide */
getSaProposalsInternal()166     public ChildSaProposal[] getSaProposalsInternal() {
167         return mSaProposals;
168     }
169 
170     /** @hide */
getHardLifetimeMsInternal()171     public long getHardLifetimeMsInternal() {
172         return TimeUnit.SECONDS.toMillis((long) mHardLifetimeSec);
173     }
174 
175     /** @hide */
getSoftLifetimeMsInternal()176     public long getSoftLifetimeMsInternal() {
177         return TimeUnit.SECONDS.toMillis((long) mSoftLifetimeSec);
178     }
179 
180     /** @hide */
isTransportMode()181     public boolean isTransportMode() {
182         return mIsTransport;
183     }
184 
185     /**
186      * This class represents common information for Child Session Parameters Builders.
187      *
188      * @hide
189      */
190     protected abstract static class Builder {
191         @NonNull protected final List<IkeTrafficSelector> mInboundTsList = new LinkedList<>();
192         @NonNull protected final List<IkeTrafficSelector> mOutboundTsList = new LinkedList<>();
193         @NonNull protected final List<SaProposal> mSaProposalList = new LinkedList<>();
194 
195         protected int mHardLifetimeSec = CHILD_HARD_LIFETIME_SEC_DEFAULT;
196         protected int mSoftLifetimeSec = CHILD_SOFT_LIFETIME_SEC_DEFAULT;
197 
addProposal(@onNull ChildSaProposal proposal)198         protected void addProposal(@NonNull ChildSaProposal proposal) {
199             mSaProposalList.add(proposal);
200         }
201 
addInboundTs(@onNull IkeTrafficSelector trafficSelector)202         protected void addInboundTs(@NonNull IkeTrafficSelector trafficSelector) {
203             mInboundTsList.add(trafficSelector);
204         }
205 
addOutboundTs(@onNull IkeTrafficSelector trafficSelector)206         protected void addOutboundTs(@NonNull IkeTrafficSelector trafficSelector) {
207             mOutboundTsList.add(trafficSelector);
208         }
209 
validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec)210         protected void validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec) {
211             if (hardLifetimeSec < CHILD_HARD_LIFETIME_SEC_MINIMUM
212                     || hardLifetimeSec > CHILD_HARD_LIFETIME_SEC_MAXIMUM
213                     || softLifetimeSec < CHILD_SOFT_LIFETIME_SEC_MINIMUM
214                     || hardLifetimeSec - softLifetimeSec < CHILD_LIFETIME_MARGIN_SEC_MINIMUM) {
215                 throw new IllegalArgumentException("Invalid lifetime value");
216             }
217         }
218 
validateOrThrow()219         protected void validateOrThrow() {
220             if (mSaProposalList.isEmpty()) {
221                 throw new IllegalArgumentException(
222                         "ChildSessionParams requires at least one Child SA proposal.");
223             }
224         }
225 
addDefaultTsIfNotConfigured()226         protected void addDefaultTsIfNotConfigured() {
227             if (mInboundTsList.isEmpty()) {
228                 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4);
229                 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6);
230             }
231 
232             if (mOutboundTsList.isEmpty()) {
233                 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4);
234                 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6);
235             }
236         }
237     }
238 
buildDefaultTrafficSelector( @keTrafficSelector.TrafficSelectorType int tsType)239     private static IkeTrafficSelector buildDefaultTrafficSelector(
240             @IkeTrafficSelector.TrafficSelectorType int tsType) {
241         int startPort = IkeTrafficSelector.PORT_NUMBER_MIN;
242         int endPort = IkeTrafficSelector.PORT_NUMBER_MAX;
243         InetAddress startAddress = null;
244         InetAddress endAddress = null;
245         switch (tsType) {
246             case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE:
247                 startAddress = InetAddresses.parseNumericAddress("0.0.0.0");
248                 endAddress = InetAddresses.parseNumericAddress("255.255.255.255");
249                 break;
250             case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE:
251                 startAddress = InetAddresses.parseNumericAddress("::");
252                 endAddress =
253                         InetAddresses.parseNumericAddress(
254                                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
255                 break;
256             default:
257                 throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType);
258         }
259 
260         return new IkeTrafficSelector(tsType, startPort, endPort, startAddress, endAddress);
261     }
262 }
263