diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/de/blinkt/openvpn/CIDRIP.java | 10 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/ConfigParser.java | 6 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/FileSelect.java | 3 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/FileSelectionFragment.java | 5 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/InlineFileTab.java | 1 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/LogWindow.java | 51 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/NetworkSateReceiver.java | 16 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVPN.java | 62 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVPNMangement.java | 13 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVpnManagementThread.java | 137 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVpnService.java | 236 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/Settings_Authentication.java | 13 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/Settings_Basic.java | 17 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/ShowConfigFragment.java | 32 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/VpnProfile.java | 166 |
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; + + } + } |