1 /*
2  * Copyright (C) 2020 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 com.android.server.connectivity;
18 
19 import static android.net.ConnectivityManager.NetworkCallback;
20 import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
21 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
22 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
23 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
24 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
25 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
26 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
27 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
28 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
29 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
30 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
31 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
32 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
33 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
34 
35 import android.annotation.NonNull;
36 import android.content.Context;
37 import android.net.Ikev2VpnProfile;
38 import android.net.InetAddresses;
39 import android.net.IpPrefix;
40 import android.net.IpSecAlgorithm;
41 import android.net.IpSecTransform;
42 import android.net.Network;
43 import android.net.RouteInfo;
44 import android.net.eap.EapSessionConfig;
45 import android.net.ipsec.ike.ChildSaProposal;
46 import android.net.ipsec.ike.ChildSessionCallback;
47 import android.net.ipsec.ike.ChildSessionConfiguration;
48 import android.net.ipsec.ike.ChildSessionParams;
49 import android.net.ipsec.ike.IkeFqdnIdentification;
50 import android.net.ipsec.ike.IkeIdentification;
51 import android.net.ipsec.ike.IkeIpv4AddrIdentification;
52 import android.net.ipsec.ike.IkeIpv6AddrIdentification;
53 import android.net.ipsec.ike.IkeKeyIdIdentification;
54 import android.net.ipsec.ike.IkeRfc822AddrIdentification;
55 import android.net.ipsec.ike.IkeSaProposal;
56 import android.net.ipsec.ike.IkeSessionCallback;
57 import android.net.ipsec.ike.IkeSessionConfiguration;
58 import android.net.ipsec.ike.IkeSessionParams;
59 import android.net.ipsec.ike.IkeTrafficSelector;
60 import android.net.ipsec.ike.TunnelModeChildSessionParams;
61 import android.net.ipsec.ike.exceptions.IkeException;
62 import android.net.ipsec.ike.exceptions.IkeProtocolException;
63 import android.net.util.IpRange;
64 import android.system.OsConstants;
65 import android.util.Log;
66 
67 import com.android.internal.net.VpnProfile;
68 import com.android.internal.util.HexDump;
69 
70 import java.net.Inet4Address;
71 import java.net.Inet6Address;
72 import java.net.InetAddress;
73 import java.util.ArrayList;
74 import java.util.Arrays;
75 import java.util.Collection;
76 import java.util.HashSet;
77 import java.util.List;
78 
79 /**
80  * Utility class to build and convert IKEv2/IPsec parameters.
81  *
82  * @hide
83  */
84 public class VpnIkev2Utils {
85     private static final String TAG = VpnIkev2Utils.class.getSimpleName();
86 
87     // TODO: Use IKE library exposed constants when @SystemApi is updated.
88     /** IANA-defined 3072 group for use in IKEv2 */
89     private static final int DH_GROUP_3072_BIT_MODP = 15;
90     /** IANA-defined 4096 group for use in IKEv2 */
91     private static final int DH_GROUP_4096_BIT_MODP = 16;
92 
buildIkeSessionParams( @onNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network)93     static IkeSessionParams buildIkeSessionParams(
94             @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) {
95         final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
96         final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
97 
98         final IkeSessionParams.Builder ikeOptionsBuilder =
99                 new IkeSessionParams.Builder(context)
100                         .setServerHostname(profile.getServerAddr())
101                         .setNetwork(network)
102                         .setLocalIdentification(localId)
103                         .setRemoteIdentification(remoteId);
104         setIkeAuth(profile, ikeOptionsBuilder);
105 
106         for (final IkeSaProposal ikeProposal : getIkeSaProposals()) {
107             ikeOptionsBuilder.addSaProposal(ikeProposal);
108         }
109 
110         return ikeOptionsBuilder.build();
111     }
112 
buildChildSessionParams(List<String> allowedAlgorithms)113     static ChildSessionParams buildChildSessionParams(List<String> allowedAlgorithms) {
114         final TunnelModeChildSessionParams.Builder childOptionsBuilder =
115                 new TunnelModeChildSessionParams.Builder();
116 
117         for (final ChildSaProposal childProposal : getChildSaProposals(allowedAlgorithms)) {
118             childOptionsBuilder.addSaProposal(childProposal);
119         }
120 
121         childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET);
122         childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6);
123         childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET);
124         childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6);
125 
126         return childOptionsBuilder.build();
127     }
128 
setIkeAuth( @onNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder)129     private static void setIkeAuth(
130             @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) {
131         switch (profile.getType()) {
132             case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
133                 final EapSessionConfig eapConfig =
134                         new EapSessionConfig.Builder()
135                                 .setEapMsChapV2Config(profile.getUsername(), profile.getPassword())
136                                 .build();
137                 builder.setAuthEap(profile.getServerRootCaCert(), eapConfig);
138                 break;
139             case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
140                 builder.setAuthPsk(profile.getPresharedKey());
141                 break;
142             case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
143                 builder.setAuthDigitalSignature(
144                         profile.getServerRootCaCert(),
145                         profile.getUserCert(),
146                         profile.getRsaPrivateKey());
147                 break;
148             default:
149                 throw new IllegalArgumentException("Unknown auth method set");
150         }
151     }
152 
getIkeSaProposals()153     private static List<IkeSaProposal> getIkeSaProposals() {
154         // TODO: Add ability to filter this when IKEv2 API is made Public API
155         final List<IkeSaProposal> proposals = new ArrayList<>();
156 
157         // Encryption Algorithms: Currently only AES_CBC is supported.
158         final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
159 
160         // Currently only AES_CBC is supported.
161         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
162         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
163         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
164 
165         // Authentication/Integrity Algorithms
166         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
167         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
168         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
169         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
170 
171         // Add AEAD options
172         final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
173         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
174         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
175         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
176         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
177         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
178         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
179         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
180         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
181         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
182 
183         // Add dh, prf for both builders
184         for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
185             builder.addDhGroup(DH_GROUP_4096_BIT_MODP);
186             builder.addDhGroup(DH_GROUP_3072_BIT_MODP);
187             builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
188             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
189             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
190         }
191 
192         proposals.add(normalModeBuilder.build());
193         proposals.add(aeadBuilder.build());
194         return proposals;
195     }
196 
197     /** Builds a child SA proposal based on the allowed IPsec algorithms */
getChildSaProposals(List<String> allowedAlgorithms)198     private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) {
199         final List<ChildSaProposal> proposals = new ArrayList<>();
200 
201         // Add non-AEAD options
202         if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) {
203             final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
204 
205             // Encryption Algorithms:
206             // AES-CBC is currently the only supported encryption algorithm.
207             normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
208             normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
209             normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
210 
211             // Authentication/Integrity Algorithms:
212             // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these.
213             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA512)) {
214                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
215             }
216             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)) {
217                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
218             }
219             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) {
220                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
221             }
222 
223             ChildSaProposal proposal = normalModeBuilder.build();
224             if (proposal.getIntegrityAlgorithms().isEmpty()) {
225                 // Should be impossible; Verified in Ikev2VpnProfile.
226                 Log.wtf(TAG, "Missing integrity algorithm when buildling Child SA proposal");
227             } else {
228                 proposals.add(normalModeBuilder.build());
229             }
230         }
231 
232         // Add AEAD options
233         if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) {
234             final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
235 
236             // AES-GCM is currently the only supported AEAD algorithm
237             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
238             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
239             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
240             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
241             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
242             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
243             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
244             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
245             aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
246 
247             proposals.add(aeadBuilder.build());
248         }
249 
250         return proposals;
251     }
252 
253     static class IkeSessionCallbackImpl implements IkeSessionCallback {
254         private final String mTag;
255         private final Vpn.IkeV2VpnRunnerCallback mCallback;
256         private final Network mNetwork;
257 
IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network)258         IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
259             mTag = tag;
260             mCallback = callback;
261             mNetwork = network;
262         }
263 
264         @Override
onOpened(@onNull IkeSessionConfiguration ikeSessionConfig)265         public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
266             Log.d(mTag, "IkeOpened for network " + mNetwork);
267             // Nothing to do here.
268         }
269 
270         @Override
onClosed()271         public void onClosed() {
272             Log.d(mTag, "IkeClosed for network " + mNetwork);
273             mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
274         }
275 
276         @Override
onClosedExceptionally(@onNull IkeException exception)277         public void onClosedExceptionally(@NonNull IkeException exception) {
278             Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
279             mCallback.onSessionLost(mNetwork);
280         }
281 
282         @Override
onError(@onNull IkeProtocolException exception)283         public void onError(@NonNull IkeProtocolException exception) {
284             Log.d(mTag, "IkeError for network " + mNetwork, exception);
285             // Non-fatal, log and continue.
286         }
287     }
288 
289     static class ChildSessionCallbackImpl implements ChildSessionCallback {
290         private final String mTag;
291         private final Vpn.IkeV2VpnRunnerCallback mCallback;
292         private final Network mNetwork;
293 
ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network)294         ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
295             mTag = tag;
296             mCallback = callback;
297             mNetwork = network;
298         }
299 
300         @Override
onOpened(@onNull ChildSessionConfiguration childConfig)301         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
302             Log.d(mTag, "ChildOpened for network " + mNetwork);
303             mCallback.onChildOpened(mNetwork, childConfig);
304         }
305 
306         @Override
onClosed()307         public void onClosed() {
308             Log.d(mTag, "ChildClosed for network " + mNetwork);
309             mCallback.onSessionLost(mNetwork);
310         }
311 
312         @Override
onClosedExceptionally(@onNull IkeException exception)313         public void onClosedExceptionally(@NonNull IkeException exception) {
314             Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
315             mCallback.onSessionLost(mNetwork);
316         }
317 
318         @Override
onIpSecTransformCreated(@onNull IpSecTransform transform, int direction)319         public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
320             Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
321             mCallback.onChildTransformCreated(mNetwork, transform, direction);
322         }
323 
324         @Override
onIpSecTransformDeleted(@onNull IpSecTransform transform, int direction)325         public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
326             // Nothing to be done; no references to the IpSecTransform are held by the
327             // Ikev2VpnRunner (or this callback class), and this transform will be closed by the
328             // IKE library.
329             Log.d(mTag,
330                     "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
331         }
332     }
333 
334     static class Ikev2VpnNetworkCallback extends NetworkCallback {
335         private final String mTag;
336         private final Vpn.IkeV2VpnRunnerCallback mCallback;
337 
Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback)338         Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
339             mTag = tag;
340             mCallback = callback;
341         }
342 
343         @Override
onAvailable(@onNull Network network)344         public void onAvailable(@NonNull Network network) {
345             Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
346             mCallback.onDefaultNetworkChanged(network);
347         }
348 
349         @Override
onLost(@onNull Network network)350         public void onLost(@NonNull Network network) {
351             Log.d(mTag, "Tearing down; lost network: " + network);
352             mCallback.onSessionLost(network);
353         }
354     }
355 
356     /**
357      * Identity parsing logic using similar logic to open source implementations of IKEv2
358      *
359      * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded
360      * identities.
361      */
parseIkeIdentification(@onNull String identityStr)362     private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) {
363         // TODO: Add identity formatting to public API javadocs.
364         if (identityStr.contains("@")) {
365             if (identityStr.startsWith("@#")) {
366                 // KEY_ID
367                 final String hexStr = identityStr.substring(2);
368                 return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr));
369             } else if (identityStr.startsWith("@@")) {
370                 // RFC822 (USER_FQDN)
371                 return new IkeRfc822AddrIdentification(identityStr.substring(2));
372             } else if (identityStr.startsWith("@")) {
373                 // FQDN
374                 return new IkeFqdnIdentification(identityStr.substring(1));
375             } else {
376                 // RFC822 (USER_FQDN)
377                 return new IkeRfc822AddrIdentification(identityStr);
378             }
379         } else if (InetAddresses.isNumericAddress(identityStr)) {
380             final InetAddress addr = InetAddresses.parseNumericAddress(identityStr);
381             if (addr instanceof Inet4Address) {
382                 // IPv4
383                 return new IkeIpv4AddrIdentification((Inet4Address) addr);
384             } else if (addr instanceof Inet6Address) {
385                 // IPv6
386                 return new IkeIpv6AddrIdentification((Inet6Address) addr);
387             } else {
388                 throw new IllegalArgumentException("IP version not supported");
389             }
390         } else {
391             if (identityStr.contains(":")) {
392                 // KEY_ID
393                 return new IkeKeyIdIdentification(identityStr.getBytes());
394             } else {
395                 // FQDN
396                 return new IkeFqdnIdentification(identityStr);
397             }
398         }
399     }
400 
getRoutesFromTrafficSelectors( List<IkeTrafficSelector> trafficSelectors)401     static Collection<RouteInfo> getRoutesFromTrafficSelectors(
402             List<IkeTrafficSelector> trafficSelectors) {
403         final HashSet<RouteInfo> routes = new HashSet<>();
404 
405         for (final IkeTrafficSelector selector : trafficSelectors) {
406             for (final IpPrefix prefix :
407                     new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
408                 routes.add(new RouteInfo(prefix, null));
409             }
410         }
411 
412         return routes;
413     }
414 }
415