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