diff options
| -rw-r--r-- | res/layout/config_converter.xml | 22 | ||||
| -rw-r--r-- | res/values/strings.xml | 3 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/ConfigConverter.java | 233 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/Settings_Basic.java | 8 | 
4 files changed, 216 insertions, 50 deletions
| diff --git a/res/layout/config_converter.xml b/res/layout/config_converter.xml new file mode 100644 index 00000000..13733fb3 --- /dev/null +++ b/res/layout/config_converter.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:orientation="vertical" > +<CheckBox +            android:id="@+id/importpkcs12" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:checked="true" +            android:text="@string/importpkcs12fromconfig" +            android:visibility="gone" +            /> +        <ListView +            android:id="@android:id/list" +            android:layout_width="fill_parent" +            android:layout_height="fill_parent" /> + +         +         +    </LinearLayout> + diff --git a/res/values/strings.xml b/res/values/strings.xml index f33cd74d..fca7a777 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -138,7 +138,7 @@      <string name="ip_not_cidr">Got interface information %1$s and %2$s, assuming second address is peer address of remote. Using /32 netmask for local IP. Mode given by OpenVPN is \"%3$s\".</string>      <string name="route_not_cidr">Cannot make sense of %1$s and %2$s as IP route with CIDR netmask, using /32 as netmask.</string>      <string name="route_not_netip">Corrected route %1$s/%2$s to %3$s/%2$s</string> -    <string name="keychain_access">Cannot access the Android Keychain Certificates. If you restored a backup of the app/app settings re-select the certificate to recreate the permission to access the certificate.</string> +    <string name="keychain_access">Cannot access the Android Keychain Certificates. (Can be caused by a firmware upgrade or by restoring a backup of the app/app settings). Please edit the VPN and reselect the certificate under basic settings to recreate the permission to access the certificate.</string>      <string name="version_info">%1$s %2$s</string>      <string name="send_logfile">Send log file</string>      <string name="send">Send</string> @@ -237,4 +237,5 @@      <string name="faq_howto_title">Quick Start</string>      <string name="setting_loadtun_summary">Try to load the tun.ko kernel module before trying to connect. Needs rooted devices.</string>      <string name="setting_loadtun">Load tun module</string> +    <string name="importpkcs12fromconfig">Import PKCS12 from configuration into Android Keystore</string>  </resources> diff --git a/src/de/blinkt/openvpn/ConfigConverter.java b/src/de/blinkt/openvpn/ConfigConverter.java index ed252f7d..ee5a4415 100644 --- a/src/de/blinkt/openvpn/ConfigConverter.java +++ b/src/de/blinkt/openvpn/ConfigConverter.java @@ -10,14 +10,21 @@ import java.util.List;  import java.util.Vector;
  import android.app.Activity;
 +import android.app.AlertDialog;
 +import android.app.AlertDialog.Builder;
  import android.app.ListActivity;
 +import android.content.ActivityNotFoundException;
  import android.content.Intent;
  import android.os.Bundle;
  import android.os.Environment;
 +import android.security.KeyChain;
 +import android.security.KeyChainAliasCallback;
  import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
 +import android.view.View;
  import android.widget.ArrayAdapter;
 +import android.widget.CheckBox;
  import de.blinkt.openvpn.ConfigParser.ConfigParseError;
  public class ConfigConverter extends ListActivity {
 @@ -29,13 +36,20 @@ public class ConfigConverter extends ListActivity {  	private List<String> mPathsegments;
 +	private String mAliasName=null;
 +
 +	private int RESULT_INSTALLPKCS12 = 7;
 +
 +	private String mPossibleName=null;
  	@Override
  	protected void onCreate(Bundle savedInstanceState) {
  		super.onCreate(savedInstanceState);
 +		setContentView(R.layout.config_converter);
  	}
 -
 +	
 +	
  	@Override
  	public boolean onOptionsItemSelected(MenuItem item) {
  		if(item.getItemId()==R.id.cancel){
 @@ -47,16 +61,13 @@ public class ConfigConverter extends ListActivity {  				return true;
  			}
 -			Intent result = new Intent();
 -			ProfileManager vpl = ProfileManager.getInstance(this);
 +			Intent in = installPKCS12();
 -			setUniqueProfileName(vpl);
 -			vpl.addProfile(mResult);
 -			vpl.saveProfile(this, mResult);
 -			vpl.saveProfileList(this);
 -			result.putExtra(VpnProfile.EXTRA_PROFILEUUID,mResult.getUUID().toString());
 -			setResult(Activity.RESULT_OK, result);
 -			finish();
 +			if(in != null)
 +				startActivityForResult(in, RESULT_INSTALLPKCS12);
 +			else
 +				saveProfile();
 +
  			return true;
  		}
 @@ -64,16 +75,101 @@ public class ConfigConverter extends ListActivity {  	}
 +	@Override
 +	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 +		if(requestCode==RESULT_INSTALLPKCS12) {
 +			if(resultCode==Activity.RESULT_OK) {
 +				showCertDialog();
 +			}
 +		}
 +
 +		super.onActivityResult(requestCode, resultCode, data);
 +	}
 +
 +	private void saveProfile() {
 +		Intent result = new Intent();
 +		ProfileManager vpl = ProfileManager.getInstance(this);
 +
 +		setUniqueProfileName(vpl);
 +		vpl.addProfile(mResult);
 +		vpl.saveProfile(this, mResult);
 +		vpl.saveProfileList(this);
 +		result.putExtra(VpnProfile.EXTRA_PROFILEUUID,mResult.getUUID().toString());
 +		setResult(Activity.RESULT_OK, result);
 +		finish();
 +	}
 +	
 +	public void showCertDialog () {
 +		try	{
 +			KeyChain.choosePrivateKeyAlias(this,
 +					new KeyChainAliasCallback() {
 +
 +				public void alias(String alias) {
 +					// Credential alias selected.  Remember the alias selection for future use.
 +					mResult.mAlias=alias;
 +					saveProfile();
 +				}
 +
 +
 +			},
 +			new String[] {"RSA"}, // List of acceptable key types. null for any
 +			null,                        // issuer, null for any
 +			mResult.mServerName,      // host name of server requesting the cert, null if unavailable
 +			-1,                         // port of server requesting the cert, -1 if unavailable
 +			mAliasName);                       // alias to preselect, null if unavailable
 +		} catch (ActivityNotFoundException anf) {
 +			Builder ab = new AlertDialog.Builder(this);
 +			ab.setTitle(R.string.broken_image_cert_title);
 +			ab.setMessage(R.string.broken_image_cert);
 +			ab.setPositiveButton(android.R.string.ok, null);
 +			ab.show();
 +		}
 +	}
 +
 +
 +	private Intent installPKCS12() {
 +		if(!((CheckBox)findViewById(R.id.importpkcs12)).isChecked())
 +			return null;
 +		File possiblepkcs12 = findFile(mResult.mPKCS12Filename);
 +
 +		if(possiblepkcs12!=null) {
 +			Intent inkeyintent = KeyChain.createInstallIntent();
 +			byte[] pkcs12data;
 +			try {
 +				pkcs12data = readBytesFromFile(possiblepkcs12);
 +			} catch (IOException e) {
 +				return null;
 +			}
 +
 +			inkeyintent.putExtra(KeyChain.EXTRA_PKCS12,pkcs12data );
 +
 +			mAliasName = possiblepkcs12.getName().replace(".p12", "");
 +			if(mAliasName.equals(""))
 +				mAliasName=null;
 +
 +			if(mAliasName!=null){
 +				inkeyintent.putExtra(KeyChain.EXTRA_NAME, mAliasName);
 +			}
 +			return inkeyintent;
 +
 +		}
 +		return null;
 +	}
 +
  	private void setUniqueProfileName(ProfileManager vpl) {
 -		int i=1;
 -		String newname = getString(R.string.converted_profile);
 -		
 +		int i=0;
 +
 +		String newname = mPossibleName;
 +
  		while(vpl.getProfileByName(newname)!=null) {
  			i++;
 -			newname = getString(R.string.converted_profile_i,i);
 +			if(i==1)
 +				newname = getString(R.string.converted_profile);
 +			else
 +				newname = getString(R.string.converted_profile_i,i);
  		}
 -		
 +
  		mResult.mName=newname;
  	}
 @@ -84,19 +180,32 @@ public class ConfigConverter extends ListActivity {  		return true;
  	}
 -
  	private String embedFile(String filename)
  	{
 -		if(filename == null || filename.equals(""))
 +		if(filename==null)
  			return null;
 +		
  		// Already embedded, nothing to do
  		if(filename.startsWith(VpnProfile.INLINE_TAG))
  			return filename;
 +		File possibleFile = findFile(filename);
 +		if(possibleFile==null)
 +			return null;
 +		else
 +			return readFileContent(possibleFile);
 +
 +	}
 +
 +	private File findFile(String filename)
 +	{
 +		if(filename == null || filename.equals(""))
 +			return null;
 +
  		// Try diffent path relative to /mnt/sdcard
  		File sdcard = Environment.getExternalStorageDirectory();
  		File root = new File("/");
 -		
 +
  		Vector<File> dirlist = new Vector<File>();
  		for(int i=mPathsegments.size()-1;i >=0 ;i--){
 @@ -108,8 +217,8 @@ public class ConfigConverter extends ListActivity {  		}
  		dirlist.add(sdcard);
  		dirlist.add(root);
 -		
 -		
 +
 +
  		String[] fileparts = filename.split("/");
  		for(File rootdir:dirlist){
  			String suffix="";
 @@ -124,26 +233,7 @@ public class ConfigConverter extends ListActivity {  					continue;
  				// read the file inline
 -
 -				String filedata =  "";
 -				byte[] buf =new byte[2048];
 -
 -				log(R.string.trying_to_read, possibleFile.getAbsolutePath());
 -				try {
 -					FileInputStream fis = new FileInputStream(possibleFile);
 -					int len = fis.read(buf);
 -					while( len > 0){
 -						filedata += new String(buf,0,len);
 -						len = fis.read(buf);
 -					}
 -					fis.close();
 -					return VpnProfile.INLINE_TAG + filedata;
 -				} catch (FileNotFoundException e) {
 -					log(e.getLocalizedMessage());
 -				} catch (IOException e) {
 -					log(e.getLocalizedMessage());
 -				}	
 -
 +				return possibleFile;
  			}
  		}
 @@ -151,6 +241,51 @@ public class ConfigConverter extends ListActivity {  		return null;
  	}
 +	String readFileContent(File possibleFile) {
 +		String filedata =  "";
 +		byte[] buf =new byte[2048];
 +
 +		log(R.string.trying_to_read, possibleFile.getAbsolutePath());
 +		try {
 +			FileInputStream fis = new FileInputStream(possibleFile);
 +			int len = fis.read(buf);
 +			while( len > 0){
 +				filedata += new String(buf,0,len);
 +				len = fis.read(buf);
 +			}
 +			fis.close();
 +			return VpnProfile.INLINE_TAG + filedata;
 +		} catch (FileNotFoundException e) {
 +			log(e.getLocalizedMessage());
 +		} catch (IOException e) {
 +			log(e.getLocalizedMessage());
 +		}	
 +
 +		return null;
 +	}
 +
 +
 +	private byte[] readBytesFromFile(File file) throws IOException {
 +		InputStream input = new FileInputStream(file);
 +
 +		long len= file.length();
 +
 +
 +		// Create the byte array to hold the data
 +		byte[] bytes = new byte[(int) len];
 +
 +		// Read in the bytes
 +		int offset = 0;
 +		int bytesRead = 0;
 +		while (offset < bytes.length
 +				&& (bytesRead=input.read(bytes, offset, bytes.length-offset)) >= 0) {
 +			offset += bytesRead;
 +		}
 +
 +		input.close();
 +		return bytes;
 +	}
 +
  	void embedFiles() {
  		// This where I would like to have a c++ style
  		// void embedFile(std::string & option)
 @@ -159,7 +294,7 @@ public class ConfigConverter extends ListActivity {  		mResult.mClientCertFilename = embedFile(mResult.mClientCertFilename);
  		mResult.mClientKeyFilename = embedFile(mResult.mClientKeyFilename);
  		mResult.mTLSAuthFilename = embedFile(mResult.mTLSAuthFilename);
 -		
 +
  		if(mResult.mUsername != null && !mResult.mUsername.equals("")){
  			String data =embedFile(mResult.mUsername);
  			mResult.mName=null;
 @@ -191,9 +326,16 @@ public class ConfigConverter extends ListActivity {  				//log(R.string.import_experimental);
  				log(R.string.importing_config,data.toString());
  				try {
 +					if(data.getScheme().equals("file")) {
 +						mPossibleName = data.getLastPathSegment();
 +						if(mPossibleName!=null){
 +							mPossibleName =mPossibleName.replace(".ovpn", "");
 +							mPossibleName =mPossibleName.replace(".conf", "");
 +						}
 +					}
  					InputStream is = getContentResolver().openInputStream(data);
  					mPathsegments = data.getPathSegments();
 -					
 +
  					doImport(is);
  				} catch (FileNotFoundException e) {
  					log(R.string.import_content_resolve_error);
 @@ -238,12 +380,13 @@ public class ConfigConverter extends ListActivity {  				int until = copt.indexOf('\n');
  				copt = copt.substring(until+1);
  			}
 -				
 +
  			log(copt);
  		}
 -		if(mResult.mAuthenticationType==VpnProfile.TYPE_KEYSTORE) {
 -			log(R.string.import_pkcs12_to_keystore);
 +		if(mResult.mAuthenticationType==VpnProfile.TYPE_KEYSTORE ||
 +				mResult.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
 +			findViewById(R.id.importpkcs12).setVisibility(View.VISIBLE);
  		}
  	}
 diff --git a/src/de/blinkt/openvpn/Settings_Basic.java b/src/de/blinkt/openvpn/Settings_Basic.java index 0bf3078a..3ac00a66 100644 --- a/src/de/blinkt/openvpn/Settings_Basic.java +++ b/src/de/blinkt/openvpn/Settings_Basic.java @@ -300,11 +300,11 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On  			}, -			new String[] {"RSA", "DSA"}, // List of acceptable key types. null for any +			new String[] {"RSA"}, // List of acceptable key types. null for any  			null,                        // issuer, null for any -			"internal.example.com",      // host name of server requesting the cert, null if unavailable -			443,                         // port of server requesting the cert, -1 if unavailable -			null);                       // alias to preselect, null if unavailable +			mProfile.mServerName,      // host name of server requesting the cert, null if unavailable +			-1,                         // port of server requesting the cert, -1 if unavailable +			mProfile.mAlias);                       // alias to preselect, null if unavailable  		} catch (ActivityNotFoundException anf) {  			Builder ab = new AlertDialog.Builder(getActivity());  			ab.setTitle(R.string.broken_image_cert_title); | 
