[PATCH] openconnect: add initial support for openconnect ssl vpn.

openconnect at lakedaemon.net openconnect at lakedaemon.net
Thu Jul 14 21:38:56 EDT 2011


From: Jason Cooper <cyanogen at lakedaemon.net>


Signed-off-by: Jason Cooper <cyanogen at lakedaemon.net>
---
 AndroidManifest.xml                                |    4 +
 res/values/strings.xml                             |   15 +
 res/xml/openconnect_advanced_settings.xml          |   60 ++++
 .../vpn/OpenconnectAuthenticationActor.java        |   66 +++++
 .../android/settings/vpn/OpenconnectEditor.java    |  300 ++++++++++++++++++++
 src/com/android/settings/vpn/VpnEditor.java        |    4 +
 src/com/android/settings/vpn/VpnSettings.java      |    8 +
 7 files changed, 457 insertions(+), 0 deletions(-)
 create mode 100644 res/xml/openconnect_advanced_settings.xml
 create mode 100644 src/com/android/settings/vpn/OpenconnectAuthenticationActor.java
 create mode 100644 src/com/android/settings/vpn/OpenconnectEditor.java

diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2f303a4..b58dd56 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -218,6 +218,10 @@
         <activity android:name=".vpn.OpenvpnEditor$AdvancedSettings"
                 android:configChanges="orientation|keyboardHidden">
         </activity>
+        <activity android:name=".vpn.OpenconnectEditor$AdvancedSettings"
+                android:configChanges="orientation|keyboardHidden">
+        </activity>
+
 
         <activity android:name="DateTimeSettings" android:label="@string/date_and_time"
                 >
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0adefcf..257c374 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2621,6 +2621,12 @@ found in the list of installed applications.</string>
     <string name="vpn_a_user_certificate">a user certificate</string>
 
     <!-- Preference title -->
+    <string name="vpn_server_certificate_title">Set server certificate</string>
+    <!-- Complete term -->
+    <string name="vpn_server_certificate">Server certificate</string>
+    <string name="vpn_a_server_certificate">a server certificate</string>
+
+    <!-- Preference title -->
     <string name="vpn_ca_certificate_title">Set CA certificate</string>
     <!-- Complete term -->
     <string name="vpn_ca_certificate">Certificate authority (CA) certificate</string>
@@ -2655,6 +2661,15 @@ found in the list of installed applications.</string>
     <string name="vpn_openvpn_set_extra">Extra arguments</string>
     <string name="vpn_openvpn_set_extra_message">Enter the extra command line arguments</string>
 
+    <string name="vpn_openconnect_port">Server port</string>
+    <string name="vpn_openconnect_userauth">User authentication</string>
+    <string name="vpn_openconnect_userauth_summary">Set if additional username/password authentication needs to be used</string>
+    <string name="vpn_openconnect_advanced_titlebar">Advanced OpenConnect Settings</string>
+    <string name="vpn_openconnect_comp_deflate">Deflate compression</string>
+    <string name="vpn_openconnect_comp_deflate_summary">Force Deflate compression to be set to on</string>
+    <string name="vpn_openconnect_set_extra">Extra arguments</string>
+    <string name="vpn_openconnect_set_extra_message">Enter the extra command line arguments</string>
+
     <!-- Preference title -->
     <string name="vpn_ipsec_presharedkey_title">Set IPSec pre-shared key</string>
     <!-- Complete term -->
diff --git a/res/xml/openconnect_advanced_settings.xml b/res/xml/openconnect_advanced_settings.xml
new file mode 100644
index 0000000..89ee195
--- /dev/null
+++ b/res/xml/openconnect_advanced_settings.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 openvpn_advanced_settings.xml which was (same license):
+
+     Copyright (C) 2010 James Bottomley <James.Bottomley at suse.de>
+
+     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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/vpn_openconnect_advanced_titlebar">
+
+    <EditTextPreference
+       android:key="set_port"
+       android:title="@string/vpn_openconnect_port"
+       android:dialogTitle="@string/vpn_openconnect_port"
+       android:singleLine="true"
+       android:inputType="number"
+       />
+
+    <CheckBoxPreference
+       android:key="set_comp_deflate"
+       android:title="@string/vpn_openconnect_comp_deflate"
+       android:summary="@string/vpn_openconnect_comp_deflate_summary"
+       />
+
+    <EditTextPreference
+       android:key="set_extra"
+       android:title="@string/vpn_openconnect_set_extra"
+       android:dialogTitle="@string/vpn_openconnect_set_extra"
+       android:dialogMessage="@string/vpn_openconnect_set_extra_message"
+       android:singleLine="true"
+       android:inputType="text"
+       />
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/vpn/OpenconnectAuthenticationActor.java b/src/com/android/settings/vpn/OpenconnectAuthenticationActor.java
new file mode 100644
index 0000000..0e46c64
--- /dev/null
+++ b/src/com/android/settings/vpn/OpenconnectAuthenticationActor.java
@@ -0,0 +1,66 @@
+/*
+ * 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 OpenvpnAuthenticationActor.java which was (same license):
+ *
+ * Copyright (C) 2010 James Bottomley <James.Bottomley at suse.de>
+ *
+ * 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.settings.vpn;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.net.vpn.OpenconnectProfile;
+import android.net.vpn.VpnProfile;
+
+/**
+ * A {@link VpnProfileActor} that provides an authentication view for users to
+ * input username and password before connecting to the VPN server.
+ */
+public class OpenconnectAuthenticationActor extends AuthenticationActor {
+
+    OpenconnectAuthenticationActor(Context c, VpnProfile p) {
+        super(c, p);
+    }
+
+    // @Override
+    public boolean isConnectDialogNeeded() {
+        OpenconnectProfile p = (OpenconnectProfile) getProfile();
+        return p.getUserAuth();
+    }
+
+    public void connect(Dialog d) {
+        if (d == null)
+            // null d means we don't need an authentication dialogue
+            // so skip it and pass in null user and password
+            connect((String) null, (String) null);
+        else
+            super.connect(d);
+    }
+}
diff --git a/src/com/android/settings/vpn/OpenconnectEditor.java b/src/com/android/settings/vpn/OpenconnectEditor.java
new file mode 100644
index 0000000..c4c619e
--- /dev/null
+++ b/src/com/android/settings/vpn/OpenconnectEditor.java
@@ -0,0 +1,300 @@
+/*
+ * 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 OpenvpnEditor.java which was (same license):
+ *
+ * Copyright (C) 2010 James Bottomley <James.Bottomley at suse.de>
+ *
+ *
+ *
+ * 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.settings.vpn;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.vpn.OpenconnectProfile;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+
+/**
+ * The class for editing {@link OpenconnectProfile}.
+ */
+class OpenconnectEditor extends VpnProfileEditor {
+
+    private static final String KEY_PROFILE = "openconnect_profile";
+
+    private static final int REQUEST_ADVANCED = 1;
+
+    private static final String TAG = OpenconnectEditor.class.getSimpleName();
+
+    private int MENU_ID_ADVANCED;
+
+    private KeyStore mKeyStore = KeyStore.getInstance();
+
+    private CheckBoxPreference mUserAuth;
+
+    private ListPreference mCert;
+
+    private ListPreference mSVRCert;
+
+    private ListPreference mCACert;
+
+    public OpenconnectEditor(OpenconnectProfile p) {
+        super(p);
+    }
+
+    @Override
+    protected void loadExtraPreferencesTo(PreferenceGroup subpanel) {
+        final Context c = subpanel.getContext();
+        final OpenconnectProfile profile = (OpenconnectProfile) getProfile();
+        mUserAuth = new CheckBoxPreference(c);
+        mUserAuth.setTitle(R.string.vpn_openconnect_userauth);
+        mUserAuth.setSummary(R.string.vpn_openconnect_userauth_summary);
+        mUserAuth.setChecked(profile.getUserAuth());
+        mUserAuth.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+            public boolean onPreferenceChange(Preference pref, Object newValue) {
+                boolean enabled = (Boolean) newValue;
+                profile.setUserAuth(enabled);
+                mUserAuth.setChecked(enabled);
+                return true;
+            }
+        });
+        subpanel.addPreference(mUserAuth);
+        mCACert = createList(c, R.string.vpn_ca_certificate_title, profile.getCAName(),
+                mKeyStore.saw(Credentials.CA_CERTIFICATE),
+                new Preference.OnPreferenceChangeListener() {
+                    public boolean onPreferenceChange(Preference pref, Object newValue) {
+                        String f = (String) newValue;
+                        profile.setCAName(f);
+                        setSummary(mCACert, R.string.vpn_ca_certificate, profile.getCAName());
+
+                        return true;
+                    }
+                });
+        setSummary(mCACert, R.string.vpn_ca_certificate, profile.getCAName());
+        subpanel.addPreference(mCACert);
+
+        mCert = createList(c, R.string.vpn_user_certificate_title, profile.getCertName(),
+                mKeyStore.saw(Credentials.USER_CERTIFICATE),
+                new Preference.OnPreferenceChangeListener() {
+                    public boolean onPreferenceChange(Preference pref, Object newValue) {
+                        String f = (String) newValue;
+                        profile.setCertName(f);
+                        setSummary(mCert, R.string.vpn_user_certificate, profile.getCertName());
+
+                        return true;
+                    }
+                });
+        setSummary(mCert, R.string.vpn_user_certificate, profile.getCertName());
+        subpanel.addPreference(mCert);
+
+        mSVRCert = createList(c, R.string.vpn_server_certificate_title, profile.getSVRCertName(),
+                mKeyStore.saw(Credentials.USER_CERTIFICATE),
+                new Preference.OnPreferenceChangeListener() {
+                    public boolean onPreferenceChange(Preference pref, Object newValue) {
+                        String f = (String) newValue;
+                        profile.setSVRCertName(f);
+                        setSummary(mSVRCert, R.string.vpn_server_certificate, profile.getSVRCertName());
+
+                        return true;
+                    }
+                });
+        setSummary(mSVRCert, R.string.vpn_server_certificate, profile.getSVRCertName());
+        subpanel.addPreference(mSVRCert);
+
+    }
+
+    @Override
+    public String validate() {
+        String result = super.validate();
+        if (result != null)
+            return result;
+
+        if (!mUserAuth.isChecked()) {
+            result = validate(mCert, R.string.vpn_a_user_certificate);
+            if (result != null)
+                return result;
+
+            result = validate(mCACert, R.string.vpn_a_ca_certificate);
+            if (result != null)
+                return result;
+        }
+
+        result = validate(mSVRCert, R.string.vpn_a_server_certificate);
+        if (result != null)
+            return result;
+
+        return null;
+    }
+
+    @Override
+    protected void onCreateOptionsMenu(Menu menu, int last_item) {
+        MENU_ID_ADVANCED = last_item + 1;
+
+        menu.add(0, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced).setIcon(
+                android.R.drawable.ic_menu_manage);
+    }
+
+    @Override
+    protected boolean onOptionsItemSelected(PreferenceActivity p, MenuItem item) {
+        if (item.getItemId() == MENU_ID_ADVANCED) {
+            Intent intent = new Intent(p, AdvancedSettings.class);
+            intent.putExtra(KEY_PROFILE, (Parcelable) getProfile());
+            p.startActivityForResult(intent, REQUEST_ADVANCED);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
+        if (requestCode != REQUEST_ADVANCED)
+            return;
+
+        OpenconnectProfile p = (OpenconnectProfile) getProfile();
+        OpenconnectProfile newP = data.getParcelableExtra(KEY_PROFILE);
+        if (newP == null) {
+            Log.e(TAG, "no profile from advanced settings");
+            return;
+        }
+        // manually copy across all advanced settings
+        p.setPort(newP.getPort());
+        p.setUseCompression(newP.getUseCompression());
+        p.setExtra(newP.getExtra());
+    }
+
+    private ListPreference createList(Context c, int titleResId, String selection, String[] keys,
+            Preference.OnPreferenceChangeListener listener) {
+        ListPreference pref = new ListPreference(c);
+        pref.setTitle(titleResId);
+        pref.setDialogTitle(titleResId);
+        pref.setPersistent(true);
+        pref.setEntries(keys);
+        pref.setEntryValues(keys);
+        pref.setValue(selection);
+        pref.setOnPreferenceChangeListener(listener);
+        return pref;
+    }
+
+    public static class AdvancedSettings extends PreferenceActivity {
+        private static final String KEY_PORT = "set_port";
+
+        private static final String KEY_COMP_DEFLATE = "set_comp_deflate";
+
+        private static final String KEY_EXTRA = "set_extra";
+
+        private EditTextPreference mPort;
+
+        private CheckBoxPreference mCompression;
+
+        private EditTextPreference mExtra;
+
+        private OpenconnectProfile profile;
+
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            profile = getIntent().getParcelableExtra(KEY_PROFILE);
+
+            addPreferencesFromResource(R.xml.openconnect_advanced_settings);
+
+            mPort = (EditTextPreference) findPreference(KEY_PORT);
+            mCompression = (CheckBoxPreference) findPreference(KEY_COMP_DEFLATE);
+            mExtra = (EditTextPreference) findPreference(KEY_EXTRA);
+
+            mPort.setSummary(profile.getPort());
+            mPort.setText(profile.getPort());
+            mPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference pref, Object newValue) {
+                    String name = (String) newValue;
+                    name.trim();
+                    profile.setPort(name);
+                    mPort.setSummary(profile.getPort());
+
+                    return true;
+                }
+            });
+
+            mCompression.setChecked(profile.getUseCompression());
+            mCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference pref, Object newValue) {
+                    Boolean b = (Boolean) newValue;
+                    profile.setUseCompression(b);
+
+                    return true;
+                }
+            });
+
+            mExtra.setSummary(profile.getExtra());
+            mExtra.setText(profile.getExtra());
+            mExtra.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference pref, Object newValue) {
+                    String name = (String) newValue;
+                    name.trim();
+                    profile.setExtra(name);
+                    mExtra.setSummary(profile.getExtra());
+                    return true;
+                }
+            });
+        }
+
+        @Override
+        public boolean onKeyDown(int keyCode, KeyEvent event) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_BACK:
+                    Intent intent = new Intent(this, VpnEditor.class);
+                    intent.putExtra(KEY_PROFILE, (Parcelable) profile);
+                    setResult(RESULT_OK, intent);
+
+                    finish();
+                    return true;
+            }
+            return super.onKeyDown(keyCode, event);
+        }
+
+    }
+}
diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java
index 9f4730b..95b3027 100644
--- a/src/com/android/settings/vpn/VpnEditor.java
+++ b/src/com/android/settings/vpn/VpnEditor.java
@@ -26,6 +26,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.VpnProfile;
 import android.net.vpn.VpnType;
@@ -187,6 +188,9 @@ public class VpnEditor extends PreferenceActivity {
             case OPENVPN:
                 return new OpenvpnEditor((OpenvpnProfile) p);
 
+            case OPENCONNECT:
+                return new OpenconnectEditor((OpenconnectProfile) p);
+
             case PPTP:
                 return new PptpEditor((PptpProfile) p);
 
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java
index b9d549c..7540d21 100644
--- a/src/com/android/settings/vpn/VpnSettings.java
+++ b/src/com/android/settings/vpn/VpnSettings.java
@@ -626,6 +626,9 @@ public class VpnSettings extends PreferenceActivity implements
             case OPENVPN:
                 return true;
 
+            case OPENCONNECT:
+                return true;
+
                // pass through
             default:
                 return false;
@@ -640,6 +643,9 @@ public class VpnSettings extends PreferenceActivity implements
             case OPENVPN:
                 return true;
 
+            case OPENCONNECT:
+                return true;
+
             case L2TP:
                 return ((L2tpProfile) p).isSecretEnabled();
 
@@ -875,6 +881,8 @@ public class VpnSettings extends PreferenceActivity implements
         switch (p.getType()) {
         case OPENVPN:
             return new OpenvpnAuthenticationActor(this, p);
+        case OPENCONNECT:
+            return new OpenconnectAuthenticationActor(this, p);
         default:
             return new AuthenticationActor(this, p);
         }
-- 
1.7.0.4




More information about the openconnect-devel mailing list