From: Jason Cooper <cyanogen at lakedaemon.net> Signed-off-by: Jason Cooper <cyanogen at lakedaemon.net> --- core/res/res/values/strings.xml | 1 + .../com/android/server/vpn/OpenconnectService.java | 132 +++++++++++++++++ .../com/android/server/vpn/VpnServiceBinder.java | 6 + vpn/java/android/net/vpn/OpenconnectProfile.java | 148 ++++++++++++++++++++ vpn/java/android/net/vpn/VpnType.java | 3 +- 5 files changed, 289 insertions(+), 1 deletions(-) create mode 100644 packages/VpnServices/src/com/android/server/vpn/OpenconnectService.java create mode 100644 vpn/java/android/net/vpn/OpenconnectProfile.java diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 967457d..b743825 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2358,6 +2358,7 @@ <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string> <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string> <string name="openvpn_vpn_description">OpenVPN SSL VPN</string> + <string name="openconnect_vpn_description">OpenConnect SSL VPN</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> diff --git a/packages/VpnServices/src/com/android/server/vpn/OpenconnectService.java b/packages/VpnServices/src/com/android/server/vpn/OpenconnectService.java new file mode 100644 index 0000000..e32a4c8 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/OpenconnectService.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011, Jason Cooper <cyanogen at lakedaemon.net> + * + * 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. + */ + +/* + * copied from OpenvpnService.java which was (same license): + * + * Copyright (C) 2009, 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.server.vpn; + +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.net.vpn.OpenconnectProfile; +import android.net.vpn.VpnManager; +import android.security.Credentials; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * The service that manages the openconnect VPN connection. + */ +class OpenconnectService extends VpnService<OpenconnectProfile> { + + private static final String OPENCONNECT_DAEMON = "openconnect"; + + private static final String MTPD = "mtpd"; + + private static final String USE_INLINE = "[[INLINE]]"; + + private static final String USE_KEYSTORE = "[[ANDROID]]"; + + private static final String TAG = OpenconnectService.class.getSimpleName(); + + private static int count = 0; + + private transient String mPassword; + + private transient String mUsername; + + private synchronized static String getCount() { + return Integer.toString(count++); + } + + @Override + protected void connect(String serverIp, String username, String password) throws IOException { + OpenconnectProfile p = getProfile(); + ArrayList<String> args = new ArrayList<String>(); + String url = "https://" + p.getServerName() + ":" + p.getPort(); + + mUsername = username; + mPassword = password; + + args.add(OPENCONNECT_DAEMON); + + if (p.getCAName() != null) { + args.add("--cafile"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.CA_CERTIFICATE + p.getCAName()); + } + if (p.getCertName() != null) { + args.add("--certificate"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.USER_CERTIFICATE + p.getCertName()); + args.add("--sslkey"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.USER_PRIVATE_KEY + p.getCertName()); + args.add("--key-type"); + args.add("PKCS#12"); + } + if (! p.getUseCompression()) { + args.add("--no-deflate"); + } + if (p.getUserAuth()) { + args.add("-u"); + args.add(mUsername); + args.add("--passwd-on-stdin"); + args.add(mPassword); + } + if (p.getExtra() != null && !p.getExtra().equals("")) { + Scanner s = new Scanner(p.getExtra()); + while (s.hasNext()) + args.add(s.next()); + } + args.add(url); + DaemonProxy mtpd = getDaemons().startDaemon(MTPD); + mtpd.sendCommand(args.toArray(new String[args.size()])); + } + + @Override + void waitUntilConnectedOrTimedout() throws IOException { + setVpnStateUp(true); + } + + void startConnectivityMonitor() { + /* + * Openconnect is completely event driven, so we don't need a polling + * monitor at all, so do nothing here + */ + } + +} diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java index 990da63..2c13a93 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -23,6 +23,7 @@ import android.net.vpn.L2tpIpsecProfile; import android.net.vpn.L2tpIpsecPskProfile; import android.net.vpn.L2tpProfile; import android.net.vpn.OpenvpnProfile; +import android.net.vpn.OpenconnectProfile; import android.net.vpn.PptpProfile; import android.net.vpn.VpnManager; import android.net.vpn.VpnProfile; @@ -168,6 +169,11 @@ public class VpnServiceBinder extends Service { ovpn.setContext(this, (OpenvpnProfile)p ); return ovpn; + case OPENCONNECT: + OpenconnectService oconn = new OpenconnectService(); + oconn.setContext(this, (OpenconnectProfile)p ); + return oconn; + case PPTP: PptpService pptp = new PptpService(); pptp.setContext(this, (PptpProfile) p); diff --git a/vpn/java/android/net/vpn/OpenconnectProfile.java b/vpn/java/android/net/vpn/OpenconnectProfile.java new file mode 100644 index 0000000..f544354 --- /dev/null +++ b/vpn/java/android/net/vpn/OpenconnectProfile.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2011, Jason Cooper <cyanogen at lakedaemon.net> + * + * 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. + */ + +/* + * copied from OpenvpnProfile.java which was (same license): + * + * Copyright (C) 2009, 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 android.net.vpn; + +import android.os.Parcel; + +/** + * The profile for OpenConnect type of VPN. + * {@hide} + */ +public class OpenconnectProfile extends VpnProfile { + private static final long serialVersionUID = 1L; + + // Standard Settings + private boolean mUserAuth = true; + + private String mCA; + + private String mCert; + + private String mSVRCert; + + // Advanced Settings + private int mPort = 443; + + private boolean mUseCompression = true; + + private String mExtra; + + @Override + public VpnType getType() { + return VpnType.OPENCONNECT; + } + + public void setPort(String port) { + try { + mPort = Integer.parseInt(port); + } catch (NumberFormatException e) { + // no update + } + } + + public String getPort() { + return Integer.toString(mPort); + } + + public boolean getUserAuth() { + return mUserAuth; + } + + public void setUserAuth(boolean auth) { + mUserAuth = auth; + } + + public String getCAName() { + return mCA; + } + + public void setCAName(String name) { + mCA = name; + } + + public String getCertName() { + return mCert; + } + + public void setCertName(String name) { + mCert = name; + } + + public String getSVRCertName() { + return mSVRCert; + } + + public void setSVRCertName(String name) { + mSVRCert = name; + } + + public void setUseCompression(boolean b) { + mUseCompression = b; + } + + public boolean getUseCompression() { + return mUseCompression; + } + + public void setExtra(String extra) { + mExtra = extra; + } + + public String getExtra() { + return mExtra; + } + + @Override + protected void readFromParcel(Parcel in) { + super.readFromParcel(in); + mPort = in.readInt(); + mCA = in.readString(); + mCert = in.readString(); + mSVRCert = in.readString(); + mUseCompression = in.readInt() == 1; + mExtra = in.readString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(mPort); + parcel.writeString(mCA); + parcel.writeString(mCert); + parcel.writeString(mSVRCert); + parcel.writeInt(mUseCompression ? 1 : 0); + parcel.writeString(mExtra); + } +} diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java index 2157067..a486318 100644 --- a/vpn/java/android/net/vpn/VpnType.java +++ b/vpn/java/android/net/vpn/VpnType.java @@ -29,7 +29,8 @@ public enum VpnType { L2tpIpsecPskProfile.class), L2TP_IPSEC("L2TP/IPSec CRT", R.string.l2tp_ipsec_crt_vpn_description, L2tpIpsecProfile.class), - OPENVPN("OpenVPN", R.string.openvpn_vpn_description, OpenvpnProfile.class); + OPENVPN("OpenVPN", R.string.openvpn_vpn_description, OpenvpnProfile.class), + OPENCONNECT("OpenConnect", R.string.openconnect_vpn_description, OpenconnectProfile.class); private String mDisplayName; private int mDescriptionId; -- 1.7.0.4