From b47948804f846d68c1dbc39ad4691bf0941cb825 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 30 May 2018 11:40:37 +0200 Subject: Implement showing an out of band authentication via URL to the user --- .../main/java/de/blinkt/openvpn/VpnProfile.java | 13 +-- .../blinkt/openvpn/core/ICSOpenVPNApplication.java | 10 ++ .../de/blinkt/openvpn/core/OpenVPNService.java | 111 +++++++++++++++------ .../openvpn/core/OpenVpnManagementThread.java | 11 ++ .../java/de/blinkt/openvpn/core/VpnStatus.java | 6 +- main/src/main/res/values/strings.xml | 7 ++ .../de/blinkt/openvpn/core/OpenVPNThreadv3.java | 16 ++- 7 files changed, 132 insertions(+), 42 deletions(-) (limited to 'main/src') diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index 4e391e2e..74c0b595 100644 --- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -311,13 +311,14 @@ public class VpnProfile implements Serializable, Cloneable { } - cfg.append("machine-readable-output\n"); - if (!mIsOpenVPN22) - cfg.append("allow-recursive-routing\n"); - - // Users are confused by warnings that are misleading... - cfg.append("ifconfig-nowarn\n"); + if (!configForOvpn3) { + cfg.append("machine-readable-output\n"); + if (!mIsOpenVPN22) + cfg.append("allow-recursive-routing\n"); + // Users are confused by warnings that are misleading... + cfg.append("ifconfig-nowarn\n"); + } boolean useTLSClient = (mAuthenticationType != TYPE_STATICKEYS); diff --git a/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java index 53847cbf..1cbd99ef 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java +++ b/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java @@ -66,5 +66,15 @@ public class ICSOpenVPNApplication extends Application { mChannel.setLightColor(Color.BLUE); mNotificationManager.createNotificationChannel(mChannel); + + + // Urgent requests, e.g. two factor auth + name = getString(R.string.channel_name_userreq); + mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_USERREQ_ID, + name, NotificationManager.IMPORTANCE_HIGH); + mChannel.setDescription(getString(R.string.channel_description_userreq)); + mChannel.enableVibration(true); + mChannel.setLightColor(Color.CYAN); + mNotificationManager.createNotificationChannel(mChannel); } } diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 5e0016f4..b5afbb0e 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -20,6 +20,7 @@ import android.content.pm.ShortcutManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.Uri; import android.net.VpnService; import android.os.Build; import android.os.Bundle; @@ -66,6 +67,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN"; public static final String NOTIFICATION_CHANNEL_BG_ID = "openvpn_bg"; public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "openvpn_newstat"; + public static final String NOTIFICATION_CHANNEL_USERREQ_ID = "openvpn_userreq"; + public static final String VPNSERVICE_TUN = "vpnservice-tun"; public final static String ORBOT_PACKAGE_NAME = "org.torproject.android"; private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; @@ -235,6 +238,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac int priority; if (channel.equals(NOTIFICATION_CHANNEL_BG_ID)) priority = PRIORITY_MIN; + else if (channel.equals(NOTIFICATION_CHANNEL_USERREQ_ID)) + priority = PRIORITY_MAX; else priority = PRIORITY_DEFAULT; @@ -258,12 +263,13 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // Try to set the priority available since API 16 (Jellybean) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { jbNotificationExtras(priority, nbuilder); + addVpnActionsToNotification(nbuilder); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - lpNotificationExtras(nbuilder); + lpNotificationExtras(nbuilder, Notification.CATEGORY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //noinspection NewApi @@ -308,8 +314,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void lpNotificationExtras(Notification.Builder nbuilder) { - nbuilder.setCategory(Notification.CATEGORY_SERVICE); + private void lpNotificationExtras(Notification.Builder nbuilder, String category) { + nbuilder.setCategory(category); nbuilder.setLocalOnly(true); } @@ -354,28 +360,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } - Intent disconnectVPN = new Intent(this, DisconnectVPN.class); - disconnectVPN.setAction(DISCONNECT_VPN); - PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); - - nbuilder.addAction(R.drawable.ic_menu_close_clear_cancel, - getString(R.string.cancel_connection), disconnectPendingIntent); - - Intent pauseVPN = new Intent(this, OpenVPNService.class); - if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) { - pauseVPN.setAction(PAUSE_VPN); - PendingIntent pauseVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); - nbuilder.addAction(R.drawable.ic_menu_pause, - getString(R.string.pauseVPN), pauseVPNPending); - - } else { - pauseVPN.setAction(RESUME_VPN); - PendingIntent resumeVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); - nbuilder.addAction(R.drawable.ic_menu_play, - getString(R.string.resumevpn), resumeVPNPending); - } - - //ignore exception } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { @@ -384,6 +368,30 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + private void addVpnActionsToNotification(Notification.Builder nbuilder) { + Intent disconnectVPN = new Intent(this, DisconnectVPN.class); + disconnectVPN.setAction(DISCONNECT_VPN); + PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); + + nbuilder.addAction(R.drawable.ic_menu_close_clear_cancel, + getString(R.string.cancel_connection), disconnectPendingIntent); + + Intent pauseVPN = new Intent(this, OpenVPNService.class); + if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) { + pauseVPN.setAction(PAUSE_VPN); + PendingIntent pauseVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); + nbuilder.addAction(R.drawable.ic_menu_pause, + getString(R.string.pauseVPN), pauseVPNPending); + + } else { + pauseVPN.setAction(RESUME_VPN); + PendingIntent resumeVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); + nbuilder.addAction(R.drawable.ic_menu_play, + getString(R.string.resumevpn), resumeVPNPending); + } + } + PendingIntent getUserInputIntent(String needed) { Intent intent = new Intent(getApplicationContext(), LaunchVPN.class); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); @@ -1106,11 +1114,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // Display byte count only after being connected { - if (level == LEVEL_WAITING_FOR_USER_INPUT) { - // The user is presented a dialog of some kind, no need to inform the user - // with a notifcation - return; - } else if (level == LEVEL_CONNECTED) { + if (level == LEVEL_CONNECTED) { mDisplayBytecount = true; mConnecttime = System.currentTimeMillis(); if (!runningOnAndroidTV()) @@ -1190,4 +1194,47 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.updateStateString("NEED", "need " + needed, resid, LEVEL_WAITING_FOR_USER_INPUT); showNotification(getString(resid), getString(resid), NOTIFICATION_CHANNEL_NEWSTATUS_ID, 0, LEVEL_WAITING_FOR_USER_INPUT); } + + public void trigger_url_open(String info) { + String channel = NOTIFICATION_CHANNEL_USERREQ_ID; + String url = info.split(":",2)[1]; + + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + Notification.Builder nbuilder = new Notification.Builder(this); + nbuilder.setContentTitle(getString(R.string.openurl_requested)); + + nbuilder.setContentText(url); + nbuilder.setAutoCancel(true); + + int icon = android.R.drawable.ic_dialog_info; + + nbuilder.setSmallIcon(icon); + + Intent openUrlIntent = new Intent(Intent.ACTION_VIEW); + openUrlIntent.setData(Uri.parse(url)); + openUrlIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + nbuilder.setContentIntent(PendingIntent.getActivity(this,0, openUrlIntent, 0)); + + + // Try to set the priority available since API 16 (Jellybean) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + jbNotificationExtras(PRIORITY_MAX, nbuilder); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + lpNotificationExtras(nbuilder, Notification.CATEGORY_STATUS); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + //noinspection NewApi + nbuilder.setChannelId(channel); + } + + @SuppressWarnings("deprecation") + Notification notification = nbuilder.getNotification(); + + int notificationId = channel.hashCode(); + + mNotificationManager.notify(notificationId, notification); + } } diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index e711d8ff..74a2ad05 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -312,6 +312,9 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { case "PK_SIGN": processSignCommand(argument); break; + case "INFOMSG": + processInfoMessage(argument); + break; default: VpnStatus.logWarning("MGMT: Got unrecognized command" + command); Log.i(TAG, "Got unrecognized command" + command); @@ -330,6 +333,14 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } } + private void processInfoMessage(String info) + { + if (info.startsWith("OPEN_URL:")) + { + mOpenVPNService.trigger_url_open(info); + } + } + private void processLogMessage(String argument) { String[] args = argument.split(",", 4); // 0 unix time stamp diff --git a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 3adf7c88..b87c8864 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -300,6 +300,8 @@ public class VpnStatus { return R.string.state_resolve; case "TCP_CONNECT": return R.string.state_tcp_connect; + case "AUTH_PENDING": + return R.string.state_auth_pending; default: return R.string.unknown_state; } @@ -323,7 +325,7 @@ public class VpnStatus { private static ConnectionStatus getLevel(String state) { String[] noreplyet = {"CONNECTING", "WAIT", "RECONNECTING", "RESOLVE", "TCP_CONNECT"}; - String[] reply = {"AUTH", "GET_CONFIG", "ASSIGN_IP", "ADD_ROUTES"}; + String[] reply = {"AUTH", "GET_CONFIG", "ASSIGN_IP", "ADD_ROUTES", "AUTH_PENDING"}; String[] connected = {"CONNECTED"}; String[] notconnected = {"DISCONNECTED", "EXITING"}; @@ -385,7 +387,7 @@ public class VpnStatus { for (StateListener sl : stateListener) { sl.updateState(state, msg, resid, level); } - newLogItem(new LogItem((LogLevel.DEBUG), String.format("New OpenVPN Status (%s->%s): %s",state,level.toString(),msg))); + //newLogItem(new LogItem((LogLevel.DEBUG), String.format("New OpenVPN Status (%s->%s): %s",state,level.toString(),msg))); } public static void logInfo(String message) { diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml index a7cd6a61..5590acf3 100755 --- a/main/src/main/res/values/strings.xml +++ b/main/src/main/res/values/strings.xml @@ -475,4 +475,11 @@ OpenVPN for Android supports two remote APIs, a sophisticated API using AIDL (remoteEXample in the git repository) and a simple one using Intents. <p>Examples using adb shell and the intents. Replace profilname with your profile name<p><p> adb shell am start-activity -a android.intent.action.MAIN de.blinkt.openvpn/.api.DisconnectVPN<p> adb shell am start-activity -a android.intent.action.MAIN -e de.blinkt.openvpn.api.profileName Blinkt de.blinkt.openvpn/.api.ConnectVPN Enable Proxy Authentication Cannot use extra http-proxy-option statement and Orbot integration at the same timeO + Info from server: \'%s\' + User interaction required + OpenVPN connection requires a user input, e.g. two factor + authentification + + Open URL to continue VPN authentication + Authentication pending diff --git a/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java b/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java index b2e8714f..08c84558 100644 --- a/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java +++ b/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java @@ -1,5 +1,6 @@ package de.blinkt.openvpn.core; +import de.blinkt.openvpn.R; import net.openvpn.ovpn3.ClientAPI_Config; import net.openvpn.ovpn3.ClientAPI_EvalConfig; import net.openvpn.ovpn3.ClientAPI_Event; @@ -189,6 +190,7 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable //config.setPlatformVersion(mVp.getPlatformVersionEnvString()); config.setExternalPkiAlias("extpki"); config.setCompressionMode("yes"); + config.setInfo(true); ClientAPI_EvalConfig ec = eval_config(config); if(ec.getExternalPki()) { @@ -278,9 +280,19 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable @Override public void event(ClientAPI_Event event) { - VpnStatus.updateStateString(event.getName(), event.getInfo()); + String name = event.getName(); + String info = event.getInfo(); + if (name.equals("INFO")) { + VpnStatus.logInfo(R.string.info_from_server, info); + if (info.startsWith("OPEN_URL:")) + { + mService.trigger_url_open(info); + } + } else{ + VpnStatus.updateStateString(name, info); + } if(event.getError()) - VpnStatus.logError(String.format("EVENT(Error): %s: %s",event.getName(),event.getInfo())); + VpnStatus.logError(String.format("EVENT(Error): %s: %s", name, info)); } -- cgit v1.2.3