From 8e6775102cae857726601cc4f32dcb774cd4e50b Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 7 Mar 2013 22:22:42 +0100 Subject: Add x509-verify-name support to ics-openvpn GUI --HG-- extra : rebase_source : 58613dd0fdf7a9ea75d59b1ea16c68fb6524138b --- src/de/blinkt/openvpn/ConfigConverter.java | 1 + src/de/blinkt/openvpn/ConfigParser.java | 26 +++++ src/de/blinkt/openvpn/OpenVpnService.java | 2 +- src/de/blinkt/openvpn/RemoteCNPreference.java | 109 ++++++++++++++++++++ src/de/blinkt/openvpn/Settings_Authentication.java | 101 ++++++++++++------- src/de/blinkt/openvpn/VpnProfile.java | 111 +++++++++++++-------- 6 files changed, 274 insertions(+), 76 deletions(-) create mode 100644 src/de/blinkt/openvpn/RemoteCNPreference.java (limited to 'src/de') diff --git a/src/de/blinkt/openvpn/ConfigConverter.java b/src/de/blinkt/openvpn/ConfigConverter.java index d715e8b7..780018f9 100644 --- a/src/de/blinkt/openvpn/ConfigConverter.java +++ b/src/de/blinkt/openvpn/ConfigConverter.java @@ -111,6 +111,7 @@ public class ConfigConverter extends ListActivity { if(mResult.mRemoteCN.startsWith("/")) mResult.mRemoteCN = mResult.mRemoteCN.substring(1); mResult.mRemoteCN = mResult.mRemoteCN.replace("/", ", "); + mResult.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN; } public void showCertDialog () { diff --git a/src/de/blinkt/openvpn/ConfigParser.java b/src/de/blinkt/openvpn/ConfigParser.java index 60cf22b4..28608c9a 100644 --- a/src/de/blinkt/openvpn/ConfigParser.java +++ b/src/de/blinkt/openvpn/ConfigParser.java @@ -439,13 +439,39 @@ public class ConfigParser { np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; noauthtypeset=false; } + + Vector compatnames = getOption("compat-names",1,2); + Vector nonameremapping = getOption("no-name-remapping",1,1); Vector tlsremote = getOption("tls-remote",1,1); if(tlsremote!=null){ np.mRemoteCN = tlsremote.get(1); np.mCheckRemoteCN=true; + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE; + + if((compatnames!=null && compatnames.size() > 2) || + (nonameremapping!=null)) + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING; + } + + Vector x509verifyname = getOption("x509-verify-name",1,2); + if(x509verifyname!=null){ + np.mRemoteCN = x509verifyname.get(1); + np.mCheckRemoteCN=true; + if(x509verifyname.size()>2) { + if (x509verifyname.get(2).equals("name")) + np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN; + else if (x509verifyname.get(2).equals("name-prefix")) + np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; + else + throw new ConfigParseError("Unknown parameter to x509-verify-name: " + x509verifyname.get(2) ); + } else { + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN; + } + } + Vector verb = getOption("verb",1,1); if(verb!=null){ np.mVerb=verb.get(1); diff --git a/src/de/blinkt/openvpn/OpenVpnService.java b/src/de/blinkt/openvpn/OpenVpnService.java index 241bf774..e580089f 100644 --- a/src/de/blinkt/openvpn/OpenVpnService.java +++ b/src/de/blinkt/openvpn/OpenVpnService.java @@ -237,7 +237,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac // Registers BroadcastReceiver to track network connection changes. IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); mNetworkStateReceiver = new NetworkSateReceiver(magnagement); - this.registerReceiver(mNetworkStateReceiver, filter); + registerReceiver(mNetworkStateReceiver, filter); } synchronized void unregisterNetworkStateReceiver() { diff --git a/src/de/blinkt/openvpn/RemoteCNPreference.java b/src/de/blinkt/openvpn/RemoteCNPreference.java new file mode 100644 index 00000000..4daf54c2 --- /dev/null +++ b/src/de/blinkt/openvpn/RemoteCNPreference.java @@ -0,0 +1,109 @@ +package de.blinkt.openvpn; + +import android.content.Context; +import android.preference.DialogPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.util.AttributeSet; +import android.util.Pair; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; + +public class RemoteCNPreference extends DialogPreference { + + + private Spinner mSpinner; + private EditText mEditText; + private int mDNType; + private ArrayAdapter mAuthtypes; + private String mDn; + + public RemoteCNPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setDialogLayoutResource(R.layout.tlsremote); + + } + + @Override + protected void onBindDialogView(View view) { + + super.onBindDialogView(view); + + mEditText = (EditText) view.findViewById(R.id.tlsremotecn); + mSpinner = (Spinner) view.findViewById(R.id.x509verifytype); + if(mDn!=null) + mEditText.setText(mDn); + + populateSpinner(); + + } + + private void populateSpinner() { + mAuthtypes = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item); + mAuthtypes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + mAuthtypes.add(getContext().getString(R.string.complete_dn)); + mAuthtypes.add("RDN (common name)"); + mAuthtypes.add("RDN prefix"); + if (mDNType == VpnProfile.X509_VERIFY_TLSREMOTE || mDNType == VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING ) + mAuthtypes.add("tls-remote (DEPRECATED)"); + + mSpinner.setAdapter(mAuthtypes); + } + + public String getCNText() { + return mDn; + } + + public int getAuthtype() { + return mDNType; + } + + public void setDN(String dn) { + mDn = dn; + if(mEditText!=null) + mEditText.setText(dn); + } + + void setAuthType(int x509authtype) { + mDNType = x509authtype; + if (mSpinner!=null) + populateSpinner(); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (positiveResult) { + String dn = mEditText.getText().toString(); + int authtype = getAuthTypeFromSpinner(); + if (callChangeListener(new Pair(authtype, dn))) { + mDn = dn; + mDNType = authtype; + } + } + } + + private int getAuthTypeFromSpinner() { + int pos = mSpinner.getSelectedItemPosition(); + switch (pos) { + case 0: + return VpnProfile.X509_VERIFY_TLSREMOTE_DN; + case 1: + return VpnProfile.X509_VERIFY_TLSREMOTE_RDN; + case 2: + return VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; + case 3: + // This is the tls-remote entry, only visible if mDntype is a + // tls-remote type + return mDNType; + default: + return VpnProfile.X509_VERIFY_TLSREMOTE; + } + } + +} diff --git a/src/de/blinkt/openvpn/Settings_Authentication.java b/src/de/blinkt/openvpn/Settings_Authentication.java index 4e3f1e6f..8f73cd07 100644 --- a/src/de/blinkt/openvpn/Settings_Authentication.java +++ b/src/de/blinkt/openvpn/Settings_Authentication.java @@ -11,13 +11,14 @@ import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.SwitchPreference; +import android.util.Pair; public class Settings_Authentication extends OpenVpnPreferencesFragment implements OnPreferenceChangeListener, OnPreferenceClickListener { private static final int SELECT_TLS_FILE = 23223232; private CheckBoxPreference mExpectTLSCert; private CheckBoxPreference mCheckRemoteCN; - private EditTextPreference mRemoteCN; + private RemoteCNPreference mRemoteCN; private ListPreference mTLSAuthDirection; private Preference mTLSAuthFile; private SwitchPreference mUseTLSAuth; @@ -34,34 +35,36 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen mExpectTLSCert = (CheckBoxPreference) findPreference("remoteServerTLS"); mCheckRemoteCN = (CheckBoxPreference) findPreference("checkRemoteCN"); - mRemoteCN = (EditTextPreference) findPreference("remotecn"); + mRemoteCN = (RemoteCNPreference) findPreference("remotecn"); mRemoteCN.setOnPreferenceChangeListener(this); - + mUseTLSAuth = (SwitchPreference) findPreference("useTLSAuth" ); mTLSAuthFile = findPreference("tlsAuthFile"); mTLSAuthDirection = (ListPreference) findPreference("tls_direction"); - - + + mTLSAuthFile.setOnPreferenceClickListener(this); - + mCipher =(EditTextPreference) findPreference("cipher"); mCipher.setOnPreferenceChangeListener(this); - + mAuth =(EditTextPreference) findPreference("auth"); mAuth.setOnPreferenceChangeListener(this); - + loadSettings(); } @Override protected void loadSettings() { - + mExpectTLSCert.setChecked(mProfile.mExpectTLSCert); mCheckRemoteCN.setChecked(mProfile.mCheckRemoteCN); - mRemoteCN.setText(mProfile.mRemoteCN); - onPreferenceChange(mRemoteCN, mProfile.mRemoteCN); - + mRemoteCN.setDN(mProfile.mRemoteCN); + mRemoteCN.setAuthType(mProfile.mX509AuthType); + onPreferenceChange(mRemoteCN, + new Pair(mProfile.mX509AuthType, mProfile.mRemoteCN)); + mUseTLSAuth.setChecked(mProfile.mUseTLSAuth); mTlsAuthFileData= mProfile.mTLSAuthFilename; setTlsAuthSummary(mTlsAuthFileData); @@ -71,76 +74,106 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen mAuth.setText(mProfile.mAuth); onPreferenceChange(mAuth, mProfile.mAuth); } - + @Override protected void saveSettings() { mProfile.mExpectTLSCert=mExpectTLSCert.isChecked(); mProfile.mCheckRemoteCN=mCheckRemoteCN.isChecked(); - mProfile.mRemoteCN=mRemoteCN.getText(); - + mProfile.mRemoteCN=mRemoteCN.getCNText(); + mProfile.mX509AuthType=mRemoteCN.getAuthtype(); + mProfile.mUseTLSAuth = mUseTLSAuth.isChecked(); mProfile.mTLSAuthFilename = mTlsAuthFileData; - + if(mTLSAuthDirection.getValue()==null) mProfile.mTLSAuthDirection=null; else mProfile.mTLSAuthDirection = mTLSAuthDirection.getValue().toString(); - + if(mCipher.getText()==null) mProfile.mCipher=null; else mProfile.mCipher = mCipher.getText(); - + if(mAuth.getText()==null) mProfile.mAuth = null; else mProfile.mAuth = mAuth.getText(); - + } - - + + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if(preference==mRemoteCN) { - if ("".equals(newValue)) - preference.setSummary(mProfile.mServerName); + @SuppressWarnings("unchecked") + int authtype = ((Pair) newValue).first; + @SuppressWarnings("unchecked") + String dn = ((Pair) newValue).second; + + if ("".equals(dn)) + preference.setSummary(getX509String(VpnProfile.X509_VERIFY_TLSREMOTE_RDN, mProfile.mServerName)); else - preference.setSummary((String)newValue); + preference.setSummary(getX509String(authtype,dn)); + } else if (preference == mCipher || preference == mAuth) { preference.setSummary((CharSequence) newValue); } return true; } + private CharSequence getX509String(int authtype, String dn) { + String ret =""; + switch (authtype) { + case VpnProfile.X509_VERIFY_TLSREMOTE: + case VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: + ret+="tls-remote "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_DN: + ret="dn: "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN: + ret="rdn: "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX: + ret="rdn prefix: "; + break; + } + return ret + dn; + } + void startFileDialog() { Intent startFC = new Intent(getActivity(),FileSelect.class); startFC.putExtra(FileSelect.START_DATA, Environment.getExternalStorageDirectory().getPath()); - + startActivityForResult(startFC,SELECT_TLS_FILE); } @Override public boolean onPreferenceClick(Preference preference) { startFileDialog(); return true; - + } - + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode==SELECT_TLS_FILE && resultCode == Activity.RESULT_OK){ - String result = data.getStringExtra(FileSelect.RESULT_DATA); - mTlsAuthFileData=result; - setTlsAuthSummary(result); - + String result = data.getStringExtra(FileSelect.RESULT_DATA); + mTlsAuthFileData=result; + setTlsAuthSummary(result); + } } private void setTlsAuthSummary(String result) { if(result==null) result = getString(R.string.no_certificate); if(result.startsWith(VpnProfile.INLINE_TAG)) - mTLSAuthFile.setSummary(R.string.inline_file_data); - else - mTLSAuthFile.setSummary(result); + mTLSAuthFile.setSummary(R.string.inline_file_data); + else + mTLSAuthFile.setSummary(result); } } \ No newline at end of file diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index ea034b55..fcb0679f 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -57,6 +57,14 @@ public class VpnProfile implements Serializable{ public static final int TYPE_USERPASS_PKCS12 = 6; public static final int TYPE_USERPASS_KEYSTORE = 7; + public static final int X509_VERIFY_TLSREMOTE=0; + public static final int X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING=1; + public static final int X509_VERIFY_TLSREMOTE_DN=2; + public static final int X509_VERIFY_TLSREMOTE_RDN=3; + public static final int X509_VERIFY_TLSREMOTE_RDN_PREFIX=4; + + + // Don't change this, not all parts of the program use this constant public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID"; public static final String INLINE_TAG = "[[INLINE]]"; @@ -119,10 +127,11 @@ public class VpnProfile implements Serializable{ public String mConnectRetry="5"; public boolean mUserEditable=true; public String mAuth=""; - + public int mX509AuthType=X509_VERIFY_TLSREMOTE_RDN; + static final String MINIVPN = "miniopenvpn"; - - + + static private native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException; static { System.loadLibrary("opvpnutil"); @@ -211,16 +220,16 @@ public class VpnProfile implements Serializable{ if(mConnectRetryMax ==null) { mConnectRetryMax="5"; } - + if(!mConnectRetryMax.equals("-1")) - cfg+="connect-retry-max " + mConnectRetryMax+ "\n"; - + cfg+="connect-retry-max " + mConnectRetryMax+ "\n"; + if(mConnectRetry==null) mConnectRetry="5"; - - + + cfg+="connect-retry " + mConnectRetry + "\n"; - + cfg+="resolv-retry 60\n"; @@ -330,7 +339,7 @@ public class VpnProfile implements Serializable{ cfg+="max-routes " + numroutes + "\n"; } cfg+=routes; - + if(mOverrideDNS || !mUsePull) { if(nonNull(mDNS1)) cfg+="dhcp-option DNS " + mDNS1 + "\n"; @@ -349,9 +358,29 @@ public class VpnProfile implements Serializable{ // Authentication if(mCheckRemoteCN) { if(mRemoteCN == null || mRemoteCN.equals("") ) - cfg+="tls-remote " + mServerName + "\n"; - else - cfg += "tls-remote " + openVpnEscape(mRemoteCN) + "\n"; + cfg+="x509-verify-name " + mServerName + " name\n"; + else + switch (mX509AuthType) { + + // 2.2 style x509 checks + case X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: + cfg+="compat-names no-remapping\n"; + case X509_VERIFY_TLSREMOTE: + cfg+="tls-remote " + openVpnEscape(mRemoteCN) + "\n"; + break; + + case X509_VERIFY_TLSREMOTE_RDN: + cfg+="x509-verify-name " + openVpnEscape(mRemoteCN) + " name\n"; + break; + + case X509_VERIFY_TLSREMOTE_RDN_PREFIX: + cfg+="x509-verify-name " + openVpnEscape(mRemoteCN) + " name-prefix\n"; + break; + + case X509_VERIFY_TLSREMOTE_DN: + cfg+="x509-verify-name " + openVpnEscape(mRemoteCN) + "\n"; + break; + } } if(mExpectTLSCert) cfg += "remote-cert-tls server\n"; @@ -377,15 +406,15 @@ public class VpnProfile implements Serializable{ cfg+= "# persist-tun also sets persist-remote-ip to avoid DNS resolve problem\n"; cfg+= "persist-remote-ip\n"; } - + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true); if(usesystemproxy) { cfg+= "# Use system proxy setting\n"; cfg+= "management-query-proxy\n"; } - - + + if(mUseCustomConfig) { cfg += "# Custom configuration options\n"; cfg += "# You are on your on own here :)\n"; @@ -536,17 +565,17 @@ public class VpnProfile implements Serializable{ try { privateKey = KeyChain.getPrivateKey(context,mAlias); mPrivateKey = privateKey; - + cachain = KeyChain.getCertificateChain(context, mAlias); if(cachain.length <= 1 && !nonNull(mCaFilename)) OpenVPN.logMessage(0, "", context.getString(R.string.keychain_nocacert)); - + for(X509Certificate cert:cachain) { OpenVPN.logInfo(R.string.cert_from_keystore,cert.getSubjectDN()); } - - - + + + if(nonNull(mCaFilename)) { try { @@ -554,15 +583,15 @@ public class VpnProfile implements Serializable{ X509Certificate[] newcachain = new X509Certificate[cachain.length+1]; for(int i=0;i