summaryrefslogtreecommitdiff
path: root/src/de/blinkt/openvpn
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/blinkt/openvpn')
-rw-r--r--src/de/blinkt/openvpn/CIDRIP.java10
-rw-r--r--src/de/blinkt/openvpn/ConfigParser.java6
-rw-r--r--src/de/blinkt/openvpn/FileSelect.java3
-rw-r--r--src/de/blinkt/openvpn/FileSelectionFragment.java5
-rw-r--r--src/de/blinkt/openvpn/InlineFileTab.java1
-rw-r--r--src/de/blinkt/openvpn/LogWindow.java51
-rw-r--r--src/de/blinkt/openvpn/NetworkSateReceiver.java16
-rw-r--r--src/de/blinkt/openvpn/OpenVPN.java62
-rw-r--r--src/de/blinkt/openvpn/OpenVPNMangement.java13
-rw-r--r--src/de/blinkt/openvpn/OpenVpnManagementThread.java137
-rw-r--r--src/de/blinkt/openvpn/OpenVpnService.java236
-rw-r--r--src/de/blinkt/openvpn/Settings_Authentication.java13
-rw-r--r--src/de/blinkt/openvpn/Settings_Basic.java17
-rw-r--r--src/de/blinkt/openvpn/ShowConfigFragment.java32
-rw-r--r--src/de/blinkt/openvpn/VpnProfile.java166
15 files changed, 513 insertions, 255 deletions
diff --git a/src/de/blinkt/openvpn/CIDRIP.java b/src/de/blinkt/openvpn/CIDRIP.java
index ccb3836a..41b56d4b 100644
--- a/src/de/blinkt/openvpn/CIDRIP.java
+++ b/src/de/blinkt/openvpn/CIDRIP.java
@@ -1,8 +1,12 @@
package de.blinkt.openvpn;
+import java.util.Locale;
+
class CIDRIP{
String mIp;
int len;
+
+
public CIDRIP(String ip, String mask){
mIp=ip;
long netmask=getInt(mask);
@@ -24,9 +28,13 @@ class CIDRIP{
}
}
+ public CIDRIP(String address, int prefix_length) {
+ len = prefix_length;
+ mIp = address;
+ }
@Override
public String toString() {
- return String.format("%s/%d",mIp,len);
+ return String.format(Locale.ENGLISH,"%s/%d",mIp,len);
}
public boolean normalise(){
diff --git a/src/de/blinkt/openvpn/ConfigParser.java b/src/de/blinkt/openvpn/ConfigParser.java
index 4eeaee86..99e7ec93 100644
--- a/src/de/blinkt/openvpn/ConfigParser.java
+++ b/src/de/blinkt/openvpn/ConfigParser.java
@@ -214,6 +214,7 @@ public class ConfigParser {
"route-up",
"ipchange",
"route-up",
+ "route-pre-down",
"auth-user-pass-verify",
"dhcp-release",
"dhcp-renew",
@@ -387,6 +388,11 @@ public class ConfigParser {
if(cipher!=null)
np.mCipher= cipher.get(1);
+ Vector<String> auth = getOption("auth", 1, 1);
+ if(auth!=null)
+ np.mAuth = auth.get(1);
+
+
Vector<String> ca = getOption("ca",1,1);
if(ca!=null){
np.mCaFilename = ca.get(1);
diff --git a/src/de/blinkt/openvpn/FileSelect.java b/src/de/blinkt/openvpn/FileSelect.java
index b6239433..18448706 100644
--- a/src/de/blinkt/openvpn/FileSelect.java
+++ b/src/de/blinkt/openvpn/FileSelect.java
@@ -199,7 +199,8 @@ public class FileSelect extends Activity {
public void saveInlineData(String string) {
Intent intent = new Intent();
- intent.putExtra(RESULT_DATA, string);
+
+ intent.putExtra(RESULT_DATA,VpnProfile.INLINE_TAG + string);
setResult(Activity.RESULT_OK,intent);
finish();
diff --git a/src/de/blinkt/openvpn/FileSelectionFragment.java b/src/de/blinkt/openvpn/FileSelectionFragment.java
index 2d8110e1..5badc6e0 100644
--- a/src/de/blinkt/openvpn/FileSelectionFragment.java
+++ b/src/de/blinkt/openvpn/FileSelectionFragment.java
@@ -4,6 +4,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.TreeMap;
import android.app.AlertDialog;
@@ -180,12 +181,12 @@ public class FileSelectionFragment extends ListFragment {
dirsPathMap.put(dirName, file.getPath());
} else {
final String fileName = file.getName();
- final String fileNameLwr = fileName.toLowerCase();
+ final String fileNameLwr = fileName.toLowerCase(Locale.getDefault());
// se ha um filtro de formatos, utiliza-o
if (formatFilter != null) {
boolean contains = false;
for (int i = 0; i < formatFilter.length; i++) {
- final String formatLwr = formatFilter[i].toLowerCase();
+ final String formatLwr = formatFilter[i].toLowerCase(Locale.getDefault());
if (fileNameLwr.endsWith(formatLwr)) {
contains = true;
break;
diff --git a/src/de/blinkt/openvpn/InlineFileTab.java b/src/de/blinkt/openvpn/InlineFileTab.java
index 1104d290..47c02a09 100644
--- a/src/de/blinkt/openvpn/InlineFileTab.java
+++ b/src/de/blinkt/openvpn/InlineFileTab.java
@@ -19,7 +19,6 @@ public class InlineFileTab extends Fragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-
mInlineData.setText(((FileSelect)getActivity()).getInlineData());
}
diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java
index 88615e12..4d2047f8 100644
--- a/src/de/blinkt/openvpn/LogWindow.java
+++ b/src/de/blinkt/openvpn/LogWindow.java
@@ -7,14 +7,17 @@ import android.app.AlertDialog.Builder;
import android.app.ListActivity;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
+import android.os.IBinder;
import android.os.Message;
import android.view.Menu;
import android.view.MenuInflater;
@@ -30,10 +33,31 @@ import android.widget.Toast;
import de.blinkt.openvpn.OpenVPN.LogItem;
import de.blinkt.openvpn.OpenVPN.LogListener;
import de.blinkt.openvpn.OpenVPN.StateListener;
+import de.blinkt.openvpn.OpenVpnService.LocalBinder;
public class LogWindow extends ListActivity implements StateListener {
private static final int START_VPN_CONFIG = 0;
private String[] mBconfig=null;
+ protected OpenVpnService mService;
+ private ServiceConnection mConnection = new ServiceConnection() {
+
+
+
+ @Override
+ public void onServiceConnected(ComponentName className,
+ IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ LocalBinder binder = (LocalBinder) service;
+ mService = binder.getService();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ mService =null;
+ }
+
+ };
+
class LogWindowListAdapter implements ListAdapter, LogListener, Callback {
@@ -198,6 +222,7 @@ public class LogWindow extends ListActivity implements StateListener {
private LogWindowListAdapter ladapter;
private TextView mSpeedView;
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.clearlog) {
@@ -213,7 +238,8 @@ public class LogWindow extends ListActivity implements StateListener {
@Override
public void onClick(DialogInterface dialog, int which) {
ProfileManager.setConntectedVpnProfileDisconnected(getApplicationContext());
- OpenVpnManagementThread.stopOpenVPN();
+ if(mService.getManagement()!=null)
+ mService.getManagement().stopVPN();
}
});
@@ -236,9 +262,18 @@ public class LogWindow extends ListActivity implements StateListener {
Toast.makeText(this, R.string.log_no_last_vpn, Toast.LENGTH_LONG).show();
}
+ } else if(item.getItemId() == android.R.id.home) {
+ // This is called when the Home (Up) button is pressed
+ // in the Action Bar.
+ Intent parentActivityIntent = new Intent(this, MainActivity.class);
+ parentActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(parentActivityIntent);
+ finish();
+ return true;
}
-
return super.onOptionsItemSelected(item);
}
@@ -324,6 +359,13 @@ public class LogWindow extends ListActivity implements StateListener {
lv.setAdapter(ladapter);
mSpeedView = (TextView) findViewById(R.id.speed);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+
+ Intent intent = new Intent(getBaseContext(), OpenVpnService.class);
+ intent.setAction(OpenVpnService.START_SERVICE);
+
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
}
@Override
@@ -335,6 +377,8 @@ public class LogWindow extends ListActivity implements StateListener {
String prefix=getString(resid) + ":";
if (status.equals("BYTECOUNT") || status.equals("NOPROCESS") )
prefix="";
+ if (resid==R.string.unknown_state)
+ prefix+=status;
mSpeedView.setText(prefix + logmessage);
}
});
@@ -343,8 +387,9 @@ public class LogWindow extends ListActivity implements StateListener {
@Override
protected void onDestroy() {
- super.onDestroy();
+ unbindService(mConnection);
OpenVPN.removeLogListener(ladapter);
+ super.onDestroy();
}
}
diff --git a/src/de/blinkt/openvpn/NetworkSateReceiver.java b/src/de/blinkt/openvpn/NetworkSateReceiver.java
index e20c8e52..487639a9 100644
--- a/src/de/blinkt/openvpn/NetworkSateReceiver.java
+++ b/src/de/blinkt/openvpn/NetworkSateReceiver.java
@@ -11,13 +11,13 @@ import android.preference.PreferenceManager;
public class NetworkSateReceiver extends BroadcastReceiver {
private int lastNetwork=-1;
- private OpenVpnManagementThread mManangement;
+ private OpenVPNMangement mManangement;
private String lastStateMsg=null;
- public NetworkSateReceiver(OpenVpnManagementThread managementThread) {
+ public NetworkSateReceiver(OpenVPNMangement magnagement) {
super();
- mManangement = managementThread;
+ mManangement = magnagement;
}
@Override
@@ -57,15 +57,19 @@ public class NetworkSateReceiver extends BroadcastReceiver {
if(networkInfo!=null && networkInfo.getState() == State.CONNECTED) {
int newnet = networkInfo.getType();
- if(sendusr1 && lastNetwork!=newnet)
- mManangement.reconnect();
+ if(sendusr1 && lastNetwork!=newnet) {
+ if (lastNetwork==-1)
+ mManangement.resume();
+ else
+ mManangement.reconnect();
+ }
lastNetwork = newnet;
} else if (networkInfo==null) {
// Not connected, stop openvpn, set last connected network to no network
lastNetwork=-1;
if(sendusr1)
- mManangement.signalusr1();
+ mManangement.pause();
}
if(!netstatestring.equals(lastStateMsg))
diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java
index 0de4bd11..2ca2d259 100644
--- a/src/de/blinkt/openvpn/OpenVPN.java
+++ b/src/de/blinkt/openvpn/OpenVPN.java
@@ -4,30 +4,33 @@ import java.util.LinkedList;
import java.util.Locale;
import java.util.Vector;
-
import android.content.Context;
import android.os.Build;
public class OpenVPN {
- private static final String NOPROCESS = "NOPROCESS";
public static LinkedList<LogItem> logbuffer;
private static Vector<LogListener> logListener;
private static Vector<StateListener> stateListener;
+ private static Vector<ByteCountListener> byteCountListener;
+
private static String[] mBconfig;
- private static String mLaststatemsg;
+ private static String mLaststatemsg="";
- private static String mLaststate=NOPROCESS;
+ private static String mLaststate = "NOPROCESS";
private static int mLastStateresid=R.string.state_noprocess;
+
+ private static long mlastByteCount[]={0,0,0,0};
static {
logbuffer = new LinkedList<LogItem>();
logListener = new Vector<OpenVPN.LogListener>();
stateListener = new Vector<OpenVPN.StateListener>();
+ byteCountListener = new Vector<OpenVPN.ByteCountListener>();
logInformation();
}
@@ -84,7 +87,7 @@ public class OpenVPN {
String str = String.format(Locale.ENGLISH,"Log (no context) resid %d", mRessourceId);
if(mArgs !=null)
for(Object o:mArgs)
- str += "|" + o.toString();
+ str += "|" + o.toString();
return str;
}
}
@@ -108,6 +111,10 @@ public class OpenVPN {
public interface StateListener {
void updateState(String state, String logmessage, int localizedResId);
}
+
+ public interface ByteCountListener {
+ void updateByteCount(long in, long out, long diffin, long diffout);
+ }
synchronized static void logMessage(int level,String prefix, String message)
{
@@ -132,14 +139,25 @@ public class OpenVPN {
public synchronized static void removeLogListener(LogListener ll) {
logListener.remove(ll);
}
+
+ public static void addByteCountListener(ByteCountListener bcl) {
+ bcl.updateByteCount(mlastByteCount[0], mlastByteCount[1], mlastByteCount[2], mlastByteCount[3]);
+ byteCountListener.add(bcl);
+ }
+
+ public static void removeByteCountListener(ByteCountListener bcl) {
+ byteCountListener.remove(bcl);
+ }
public synchronized static void addStateListener(StateListener sl){
- stateListener.add(sl);
- if(mLaststate!=null)
- sl.updateState(mLaststate, mLaststatemsg, mLastStateresid);
+ if(!stateListener.contains(sl)){
+ stateListener.add(sl);
+ if(mLaststate!=null)
+ sl.updateState(mLaststate, mLaststatemsg, mLastStateresid);
+ }
}
-
+
private static int getLocalizedState(String state){
if (state.equals("CONNECTING"))
return R.string.state_connecting;
@@ -155,6 +173,8 @@ public class OpenVPN {
return R.string.state_add_routes;
else if (state.equals("CONNECTED"))
return R.string.state_connected;
+ else if (state.equals("DISCONNECTED"))
+ return R.string.state_disconnected;
else if (state.equals("RECONNECTING"))
return R.string.state_reconnecting;
else if (state.equals("EXITING"))
@@ -200,12 +220,10 @@ public class OpenVPN {
}
public synchronized static void updateStateString(String state, String msg, int resid) {
- if (! "BYTECOUNT".equals(state)) {
- mLaststate= state;
- mLaststatemsg = msg;
- mLastStateresid = resid;
- }
-
+ mLaststate= state;
+ mLaststatemsg = msg;
+ mLastStateresid = resid;
+
for (StateListener sl : stateListener) {
sl.updateState(state,msg,resid);
}
@@ -241,4 +259,18 @@ public class OpenVPN {
newlogItem(new LogItem(LogItem.ERROR, ressourceId,args));
}
+ public static void updateByteCount(long in, long out) {
+ long lastIn = mlastByteCount[0];
+ long lastOut = mlastByteCount[1];
+ long diffin = in - lastIn;
+ long diffout = out - lastOut;
+
+ mlastByteCount = new long[] {in,out,diffin,diffout};
+ for(ByteCountListener bcl:byteCountListener){
+ bcl.updateByteCount(in, out, diffin,diffout);
+ }
+ }
+
+
+
}
diff --git a/src/de/blinkt/openvpn/OpenVPNMangement.java b/src/de/blinkt/openvpn/OpenVPNMangement.java
new file mode 100644
index 00000000..0c6d7163
--- /dev/null
+++ b/src/de/blinkt/openvpn/OpenVPNMangement.java
@@ -0,0 +1,13 @@
+package de.blinkt.openvpn;
+
+public interface OpenVPNMangement {
+
+ void reconnect();
+
+ void pause();
+
+ void resume();
+
+ boolean stopVPN();
+
+}
diff --git a/src/de/blinkt/openvpn/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/OpenVpnManagementThread.java
index 3d68d943..e4528132 100644
--- a/src/de/blinkt/openvpn/OpenVpnManagementThread.java
+++ b/src/de/blinkt/openvpn/OpenVpnManagementThread.java
@@ -7,27 +7,18 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.util.LinkedList;
+import java.util.Locale;
import java.util.Vector;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-
import android.content.SharedPreferences;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
-import android.util.Base64;
import android.util.Log;
-public class OpenVpnManagementThread implements Runnable {
+public class OpenVpnManagementThread implements Runnable, OpenVPNMangement {
private static final String TAG = "openvpn";
private LocalSocket mSocket;
@@ -35,8 +26,6 @@ public class OpenVpnManagementThread implements Runnable {
private OpenVpnService mOpenVPNService;
private LinkedList<FileDescriptor> mFDList=new LinkedList<FileDescriptor>();
private int mBytecountinterval=2;
- private long mLastIn=0;
- private long mLastOut=0;
private LocalServerSocket mServerSocket;
private boolean mReleaseHold=true;
private boolean mWaitingForRelease=false;
@@ -45,7 +34,6 @@ public class OpenVpnManagementThread implements Runnable {
private static Vector<OpenVpnManagementThread> active=new Vector<OpenVpnManagementThread>();
static private native void jniclose(int fdint);
- static private native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
public OpenVpnManagementThread(VpnProfile profile, LocalServerSocket mgmtsocket, OpenVpnService openVpnService) {
mProfile = profile;
@@ -261,7 +249,7 @@ public class OpenVpnManagementThread implements Runnable {
OpenVPN.logInfo(R.string.using_proxy, isa.getHostName(),isa.getPort());
- String proxycmd = String.format("proxy HTTP %s %d\n", isa.getHostName(),isa.getPort());
+ String proxycmd = String.format(Locale.ENGLISH,"proxy HTTP %s %d\n", isa.getHostName(),isa.getPort());
managmentCommand(proxycmd);
} else {
managmentCommand("proxy NONE\n");
@@ -284,30 +272,12 @@ public class OpenVpnManagementThread implements Runnable {
long in = Long.parseLong(argument.substring(0, comma));
long out = Long.parseLong(argument.substring(comma+1));
- long diffin = in - mLastIn;
- long diffout = out - mLastOut;
-
- mLastIn=in;
- mLastOut=out;
-
- String netstat = String.format("In: %8s, %8s/s Out %8s, %8s/s",
- humanReadableByteCount(in, false),
- humanReadableByteCount(diffin, false),
- humanReadableByteCount(out, false),
- humanReadableByteCount(diffout, false));
- OpenVPN.updateStateString("BYTECOUNT",netstat);
+ OpenVPN.updateByteCount(in,out);
}
- // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
- public static String humanReadableByteCount(long bytes, boolean si) {
- int unit = si ? 1000 : 1024;
- if (bytes < unit) return bytes + " B";
- int exp = (int) (Math.log(bytes) / Math.log(unit));
- String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
- return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
- }
+
private void processNeedCommand(String argument) {
int p1 =argument.indexOf('\'');
@@ -460,7 +430,7 @@ public class OpenVpnManagementThread implements Runnable {
}
- public static boolean stopOpenVPN() {
+ private static boolean stopOpenVPN() {
boolean sendCMD=false;
for (OpenVpnManagementThread mt: active){
mt.managmentCommand("signal SIGINT\n");
@@ -488,89 +458,24 @@ public class OpenVpnManagementThread implements Runnable {
private void processSignCommand(String b64data) {
- PrivateKey privkey = mProfile.getKeystoreKey();
- Exception err =null;
-
- byte[] data = Base64.decode(b64data, Base64.DEFAULT);
-
- // The Jelly Bean *evil* Hack
- // 4.2 implements the RSA/ECB/PKCS1PADDING in the OpenSSLprovider
- if(Build.VERSION.SDK_INT==16){
- processSignJellyBeans(privkey,data);
- return;
- }
-
-
- try{
-
-
- Cipher rsasinger = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
-
- rsasinger.init(Cipher.ENCRYPT_MODE, privkey);
-
- byte[] signed_bytes = rsasinger.doFinal(data);
- String signed_string = Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
- managmentCommand("rsa-sig\n");
- managmentCommand(signed_string);
- managmentCommand("\nEND\n");
- } catch (NoSuchAlgorithmException e){
- err =e;
- } catch (InvalidKeyException e) {
- err =e;
- } catch (NoSuchPaddingException e) {
- err =e;
- } catch (IllegalBlockSizeException e) {
- err =e;
- } catch (BadPaddingException e) {
- err =e;
- }
- if(err !=null) {
- OpenVPN.logError(R.string.error_rsa_sign,err.getClass().toString(),err.getLocalizedMessage());
- }
-
+ String signed_string = mProfile.getSignedData(b64data);
+ managmentCommand("rsa-sig\n");
+ managmentCommand(signed_string);
+ managmentCommand("\nEND\n");
}
+ @Override
+ public void pause() {
+ signalusr1();
+ }
- private void processSignJellyBeans(PrivateKey privkey, byte[] data) {
- Exception err =null;
- try {
- Method[] allm = privkey.getClass().getSuperclass().getDeclaredMethods();
- System.out.println(allm);
- Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey");
- getKey.setAccessible(true);
-
- // Real object type is OpenSSLKey
- Object opensslkey = getKey.invoke(privkey);
-
- getKey.setAccessible(false);
-
- Method getPkeyContext = opensslkey.getClass().getDeclaredMethod("getPkeyContext");
-
- // integer pointer to EVP_pkey
- getPkeyContext.setAccessible(true);
- int pkey = (Integer) getPkeyContext.invoke(opensslkey);
- getPkeyContext.setAccessible(false);
-
- byte[] signed_bytes = rsasign(data, pkey);
- String signed_string = Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
- managmentCommand("rsa-sig\n");
- managmentCommand(signed_string);
- managmentCommand("\nEND\n");
-
- } catch (NoSuchMethodException e) {
- err=e;
- } catch (IllegalArgumentException e) {
- err=e;
- } catch (IllegalAccessException e) {
- err=e;
- } catch (InvocationTargetException e) {
- err=e;
- } catch (InvalidKeyException e) {
- err=e;
- }
- if(err !=null) {
- OpenVPN.logError(R.string.error_rsa_sign,err.getClass().toString(),err.getLocalizedMessage());
- }
+ @Override
+ public void resume() {
+ releaseHold();
+ }
+ @Override
+ public boolean stopVPN() {
+ return stopOpenVPN();
}
}
diff --git a/src/de/blinkt/openvpn/OpenVpnService.java b/src/de/blinkt/openvpn/OpenVpnService.java
index 4dff943b..ce9f75a1 100644
--- a/src/de/blinkt/openvpn/OpenVpnService.java
+++ b/src/de/blinkt/openvpn/OpenVpnService.java
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package de.blinkt.openvpn;
-
-import java.io.IOException;
+package de.blinkt.openvpn;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Locale;
import java.util.Vector;
import android.annotation.TargetApi;
@@ -28,22 +27,27 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.VpnService;
import android.os.Binder;
-import android.os.Handler;
-import android.os.Handler.Callback;
import android.os.Build;
+import android.os.Handler.Callback;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.preference.PreferenceManager;
+import de.blinkt.openvpn.OpenVPN.ByteCountListener;
import de.blinkt.openvpn.OpenVPN.StateListener;
-public class OpenVpnService extends VpnService implements StateListener, Callback {
+public class OpenVpnService extends VpnService implements StateListener, Callback, ByteCountListener {
public static final String START_SERVICE = "de.blinkt.openvpn.START_SERVICE";
+ public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY";
+ public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE";
+
private Thread mProcessThread=null;
@@ -58,9 +62,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
private CIDRIP mLocalIP=null;
- private OpenVpnManagementThread mSocketManager;
-
- private Thread mSocketManagerThread;
private int mMtu;
private String mLocalIPv6=null;
private NetworkSateReceiver mNetworkStateReceiver;
@@ -76,15 +77,25 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
public static final int PROTECT_FD = 0;
+
+ private static final int LEVEL_OFFLINE = 0;
+ private static final int LEVEL_NOTCONNECTED = 1;
+ private static final int LEVEL_CONNECTED = 2;
+
+ private static boolean mNotificationalwaysVisible=false;
+
private final IBinder mBinder = new LocalBinder();
-
+ private boolean mOvpn3;
+ private Thread mSocketManagerThread;
+ private OpenVPNMangement mManagement;
+
public class LocalBinder extends Binder {
public OpenVpnService getService() {
- // Return this instance of LocalService so clients can call public methods
- return OpenVpnService.this;
- }
- }
-
+ // Return this instance of LocalService so clients can call public methods
+ return OpenVpnService.this;
+ }
+ }
+
@Override
public IBinder onBind(Intent intent) {
String action = intent.getAction();
@@ -93,10 +104,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
else
return super.onBind(intent);
}
-
+
@Override
public void onRevoke() {
- OpenVpnManagementThread.stopOpenVPN();
+ mManagement.stopVPN();
endVpnService();
}
@@ -108,34 +119,43 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
private void endVpnService() {
mProcessThread=null;
OpenVPN.logBuilderConfig(null);
+ OpenVPN.removeStateListener(this);
+ OpenVPN.removeByteCountListener(this);
+ unregisterNetworkStateReceiver();
ProfileManager.setConntectedVpnProfileDisconnected(this);
if(!mStarting) {
- stopSelf();
- stopForeground(true);
+ stopForeground(!mNotificationalwaysVisible);
+
+ if( !mNotificationalwaysVisible)
+ stopSelf();
}
}
- private void showNotification(String msg, String tickerText, boolean lowpriority, long when) {
+ private void showNotification(String msg, String tickerText, boolean lowpriority, long when, int level) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
- int icon = R.drawable.ic_stat_vpn;
+ int icon = R.drawable.notification_icon;
android.app.Notification.Builder nbuilder = new Notification.Builder(this);
- nbuilder.setContentTitle(getString(R.string.notifcation_title,mProfile.mName));
+ if(mProfile!=null)
+ nbuilder.setContentTitle(getString(R.string.notifcation_title,mProfile.mName));
+ else
+ nbuilder.setContentTitle(getString(R.string.notifcation_title_notconnect));
+
nbuilder.setContentText(msg);
nbuilder.setOnlyAlertOnce(true);
nbuilder.setOngoing(true);
nbuilder.setContentIntent(getLogPendingIntent());
- nbuilder.setSmallIcon(icon);
+ nbuilder.setSmallIcon(icon,level);
if(when !=0)
nbuilder.setWhen(when);
// Try to set the priority available since API 16 (Jellybean)
jbNotificationExtras(lowpriority, nbuilder);
- if(tickerText!=null)
+ if(tickerText!=null && !tickerText.equals(""))
nbuilder.setTicker(tickerText);
@SuppressWarnings("deprecation")
@@ -146,6 +166,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
startForeground(OPENVPN_STATUS, notification);
}
+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void jbNotificationExtras(boolean lowpriority,
android.app.Notification.Builder nbuilder) {
@@ -155,7 +176,9 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
// PRIORITY_MIN == -2
setpriority.invoke(nbuilder, -2 );
- nbuilder.setUsesChronometer(true);
+ Method setUsesChronometer = nbuilder.getClass().getMethod("setUsesChronometer", boolean.class);
+ setUsesChronometer.invoke(nbuilder,true);
+
/* PendingIntent cancelconnet=null;
nbuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel,
@@ -185,6 +208,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
private LocalServerSocket openManagmentInterface(int tries) {
// Could take a while to open connection
String socketname = (getCacheDir().getAbsolutePath() + "/" + "mgmtsocket");
+ // The sock is transfered to the LocalServerSocket, ignore warning
+ @SuppressWarnings("resource")
LocalSocket sock = new LocalSocket();
while(tries > 0 && !sock.isConnected()) {
@@ -211,20 +236,35 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
}
- void registerNetworkStateReceiver() {
+ void registerNetworkStateReceiver(OpenVPNMangement magnagement) {
// Registers BroadcastReceiver to track network connection changes.
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- mNetworkStateReceiver = new NetworkSateReceiver(mSocketManager);
+ mNetworkStateReceiver = new NetworkSateReceiver(magnagement);
this.registerReceiver(mNetworkStateReceiver, filter);
}
-
+ void unregisterNetworkStateReceiver() {
+ if(mNetworkStateReceiver!=null)
+ this.unregisterReceiver(mNetworkStateReceiver);
+ mNetworkStateReceiver=null;
+ }
+
+
@Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if(intent != null && intent.getBooleanExtra(ALWAYS_SHOW_NOTIFICATION, false))
+ mNotificationalwaysVisible=true;
+
+ OpenVPN.addStateListener(this);
+ OpenVPN.addByteCountListener(this);
+
if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE))
return START_NOT_STICKY;
-
+ if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE_STICKY)) {
+ return START_REDELIVER_INTENT;
+ }
+
// Extract information from the intent.
String prefix = getPackageName();
@@ -234,21 +274,18 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mProfile = ProfileManager.get(profileUUID);
- showNotification("Starting VPN " + mProfile.mName,"Starting VPN " + mProfile.mName, false,0);
-
-
- OpenVPN.addStateListener(this);
+ showNotification("Starting VPN " + mProfile.mName,"Starting VPN " + mProfile.mName, false,0,LEVEL_NOTCONNECTED);
// Set a flag that we are starting a new VPN
mStarting=true;
// Stop the previous session by interrupting the thread.
- if(OpenVpnManagementThread.stopOpenVPN()){
- // an old was asked to exit, wait 2s
+ if(mManagement!=null && mManagement.stopVPN())
+ // an old was asked to exit, wait 1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
- }
+
if (mProcessThread!=null) {
mProcessThread.interrupt();
@@ -262,33 +299,72 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
// Open the Management Interface
- LocalServerSocket mgmtsocket = openManagmentInterface(8);
-
- if(mgmtsocket!=null) {
- // start a Thread that handles incoming messages of the managment socket
- mSocketManager = new OpenVpnManagementThread(mProfile,mgmtsocket,this);
- mSocketManagerThread = new Thread(mSocketManager,"OpenVPNMgmtThread");
- mSocketManagerThread.start();
- OpenVPN.logInfo("started Socket Thread");
- registerNetworkStateReceiver();
+ if(!mOvpn3) {
+ LocalServerSocket mgmtsocket = openManagmentInterface(8);
+
+ if(mgmtsocket!=null) {
+ // start a Thread that handles incoming messages of the managment socket
+ OpenVpnManagementThread ovpnmgmthread = new OpenVpnManagementThread(mProfile,mgmtsocket,this);
+ mSocketManagerThread = new Thread(ovpnmgmthread,"OpenVPNMgmtThread");
+ mSocketManagerThread.start();
+ mManagement= ovpnmgmthread;
+ OpenVPN.logInfo("started Socket Thread");
+ }
}
-
// Start a new session by creating a new thread.
- OpenVPNThread processThread = new OpenVPNThread(this, argv,nativelibdir);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+ mOvpn3 = prefs.getBoolean("ovpn3", false);
+
+ Runnable processThread;
+ if(mOvpn3) {
+
+ OpenVPNMangement mOpenVPN3 = instantiateOpenVPN3Core();
+ processThread = (Runnable) mOpenVPN3;
+ mManagement = mOpenVPN3;
+
+
+ } else {
+ processThread = new OpenVPNThread(this, argv,nativelibdir);
+ }
mProcessThread = new Thread(processThread, "OpenVPNProcessThread");
mProcessThread.start();
+ registerNetworkStateReceiver(mManagement);
+
+
ProfileManager.setConnectedVpnProfile(this, mProfile);
return START_NOT_STICKY;
}
+ private OpenVPNMangement instantiateOpenVPN3Core() {
+ //new OpenVPNThreadv3(this,mProfile);
+ try {
+ Class cl = Class.forName("Lde/blinkt/openvpn/OpenVPNThreadv3;");
+ return (OpenVPNMangement) cl.getConstructor(OpenVpnService.class,VpnProfile.class).newInstance(this,mProfile);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
@Override
public void onDestroy() {
if (mProcessThread != null) {
- mSocketManager.managmentCommand("signal SIGINT\n");
+ mManagement.stopVPN();
mProcessThread.interrupt();
}
@@ -426,6 +502,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
}
+ public void addRoute(CIDRIP route)
+ {
+ mRoutes.add(route );
+ }
public void addRoute(String dest, String mask) {
CIDRIP route = new CIDRIP(dest, mask);
if(route.len == 32 && !mask.equals("255.255.255.255")) {
@@ -442,6 +522,15 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mRoutesv6.add(extra);
}
+ public void setMtu(int mtu) {
+ mMtu=mtu;
+ }
+
+ public void setLocalIP(CIDRIP cdrip)
+ {
+ mLocalIP=cdrip;
+ }
+
public void setLocalIP(String local, String netmask,int mtu, String mode) {
mLocalIP = new CIDRIP(local, netmask);
@@ -451,7 +540,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
// get the netmask as IP
long netint = CIDRIP.getInt(netmask);
if(Math.abs(netint - mLocalIP.getInt()) ==1) {
- if(mode.equals("net30"))
+ if("net30".equals(mode))
mLocalIP.len=30;
else
mLocalIP.len=31;
@@ -469,20 +558,23 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
public void updateState(String state,String logmessage, int resid) {
// If the process is not running, ignore any state,
// Notification should be invisible in this state
- if(mProcessThread==null)
+ if(mProcessThread==null && !mNotificationalwaysVisible)
return;
// Display byte count only after being connected
- if("BYTECOUNT".equals(state)) {
- if(mDisplayBytecount) {
- showNotification(logmessage,null,true,mConnecttime);
- }
- } else {
+ {
+ int level;
if("CONNECTED".equals(state)) {
mDisplayBytecount = true;
mConnecttime = System.currentTimeMillis();
+ level = LEVEL_CONNECTED;
} else {
+ if ("NONETWORK".equals(state)) {
+ level = LEVEL_OFFLINE;
+ } else {
+ level = LEVEL_NOTCONNECTED;
+ }
mDisplayBytecount = false;
}
@@ -490,12 +582,36 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
// This also mean we are no longer connected, ignore bytecount messages until next
// CONNECTED
String ticker = getString(resid);
- showNotification(getString(resid) +" " + logmessage,ticker,false,0);
+ showNotification(getString(resid) +" " + logmessage,ticker,false,0, level);
}
}
@Override
+ public void updateByteCount(long in, long out, long diffin, long diffout) {
+ if(mDisplayBytecount) {
+ String netstat = String.format(getString(R.string.statusline_bytecount),
+ humanReadableByteCount(in, false),
+ humanReadableByteCount(diffin, false),
+ humanReadableByteCount(out, false),
+ humanReadableByteCount(diffout, false));
+
+ boolean lowpriority = !mNotificationalwaysVisible;
+ showNotification(netstat,null,lowpriority,mConnecttime, LEVEL_CONNECTED);
+ }
+
+ }
+
+ // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
+ public static String humanReadableByteCount(long bytes, boolean si) {
+ int unit = si ? 1000 : 1024;
+ if (bytes < unit) return bytes + " B";
+ int exp = (int) (Math.log(bytes) / Math.log(unit));
+ String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
+ return String.format(Locale.getDefault(),"%.1f %sB", bytes / Math.pow(unit, exp), pre);
+ }
+
+ @Override
public boolean handleMessage(Message msg) {
Runnable r = msg.getCallback();
if(r!=null){
@@ -505,4 +621,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
return false;
}
}
+
+ public OpenVPNMangement getManagement() {
+ return mManagement;
+ }
}
diff --git a/src/de/blinkt/openvpn/Settings_Authentication.java b/src/de/blinkt/openvpn/Settings_Authentication.java
index 6733ed0c..4e3f1e6f 100644
--- a/src/de/blinkt/openvpn/Settings_Authentication.java
+++ b/src/de/blinkt/openvpn/Settings_Authentication.java
@@ -23,6 +23,7 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen
private SwitchPreference mUseTLSAuth;
private EditTextPreference mCipher;
private String mTlsAuthFileData;
+ private EditTextPreference mAuth;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,6 +47,9 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen
mCipher =(EditTextPreference) findPreference("cipher");
mCipher.setOnPreferenceChangeListener(this);
+ mAuth =(EditTextPreference) findPreference("auth");
+ mAuth.setOnPreferenceChangeListener(this);
+
loadSettings();
}
@@ -64,6 +68,8 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen
mTLSAuthDirection.setValue(mProfile.mTLSAuthDirection);
mCipher.setText(mProfile.mCipher);
onPreferenceChange(mCipher, mProfile.mCipher);
+ mAuth.setText(mProfile.mAuth);
+ onPreferenceChange(mAuth, mProfile.mAuth);
}
@Override
@@ -85,6 +91,11 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen
else
mProfile.mCipher = mCipher.getText();
+ if(mAuth.getText()==null)
+ mProfile.mAuth = null;
+ else
+ mProfile.mAuth = mAuth.getText();
+
}
@@ -96,7 +107,7 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen
preference.setSummary(mProfile.mServerName);
else
preference.setSummary((String)newValue);
- } else if (preference == mCipher) {
+ } else if (preference == mCipher || preference == mAuth) {
preference.setSummary((CharSequence) newValue);
}
return true;
diff --git a/src/de/blinkt/openvpn/Settings_Basic.java b/src/de/blinkt/openvpn/Settings_Basic.java
index 9c85094a..137e75c5 100644
--- a/src/de/blinkt/openvpn/Settings_Basic.java
+++ b/src/de/blinkt/openvpn/Settings_Basic.java
@@ -58,29 +58,15 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On
private Spinner mType;
private FileSelectLayout mpkcs12;
private TextView mPKCS12Password;
-
private Handler mHandler;
-
-
-
-
-
- private SparseArray<FileSelectLayout> fileselects = new SparseArray<FileSelectLayout>();
-
-
private EditText mUserName;
-
-
private EditText mPassword;
-
-
private View mView;
-
-
private VpnProfile mProfile;
private EditText mProfileName;
private EditText mKeyPassword;
+ private SparseArray<FileSelectLayout> fileselects = new SparseArray<FileSelectLayout>();
private void addFileSelectLayout (FileSelectLayout fsl) {
@@ -129,7 +115,6 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On
mpkcs12.setBase64Encode();
mCaCert.setShowClear();
-
mType.setOnItemSelectedListener(this);
mView.findViewById(R.id.select_keystore_button).setOnClickListener(this);
diff --git a/src/de/blinkt/openvpn/ShowConfigFragment.java b/src/de/blinkt/openvpn/ShowConfigFragment.java
index dae83438..c9c778df 100644
--- a/src/de/blinkt/openvpn/ShowConfigFragment.java
+++ b/src/de/blinkt/openvpn/ShowConfigFragment.java
@@ -17,21 +17,41 @@ public class ShowConfigFragment extends Fragment {
public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
String profileUUID = getArguments().getString(getActivity().getPackageName() + ".profileUUID");
- VpnProfile vp = ProfileManager.get(profileUUID);
+ final VpnProfile vp = ProfileManager.get(profileUUID);
View v=inflater.inflate(R.layout.viewconfig, container,false);
- TextView cv = (TextView) v.findViewById(R.id.configview);
+ final TextView cv = (TextView) v.findViewById(R.id.configview);
int check=vp.checkProfile(getActivity());
if(check!=R.string.no_error_found) {
cv.setText(check);
configtext = getString(check);
}
- else {
- String cfg=vp.getConfigFile(getActivity());
- configtext= cfg;
- cv.setText(cfg);
+ else {
+ // Run in own Thread since Keystore does not like to be queried from the main thread
+
+ cv.setText("Generating config...");
+ startGenConfig(vp, cv);
}
return v;
+ }
+
+ private void startGenConfig(final VpnProfile vp, final TextView cv) {
+
+ new Thread() {
+ public void run() {
+ final String cfg=vp.getConfigFile(getActivity(),false);
+ configtext= cfg;
+ getActivity().runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ cv.setText(cfg);
+ }
+ });
+
+
+ };
+ }.start();
};
@Override
diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java
index d8b22ae6..ea034b55 100644
--- a/src/de/blinkt/openvpn/VpnProfile.java
+++ b/src/de/blinkt/openvpn/VpnProfile.java
@@ -9,15 +9,26 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
+import java.util.Locale;
import java.util.UUID;
import java.util.Vector;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemWriter;
@@ -25,9 +36,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.os.Build;
import android.preference.PreferenceManager;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.util.Base64;
public class VpnProfile implements Serializable{
// Parcable
@@ -105,13 +118,15 @@ public class VpnProfile implements Serializable{
public String mConnectRetryMax="5";
public String mConnectRetry="5";
public boolean mUserEditable=true;
+ public String mAuth="";
static final String MINIVPN = "miniopenvpn";
-
-
-
+ static private native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
+ static {
+ System.loadLibrary("opvpnutil");
+ }
public void clearDefaults() {
mServerName="unkown";
@@ -137,11 +152,6 @@ public class VpnProfile implements Serializable{
return '"' + escapedString + '"';
}
-
- static final String OVPNCONFIGCA = "android-ca.pem";
- static final String OVPNCONFIGUSERCERT = "android-user.pem";
-
-
public VpnProfile(String name) {
mUuid = UUID.randomUUID();
mName = name;
@@ -157,7 +167,7 @@ public class VpnProfile implements Serializable{
}
- public String getConfigFile(Context context)
+ public String getConfigFile(Context context, boolean configForOvpn3)
{
File cacheDir= context.getCacheDir();
@@ -252,10 +262,13 @@ public class VpnProfile implements Serializable{
case VpnProfile.TYPE_USERPASS_KEYSTORE:
cfg+="auth-user-pass\n";
case VpnProfile.TYPE_KEYSTORE:
- cfg+="ca " + cacheDir.getAbsolutePath() + "/" + OVPNCONFIGCA + "\n";
- cfg+="cert " + cacheDir.getAbsolutePath() + "/" + OVPNCONFIGUSERCERT + "\n";
- cfg+="management-external-key\n";
-
+ if(!configForOvpn3) {
+ String[] ks =getKeyStoreCertificates(context);
+ cfg+="### From Keystore ####\n";
+ cfg+="<ca>\n" + ks[0] + "</ca>\n";
+ cfg+="<cert>\n" + ks[0] + "</cert>\n";
+ cfg+="management-external-key\n";
+ }
break;
case VpnProfile.TYPE_USERPASS:
cfg+="auth-user-pass\n";
@@ -348,6 +361,9 @@ public class VpnProfile implements Serializable{
cfg += "cipher " + mCipher + "\n";
}
+ if(nonNull(mAuth)) {
+ cfg += "auth " + mAuth + "\n";
+ }
// Obscure Settings dialog
if(mUseRandomHostname)
@@ -386,12 +402,13 @@ public class VpnProfile implements Serializable{
//! Put inline data inline and other data as normal escaped filename
private String insertFileData(String cfgentry, String filedata) {
if(filedata==null) {
+ // TODO: generate good error
return String.format("%s %s\n",cfgentry,"missing");
}else if(filedata.startsWith(VpnProfile.INLINE_TAG)){
String datawoheader = filedata.substring(VpnProfile.INLINE_TAG.length());
- return String.format("<%s>\n%s\n</%s>\n",cfgentry,datawoheader,cfgentry);
+ return String.format(Locale.ENGLISH,"<%s>\n%s\n</%s>\n",cfgentry,datawoheader,cfgentry);
} else {
- return String.format("%s %s\n",cfgentry,openVpnEscape(filedata));
+ return String.format(Locale.ENGLISH,"%s %s\n",cfgentry,openVpnEscape(filedata));
}
}
@@ -460,7 +477,7 @@ public class VpnProfile implements Serializable{
long nm = 0xffffffffl;
nm = (nm << (32-len)) & 0xffffffffl;
- String netmask =String.format("%d.%d.%d.%d", (nm & 0xff000000) >> 24,(nm & 0xff0000) >> 16, (nm & 0xff00) >> 8 ,nm & 0xff );
+ String netmask =String.format(Locale.ENGLISH,"%d.%d.%d.%d", (nm & 0xff000000) >> 24,(nm & 0xff0000) >> 16, (nm & 0xff00) >> 8 ,nm & 0xff );
return parts[0] + " " + netmask;
}
@@ -491,7 +508,7 @@ public class VpnProfile implements Serializable{
Intent intent = new Intent(context,OpenVpnService.class);
if(mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
- if(!saveCertificates(context))
+ if(getKeyStoreCertificates(context)==null)
return null;
}
@@ -503,7 +520,7 @@ public class VpnProfile implements Serializable{
try {
FileWriter cfg = new FileWriter(context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
- cfg.write(getConfigFile(context));
+ cfg.write(getConfigFile(context,false));
cfg.flush();
cfg.close();
} catch (IOException e) {
@@ -513,7 +530,7 @@ public class VpnProfile implements Serializable{
return intent;
}
- private boolean saveCertificates(Context context) {
+ String[] getKeyStoreCertificates(Context context) {
PrivateKey privateKey = null;
X509Certificate[] cachain=null;
try {
@@ -546,27 +563,30 @@ public class VpnProfile implements Serializable{
}
- FileWriter fout = new FileWriter(context.getCacheDir().getAbsolutePath() + "/" + VpnProfile.OVPNCONFIGCA);
- PemWriter pw = new PemWriter(fout);
+
+ StringWriter caout = new StringWriter();
+
+ PemWriter pw = new PemWriter(caout);
for(X509Certificate cert:cachain) {
pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
}
-
pw.close();
+
+ StringWriter certout = new StringWriter();
+
+
if(cachain.length>= 1){
X509Certificate usercert = cachain[0];
- FileWriter userout = new FileWriter(context.getCacheDir().getAbsolutePath() + "/" + VpnProfile.OVPNCONFIGUSERCERT);
-
- PemWriter upw = new PemWriter(userout);
+ PemWriter upw = new PemWriter(certout);
upw.writeObject(new PemObject("CERTIFICATE", usercert.getEncoded()));
upw.close();
}
-
- return true;
+
+ return new String[] {caout.toString(),certout.toString()};
} catch (InterruptedException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
@@ -577,8 +597,13 @@ public class VpnProfile implements Serializable{
e.printStackTrace();
} catch (KeyChainException e) {
OpenVPN.logMessage(0,"",context.getString(R.string.keychain_access));
+ if(Build.VERSION.SDK_INT==Build.VERSION_CODES.JELLY_BEAN){
+ if(!mAlias.matches("^[a-zA-Z0-9]$")) {
+ OpenVPN.logError(R.string.jelly_keystore_alphanumeric_bug);
+ }
+ }
}
- return false;
+ return null;
}
private Certificate getCacertFromFile() throws FileNotFoundException, CertificateException {
CertificateFactory certFact = CertificateFactory.getInstance("X.509");
@@ -639,7 +664,7 @@ public class VpnProfile implements Serializable{
return null;
}
}
- private boolean isUserPWAuth() {
+ boolean isUserPWAuth() {
switch(mAuthenticationType) {
case TYPE_USERPASS:
case TYPE_USERPASS_CERTIFICATES:
@@ -736,6 +761,89 @@ public class VpnProfile implements Serializable{
return mPrivateKey;
}
+ public String getSignedData(String b64data) {
+ PrivateKey privkey = getKeystoreKey();
+ Exception err =null;
+
+ byte[] data = Base64.decode(b64data, Base64.DEFAULT);
+
+ // The Jelly Bean *evil* Hack
+ // 4.2 implements the RSA/ECB/PKCS1PADDING in the OpenSSLprovider
+ if(Build.VERSION.SDK_INT==Build.VERSION_CODES.JELLY_BEAN){
+ return processSignJellyBeans(privkey,data);
+ }
+
+
+ try{
+
+
+ Cipher rsasinger = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
+
+ rsasinger.init(Cipher.ENCRYPT_MODE, privkey);
+
+ byte[] signed_bytes = rsasinger.doFinal(data);
+ return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
+
+ } catch (NoSuchAlgorithmException e){
+ err =e;
+ } catch (InvalidKeyException e) {
+ err =e;
+ } catch (NoSuchPaddingException e) {
+ err =e;
+ } catch (IllegalBlockSizeException e) {
+ err =e;
+ } catch (BadPaddingException e) {
+ err =e;
+ }
+ if(err !=null) {
+ OpenVPN.logError(R.string.error_rsa_sign,err.getClass().toString(),err.getLocalizedMessage());
+ }
+ return null;
+
+ }
+
+
+ private String processSignJellyBeans(PrivateKey privkey, byte[] data) {
+ Exception err =null;
+ try {
+ Method[] allm = privkey.getClass().getSuperclass().getDeclaredMethods();
+ System.out.println(allm);
+ Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey");
+ getKey.setAccessible(true);
+
+ // Real object type is OpenSSLKey
+ Object opensslkey = getKey.invoke(privkey);
+
+ getKey.setAccessible(false);
+
+ Method getPkeyContext = opensslkey.getClass().getDeclaredMethod("getPkeyContext");
+
+ // integer pointer to EVP_pkey
+ getPkeyContext.setAccessible(true);
+ int pkey = (Integer) getPkeyContext.invoke(opensslkey);
+ getPkeyContext.setAccessible(false);
+
+ byte[] signed_bytes = rsasign(data, pkey);
+ return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
+
+ } catch (NoSuchMethodException e) {
+ err=e;
+ } catch (IllegalArgumentException e) {
+ err=e;
+ } catch (IllegalAccessException e) {
+ err=e;
+ } catch (InvocationTargetException e) {
+ err=e;
+ } catch (InvalidKeyException e) {
+ err=e;
+ }
+ if(err !=null) {
+ OpenVPN.logError(R.string.error_rsa_sign,err.getClass().toString(),err.getLocalizedMessage());
+ }
+ return null;
+
+ }
+
}