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