|
34 | 34 | import android.net.BaseNetworkStateTracker; |
35 | 35 | import android.net.ConnectivityManager; |
36 | 36 | import android.net.INetworkManagementEventObserver; |
| 37 | +import android.net.LinkProperties; |
37 | 38 | import android.net.LocalSocket; |
38 | 39 | import android.net.LocalSocketAddress; |
39 | 40 | import android.net.NetworkInfo; |
| 41 | +import android.net.RouteInfo; |
40 | 42 | import android.net.NetworkInfo.DetailedState; |
41 | 43 | import android.os.Binder; |
42 | 44 | import android.os.FileUtils; |
|
48 | 50 | import android.os.RemoteException; |
49 | 51 | import android.os.SystemClock; |
50 | 52 | import android.os.SystemService; |
| 53 | +import android.security.Credentials; |
| 54 | +import android.security.KeyStore; |
51 | 55 | import android.util.Log; |
| 56 | +import android.widget.Toast; |
52 | 57 |
|
53 | 58 | import com.android.internal.R; |
54 | 59 | import com.android.internal.net.LegacyVpnInfo; |
55 | 60 | import com.android.internal.net.VpnConfig; |
| 61 | +import com.android.internal.net.VpnProfile; |
56 | 62 | import com.android.internal.util.Preconditions; |
57 | 63 | import com.android.server.ConnectivityService.VpnCallback; |
58 | 64 | import com.android.server.net.BaseNetworkObserver; |
59 | 65 |
|
60 | 66 | import java.io.File; |
61 | 67 | import java.io.InputStream; |
62 | 68 | import java.io.OutputStream; |
| 69 | +import java.net.Inet4Address; |
| 70 | +import java.net.InetAddress; |
63 | 71 | import java.nio.charset.Charsets; |
64 | 72 | import java.util.Arrays; |
65 | 73 |
|
@@ -430,20 +438,127 @@ private void hideNotification() { |
430 | 438 | private native int jniCheck(String interfaze); |
431 | 439 | private native void jniProtect(int socket, String interfaze); |
432 | 440 |
|
| 441 | + private static String findLegacyVpnGateway(LinkProperties prop) { |
| 442 | + for (RouteInfo route : prop.getRoutes()) { |
| 443 | + // Currently legacy VPN only works on IPv4. |
| 444 | + if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) { |
| 445 | + return route.getGateway().getHostAddress(); |
| 446 | + } |
| 447 | + } |
| 448 | + |
| 449 | + throw new IllegalStateException("Unable to find suitable gateway"); |
| 450 | + } |
| 451 | + |
433 | 452 | /** |
434 | | - * Start legacy VPN. This method stops the daemons and restart them |
435 | | - * if arguments are not null. Heavy things are offloaded to another |
436 | | - * thread, so callers will not be blocked for a long time. |
437 | | - * |
438 | | - * @param config The parameters to configure the network. |
439 | | - * @param racoon The arguments to be passed to racoon. |
440 | | - * @param mtpd The arguments to be passed to mtpd. |
| 453 | + * Start legacy VPN, controlling native daemons as needed. Creates a |
| 454 | + * secondary thread to perform connection work, returning quickly. |
441 | 455 | */ |
442 | | - public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { |
443 | | - stopLegacyVpn(); |
| 456 | + public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) { |
| 457 | + if (keyStore.state() != KeyStore.State.UNLOCKED) { |
| 458 | + throw new IllegalStateException("KeyStore isn't unlocked"); |
| 459 | + } |
| 460 | + |
| 461 | + final String iface = egress.getInterfaceName(); |
| 462 | + final String gateway = findLegacyVpnGateway(egress); |
| 463 | + |
| 464 | + // Load certificates. |
| 465 | + String privateKey = ""; |
| 466 | + String userCert = ""; |
| 467 | + String caCert = ""; |
| 468 | + String serverCert = ""; |
| 469 | + if (!profile.ipsecUserCert.isEmpty()) { |
| 470 | + privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert; |
| 471 | + byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert); |
| 472 | + userCert = (value == null) ? null : new String(value, Charsets.UTF_8); |
| 473 | + } |
| 474 | + if (!profile.ipsecCaCert.isEmpty()) { |
| 475 | + byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert); |
| 476 | + caCert = (value == null) ? null : new String(value, Charsets.UTF_8); |
| 477 | + } |
| 478 | + if (!profile.ipsecServerCert.isEmpty()) { |
| 479 | + byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); |
| 480 | + serverCert = (value == null) ? null : new String(value, Charsets.UTF_8); |
| 481 | + } |
| 482 | + if (privateKey == null || userCert == null || caCert == null || serverCert == null) { |
| 483 | + throw new IllegalStateException("Cannot load credentials"); |
| 484 | + } |
| 485 | + |
| 486 | + // Prepare arguments for racoon. |
| 487 | + String[] racoon = null; |
| 488 | + switch (profile.type) { |
| 489 | + case VpnProfile.TYPE_L2TP_IPSEC_PSK: |
| 490 | + racoon = new String[] { |
| 491 | + iface, profile.server, "udppsk", profile.ipsecIdentifier, |
| 492 | + profile.ipsecSecret, "1701", |
| 493 | + }; |
| 494 | + break; |
| 495 | + case VpnProfile.TYPE_L2TP_IPSEC_RSA: |
| 496 | + racoon = new String[] { |
| 497 | + iface, profile.server, "udprsa", privateKey, userCert, |
| 498 | + caCert, serverCert, "1701", |
| 499 | + }; |
| 500 | + break; |
| 501 | + case VpnProfile.TYPE_IPSEC_XAUTH_PSK: |
| 502 | + racoon = new String[] { |
| 503 | + iface, profile.server, "xauthpsk", profile.ipsecIdentifier, |
| 504 | + profile.ipsecSecret, profile.username, profile.password, "", gateway, |
| 505 | + }; |
| 506 | + break; |
| 507 | + case VpnProfile.TYPE_IPSEC_XAUTH_RSA: |
| 508 | + racoon = new String[] { |
| 509 | + iface, profile.server, "xauthrsa", privateKey, userCert, |
| 510 | + caCert, serverCert, profile.username, profile.password, "", gateway, |
| 511 | + }; |
| 512 | + break; |
| 513 | + case VpnProfile.TYPE_IPSEC_HYBRID_RSA: |
| 514 | + racoon = new String[] { |
| 515 | + iface, profile.server, "hybridrsa", |
| 516 | + caCert, serverCert, profile.username, profile.password, "", gateway, |
| 517 | + }; |
| 518 | + break; |
| 519 | + } |
| 520 | + |
| 521 | + // Prepare arguments for mtpd. |
| 522 | + String[] mtpd = null; |
| 523 | + switch (profile.type) { |
| 524 | + case VpnProfile.TYPE_PPTP: |
| 525 | + mtpd = new String[] { |
| 526 | + iface, "pptp", profile.server, "1723", |
| 527 | + "name", profile.username, "password", profile.password, |
| 528 | + "linkname", "vpn", "refuse-eap", "nodefaultroute", |
| 529 | + "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", |
| 530 | + (profile.mppe ? "+mppe" : "nomppe"), |
| 531 | + }; |
| 532 | + break; |
| 533 | + case VpnProfile.TYPE_L2TP_IPSEC_PSK: |
| 534 | + case VpnProfile.TYPE_L2TP_IPSEC_RSA: |
| 535 | + mtpd = new String[] { |
| 536 | + iface, "l2tp", profile.server, "1701", profile.l2tpSecret, |
| 537 | + "name", profile.username, "password", profile.password, |
| 538 | + "linkname", "vpn", "refuse-eap", "nodefaultroute", |
| 539 | + "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", |
| 540 | + }; |
| 541 | + break; |
| 542 | + } |
444 | 543 |
|
445 | | - // TODO: move legacy definition to settings |
| 544 | + VpnConfig config = new VpnConfig(); |
446 | 545 | config.legacy = true; |
| 546 | + config.user = profile.key; |
| 547 | + config.interfaze = iface; |
| 548 | + config.session = profile.name; |
| 549 | + config.routes = profile.routes; |
| 550 | + if (!profile.dnsServers.isEmpty()) { |
| 551 | + config.dnsServers = Arrays.asList(profile.dnsServers.split(" +")); |
| 552 | + } |
| 553 | + if (!profile.searchDomains.isEmpty()) { |
| 554 | + config.searchDomains = Arrays.asList(profile.searchDomains.split(" +")); |
| 555 | + } |
| 556 | + |
| 557 | + startLegacyVpn(config, racoon, mtpd); |
| 558 | + } |
| 559 | + |
| 560 | + private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { |
| 561 | + stopLegacyVpn(); |
447 | 562 |
|
448 | 563 | // Prepare for the new request. This also checks the caller. |
449 | 564 | prepare(null, VpnConfig.LEGACY_VPN); |
|
0 commit comments