Merge "Migrate legacy VPN arguments to system_server." into jb-mr1-dev

This commit is contained in:
Jeff Sharkey
2012-08-24 11:32:59 -07:00
committed by Android (Google) Code Review
6 changed files with 197 additions and 21 deletions

View File

@@ -26,6 +26,7 @@ import android.os.ParcelFileDescriptor;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
/**
* Interface that answers queries about, and allows changing, the
@@ -118,7 +119,7 @@ interface IConnectivityManager
ParcelFileDescriptor establishVpn(in VpnConfig config);
void startLegacyVpn(in VpnConfig config, in String[] racoon, in String[] mtpd);
void startLegacyVpn(in VpnProfile profile);
LegacyVpnInfo getLegacyVpnInfo();
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.net;
parcelable VpnProfile;

View File

@@ -16,6 +16,9 @@
package com.android.internal.net;
import android.os.Parcel;
import android.os.Parcelable;
import java.nio.charset.Charsets;
/**
@@ -27,7 +30,7 @@ import java.nio.charset.Charsets;
*
* @hide
*/
public class VpnProfile implements Cloneable {
public class VpnProfile implements Cloneable, Parcelable {
// Match these constants with R.array.vpn_types.
public static final int TYPE_PPTP = 0;
public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -120,4 +123,28 @@ public class VpnProfile implements Cloneable {
builder.append('\0').append(ipsecServerCert);
return builder.toString().getBytes(Charsets.UTF_8);
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(key);
out.writeByteArray(encode());
}
public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
@Override
public VpnProfile createFromParcel(Parcel in) {
final String key = in.readString();
return decode(key, in.createByteArray());
}
@Override
public VpnProfile[] newArray(int size) {
return new VpnProfile[size];
}
};
@Override
public int describeContents() {
return 0;
}
}

View File

@@ -123,7 +123,11 @@ public class ManageDialog extends AlertActivity implements
if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
if (mConfig.legacy) {
mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
} else {
mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
}
}
} catch (Exception e) {
Log.e(TAG, "onClick", e);

View File

@@ -31,6 +31,8 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver;
import android.content.Context;
@@ -78,6 +80,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -85,8 +88,10 @@ import android.util.SparseIntArray;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Preconditions;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
@@ -137,6 +142,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
private final KeyStore mKeyStore;
private Vpn mVpn;
private VpnCallback mVpnCallback = new VpnCallback();
@@ -371,6 +378,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mKeyStore = KeyStore.getInstance();
try {
mPolicyManager.registerListener(mPolicyListener);
@@ -3124,14 +3132,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
* Start legacy VPN and return an intent to VpnDialogs. This method is
* used by VpnSettings and not available in ConnectivityManager.
* Permissions are checked in Vpn class.
* @hide
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
*/
@Override
public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
mVpn.startLegacyVpn(config, racoon, mtpd);
public void startLegacyVpn(VpnProfile profile) {
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
mVpn.startLegacyVpn(profile, mKeyStore, egress);
}
/**

View File

@@ -34,9 +34,11 @@ import android.graphics.drawable.Drawable;
import android.net.BaseNetworkStateTracker;
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.NetworkInfo;
import android.net.RouteInfo;
import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
import android.os.FileUtils;
@@ -48,11 +50,15 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemService;
import android.security.Credentials;
import android.security.KeyStore;
import android.util.Log;
import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService.VpnCallback;
import com.android.server.net.BaseNetworkObserver;
@@ -60,6 +66,8 @@ import com.android.server.net.BaseNetworkObserver;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.nio.charset.Charsets;
import java.util.Arrays;
@@ -430,20 +438,127 @@ public class Vpn extends BaseNetworkStateTracker {
private native int jniCheck(String interfaze);
private native void jniProtect(int socket, String interfaze);
/**
* Start legacy VPN. This method stops the daemons and restart them
* if arguments are not null. Heavy things are offloaded to another
* thread, so callers will not be blocked for a long time.
*
* @param config The parameters to configure the network.
* @param racoon The arguments to be passed to racoon.
* @param mtpd The arguments to be passed to mtpd.
*/
public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
stopLegacyVpn();
private static String findLegacyVpnGateway(LinkProperties prop) {
for (RouteInfo route : prop.getRoutes()) {
// Currently legacy VPN only works on IPv4.
if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
return route.getGateway().getHostAddress();
}
}
// TODO: move legacy definition to settings
throw new IllegalStateException("Unable to find suitable gateway");
}
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
*/
public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
if (keyStore.state() != KeyStore.State.UNLOCKED) {
throw new IllegalStateException("KeyStore isn't unlocked");
}
final String iface = egress.getInterfaceName();
final String gateway = findLegacyVpnGateway(egress);
// Load certificates.
String privateKey = "";
String userCert = "";
String caCert = "";
String serverCert = "";
if (!profile.ipsecUserCert.isEmpty()) {
privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
}
if (!profile.ipsecCaCert.isEmpty()) {
byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
}
if (!profile.ipsecServerCert.isEmpty()) {
byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
}
if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
throw new IllegalStateException("Cannot load credentials");
}
// Prepare arguments for racoon.
String[] racoon = null;
switch (profile.type) {
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
racoon = new String[] {
iface, profile.server, "udppsk", profile.ipsecIdentifier,
profile.ipsecSecret, "1701",
};
break;
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
racoon = new String[] {
iface, profile.server, "udprsa", privateKey, userCert,
caCert, serverCert, "1701",
};
break;
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
racoon = new String[] {
iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
profile.ipsecSecret, profile.username, profile.password, "", gateway,
};
break;
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
racoon = new String[] {
iface, profile.server, "xauthrsa", privateKey, userCert,
caCert, serverCert, profile.username, profile.password, "", gateway,
};
break;
case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
racoon = new String[] {
iface, profile.server, "hybridrsa",
caCert, serverCert, profile.username, profile.password, "", gateway,
};
break;
}
// Prepare arguments for mtpd.
String[] mtpd = null;
switch (profile.type) {
case VpnProfile.TYPE_PPTP:
mtpd = new String[] {
iface, "pptp", profile.server, "1723",
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute",
"usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
(profile.mppe ? "+mppe" : "nomppe"),
};
break;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
mtpd = new String[] {
iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute",
"usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
};
break;
}
VpnConfig config = new VpnConfig();
config.legacy = true;
config.user = profile.key;
config.interfaze = iface;
config.session = profile.name;
config.routes = profile.routes;
if (!profile.dnsServers.isEmpty()) {
config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
}
if (!profile.searchDomains.isEmpty()) {
config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
}
startLegacyVpn(config, racoon, mtpd);
}
private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
stopLegacyVpn();
// Prepare for the new request. This also checks the caller.
prepare(null, VpnConfig.LEGACY_VPN);