diff options
-rw-r--r-- | res/layout/basic_settings.xml | 23 | ||||
-rw-r--r-- | res/values/strings.xml | 5 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/LaunchVPN.java | 12 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/Settings_Basic.java | 17 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/VpnProfile.java | 86 |
5 files changed, 112 insertions, 31 deletions
diff --git a/res/layout/basic_settings.xml b/res/layout/basic_settings.xml index 6d4b8cc1..d47ba207 100644 --- a/res/layout/basic_settings.xml +++ b/res/layout/basic_settings.xml @@ -140,6 +140,29 @@ android:layout_width="match_parent" android:layout_height="wrap_content" blinkt:title="@string/client_key_title" /> + + <LinearLayout + android:id="@+id/key_password_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" > + + <TextView + style="@style/item" + android:layout_marginLeft="8dip" + android:layout_marginRight="8dip" + android:text="@string/private_key_password" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + <EditText + android:id="@+id/key_password" + style="@style/item" + android:layout_marginLeft="8dip" + android:layout_marginRight="8dip" + android:hint="@string/pw_query_hint" + android:inputType="textPassword" /> + </LinearLayout> </LinearLayout> <LinearLayout diff --git a/res/values/strings.xml b/res/values/strings.xml index fc684ae6..005dc587 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -225,5 +225,8 @@ <string name="converted_profile_i">imported profile %d</string> <string name="broken_images">Broken Images</string> <string name="broken_images_faq"><p>Offical HTC images are known to have a strange routing problem causing traffic not to flow through the tunnel (See also <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=18\">Issue 18</a> in the bug tracker.)</p><p>The offical SONY images from Xperia arc S and Xperia Ray have been reported to be missing the VPNService completly from the image. Other Sony images may be affected as well. (See also <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=29\">Issue 29</a> in the bug tracker.)</p><p>On custom build images the tun module might be missing or the rights of /dev/tun might be wrong. On the experimental CM9 Defy image a <i>chown system /dev/tun</i> is needed to make the VPNService API work.</p></string> - <string name="error_empty_username">The username must not be empty.</string> + <string name="error_empty_username">The username must not be empty.</string> + <string name="pkcs12_file_encryption_key">PKCS12 File Encryption Key</string> + <string name="private_key_password">Private Key Password</string> + <string name="password">Password</string> </resources> diff --git a/src/de/blinkt/openvpn/LaunchVPN.java b/src/de/blinkt/openvpn/LaunchVPN.java index 73b1f34d..b031fe2f 100644 --- a/src/de/blinkt/openvpn/LaunchVPN.java +++ b/src/de/blinkt/openvpn/LaunchVPN.java @@ -243,7 +243,7 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { } } - private void askForPW(final String type) { + private void askForPW(final int type) { final EditText entry = new EditText(this); entry.setSingleLine(); @@ -251,7 +251,7 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { entry.setTransformationMethod(new PasswordTransformationMethod()); AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle("Need " + type); + dialog.setTitle("Need " + getString(type)); dialog.setMessage("Enter the password for profile " + mSelectedProfile.mName); dialog.setView(entry); @@ -260,7 +260,7 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { @Override public void onClick(DialogInterface dialog, int which) { String pw = entry.getText().toString(); - if(type.equals("Password")) { + if(type == R.string.password) { mSelectedProfile.mTransientPW = pw; } else { mSelectedProfile.mTransientPCKS12PW = pw; @@ -287,9 +287,9 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { if(requestCode==START_VPN_PROFILE) { if(resultCode == Activity.RESULT_OK) { - - if(mSelectedProfile.needUserPWInput()!=null) { - askForPW(mSelectedProfile.needUserPWInput()); + int needpw = mSelectedProfile.needUserPWInput(); + if(needpw !=0) { + askForPW(needpw); } else { new startOpenVpnThread().start(); } diff --git a/src/de/blinkt/openvpn/Settings_Basic.java b/src/de/blinkt/openvpn/Settings_Basic.java index 3eb484cb..72594851 100644 --- a/src/de/blinkt/openvpn/Settings_Basic.java +++ b/src/de/blinkt/openvpn/Settings_Basic.java @@ -82,6 +82,7 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On private VpnProfile mProfile; private EditText mProfileName; + private EditText mKeyPassword; @@ -119,7 +120,7 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mUserName = (EditText) mView.findViewById(R.id.auth_username); mPassword = (EditText) mView.findViewById(R.id.auth_password); - + mKeyPassword = (EditText) mView.findViewById(R.id.key_password); @@ -158,8 +159,15 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On String filedata = data.getStringExtra(FileSelect.RESULT_DATA); FileSelectLayout fsl = fileselects.get(request); fsl.setData(filedata); + + savePreferences(); + + // Private key files may result in showing/hiding the private key password dialog + if(fsl==mClientKey) { + changeType(mType.getSelectedItemPosition()); + } } - savePreferences(); + } @@ -190,6 +198,7 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mView.findViewById(R.id.keystore).setVisibility(View.GONE); mView.findViewById(R.id.cacert).setVisibility(View.GONE); mView.findViewById(R.id.userpassword).setVisibility(View.GONE); + mView.findViewById(R.id.key_password_layout).setVisibility(View.GONE); // Fallthroughs are by desing switch(type) { @@ -198,6 +207,8 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On case VpnProfile.TYPE_CERTIFICATES: mView.findViewById(R.id.certs).setVisibility(View.VISIBLE); mView.findViewById(R.id.cacert).setVisibility(View.VISIBLE); + if(mProfile.requireTLSKeyPassword()) + mView.findViewById(R.id.key_password_layout).setVisibility(View.VISIBLE); break; case VpnProfile.TYPE_USERPASS_PKCS12: mView.findViewById(R.id.userpassword).setVisibility(View.VISIBLE); @@ -237,6 +248,7 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mPKCS12Password.setText(mProfile.mPKCS12Password); mUserName.setText(mProfile.mUsername); mPassword.setText(mProfile.mPassword); + mKeyPassword.setText(mProfile.mKeyPassword); setAlias(); @@ -260,6 +272,7 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mProfile.mPassword = mPassword.getText().toString(); mProfile.mUsername = mUserName.getText().toString(); + mProfile.mKeyPassword = mKeyPassword.getText().toString(); } diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index 125dc675..8d721b38 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -3,6 +3,7 @@ package de.blinkt.openvpn; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Serializable; @@ -37,7 +38,7 @@ public class VpnProfile implements Serializable{ public static final int TYPE_USERPASS_CERTIFICATES = 5; public static final int TYPE_USERPASS_PKCS12 = 6; public static final int TYPE_USERPASS_KEYSTORE = 7; - + // 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]]"; @@ -93,6 +94,7 @@ public class VpnProfile implements Serializable{ public boolean mNobind=false; public boolean mUseDefaultRoutev6=true; public String mCustomRoutesv6=""; + public String mKeyPassword=""; public void clearDefaults() { @@ -112,7 +114,7 @@ public class VpnProfile implements Serializable{ escapedString = escapedString.replace("\n","\\n"); return '"' + escapedString + '"'; } - + static final String OVPNCONFIGPKCS12 = "android.pkcs12"; @@ -144,6 +146,7 @@ public class VpnProfile implements Serializable{ cfg +=cacheDir.getAbsolutePath() + "/" + "mgmtsocket"; cfg += " unix\n"; + cfg+= "management-query-passwords\n"; cfg += "management-hold\n\n"; cfg+="# /tmp does not exist on Android\n"; @@ -154,7 +157,7 @@ public class VpnProfile implements Serializable{ cfg+="# Log window is better readable this way\n"; cfg+="suppress-timestamps\n"; - + boolean useTLSClient = (mAuthenticationType != TYPE_STATICKEYS); @@ -196,7 +199,6 @@ public class VpnProfile implements Serializable{ switch(mAuthenticationType) { case VpnProfile.TYPE_USERPASS_CERTIFICATES: cfg+="auth-user-pass\n"; - cfg+="management-query-passwords\n"; case VpnProfile.TYPE_CERTIFICATES: // Ca cfg+=insertFileData("ca",mCaFilename); @@ -210,7 +212,6 @@ public class VpnProfile implements Serializable{ cfg+="auth-user-pass\n"; case VpnProfile.TYPE_PKCS12: cfg+=insertFileData("pkcs12",mPKCS12Filename); - cfg+="management-query-passwords\n"; break; case VpnProfile.TYPE_USERPASS_KEYSTORE: @@ -219,11 +220,9 @@ public class VpnProfile implements Serializable{ cfg+="pkcs12 "; cfg+=cacheDir.getAbsolutePath() + "/" + OVPNCONFIGPKCS12; cfg+="\n"; - cfg+="management-query-passwords\n"; break; case VpnProfile.TYPE_USERPASS: cfg+="auth-user-pass\n"; - cfg+="management-query-passwords\n"; cfg+=insertFileData("ca",mCaFilename); } @@ -237,7 +236,7 @@ public class VpnProfile implements Serializable{ else cfg+=insertFileData("tls-auth",mTLSAuthFilename); cfg+=" "; - + if(nonNull(mTLSAuthDirection)) { cfg+= "key-direction "; cfg+= mTLSAuthDirection; @@ -248,7 +247,7 @@ public class VpnProfile implements Serializable{ if(!mUsePull ) { if(nonNull(mIPv4Address)) cfg +="ifconfig " + cidrToIPAndNetmask(mIPv4Address) + "\n"; - + if(nonNull(mIPv6Address)) cfg +="ifconfig-ipv6 " + mIPv6Address + "\n"; } @@ -263,7 +262,7 @@ public class VpnProfile implements Serializable{ cfg += "route " + route + "\n"; } - + if(mUseDefaultRoutev6) cfg += "route-ipv6 ::/0\n"; else @@ -278,7 +277,7 @@ public class VpnProfile implements Serializable{ cfg+="dhcp-option DNS " + mDNS2 + "\n"; } - + if(mNobind) cfg+="nobind\n"; @@ -373,8 +372,8 @@ public class VpnProfile implements Serializable{ return cidrRoutes; } - - + + private String cidrToIPAndNetmask(String route) { String[] parts = route.split("/"); @@ -414,10 +413,10 @@ public class VpnProfile implements Serializable{ args.add("--config"); args.add(cacheDir.getAbsolutePath() + "/" + OVPNCONFIGFILE); // Silences script security warning - + args.add("script-security"); args.add("0"); - + return (String[]) args.toArray(new String[args.size()]); } @@ -433,7 +432,7 @@ public class VpnProfile implements Serializable{ intent.putExtra(prefix + ".ARGV" , buildOpenvpnArgv(context.getCacheDir())); intent.putExtra(prefix + ".profileUUID", mUuid.toString()); - + ApplicationInfo info = context.getApplicationInfo(); intent.putExtra(prefix +".nativelib",info.nativeLibraryDir); @@ -534,11 +533,12 @@ public class VpnProfile implements Serializable{ case TYPE_USERPASS_PKCS12: return mPKCS12Password; + case TYPE_CERTIFICATES: + case TYPE_USERPASS_CERTIFICATES: + return mKeyPassword; case TYPE_USERPASS: case TYPE_STATICKEYS: - case TYPE_CERTIFICATES: - case TYPE_USERPASS_CERTIFICATES: default: return null; } @@ -556,18 +556,60 @@ public class VpnProfile implements Serializable{ } } - public String needUserPWInput() { + + public boolean requireTLSKeyPassword() { + if(!nonNull(mClientKeyFilename)) + return false; + + String data = ""; + if(mClientKeyFilename.startsWith(INLINE_TAG)) + data = mClientKeyFilename; + else { + char[] buf = new char[2048]; + FileReader fr; + try { + fr = new FileReader(mClientKeyFilename); + int len = fr.read(buf); + while(len > 0 ) { + data += new String(buf,0,len); + len = fr.read(buf); + } + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + return false; + } + + } + + if(data.contains("Proc-Type: 4,ENCRYPTED")) + return true; + else if(data.contains("-----BEGIN ENCRYPTED PRIVATE KEY-----")) + return true; + else + return false; + } + + public int needUserPWInput() { if((mAuthenticationType == TYPE_PKCS12 || mAuthenticationType == TYPE_USERPASS_PKCS12)&& (mPKCS12Password.equals("") || mPKCS12Password == null)) { if(mTransientPCKS12PW==null) - return "PKCS12 File Encryption Key"; + return R.string.pkcs12_file_encryption_key; + } + + if(mAuthenticationType == TYPE_CERTIFICATES || mAuthenticationType == TYPE_USERPASS_CERTIFICATES) { + if(requireTLSKeyPassword() && !nonNull(mKeyPassword)) + if(mTransientPCKS12PW==null) { + return R.string.private_key_password; + } } + if(isUserPWAuth() && (mPassword.equals("") || mPassword == null)) { if(mTransientPW==null) - return "Password"; + return R.string.password; } - return null; + return 0; } public String getPasswordAuth() { |