From 9ca2ff6f0688ae6c987f0fa6fe50f1b2fc2425d7 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 8 Oct 2021 02:22:07 +0200 Subject: Add red warnings if a profile uses deprecated/insecure options --- .../java/de/blinkt/openvpn/core/TestUiUtils.kt | 13 +++++ .../ui/java/de/blinkt/openvpn/fragments/Utils.kt | 56 ++++++++++++++++++++++ .../blinkt/openvpn/fragments/VPNProfileList.java | 36 +++++++++----- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/main/src/testui/java/de/blinkt/openvpn/core/TestUiUtils.kt b/main/src/testui/java/de/blinkt/openvpn/core/TestUiUtils.kt index 8bc2e4fa..040606cf 100644 --- a/main/src/testui/java/de/blinkt/openvpn/core/TestUiUtils.kt +++ b/main/src/testui/java/de/blinkt/openvpn/core/TestUiUtils.kt @@ -1,3 +1,4 @@ +import de.blinkt.openvpn.VpnProfile import de.blinkt.openvpn.fragments.Utils import org.junit.Assert import org.junit.Test @@ -9,4 +10,16 @@ class TestUiUtils { Assert.assertEquals(0, Utils.mapCompatVer(20707)) Assert.assertEquals(3, Utils.mapCompatVer(11723)) } + + @Test + fun testWarnings() { + val vp = VpnProfile("unittest") + vp.mUseCustomConfig = true; + vp.mCustomConfigOptions = "\ntls-cipher DEFAULT:@SECLEVEL=0\n" + + val warnings = mutableListOf() + Utils.addSoftWarnings(warnings, vp) + Assert.assertTrue(warnings.size >= 1) + + } } \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt index 64a8d3ba..f4cc446f 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt @@ -8,10 +8,16 @@ import android.annotation.TargetApi import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.graphics.Color import android.os.Build import android.provider.OpenableColumns +import android.text.SpannableString +import android.text.SpannableStringBuilder +import android.text.TextUtils +import android.text.style.ForegroundColorSpan import android.util.Base64 import android.webkit.MimeTypeMap +import de.blinkt.openvpn.R import kotlin.Throws import de.blinkt.openvpn.VpnProfile import de.blinkt.openvpn.core.Preferences @@ -258,4 +264,54 @@ object Utils { else -> return 0 } } + + val seclevleregex = Regex("tls-cipher.*@SECLEVEL=0") + + @JvmStatic + fun getWarningText(c:Context, vp:VpnProfile): SpannableStringBuilder { + val warnings = mutableListOf() + + val errorId = vp.checkProfile(c) + if (errorId != R.string.no_error_found) + { + warnings.add(c.resources.getString(errorId)) + } + + addSoftWarnings(warnings, vp) + val builder = SpannableStringBuilder() + if (warnings.size > 0) { + val warnSpan = SpannableString( warnings.joinToString(separator = ", ")) + warnSpan.setSpan(ForegroundColorSpan(Color.RED), 0, warnSpan.length, 0) + builder.append(warnSpan) + } + + return builder + } + + val weakCiphers = listOf("BF-CBC", "DES-CBC", "NONE") + + @JvmStatic + fun addSoftWarnings(warnings:MutableList, vp:VpnProfile) { + if (vp.mUseLegacyProvider) + warnings.add("legacy Provider enabled") + if (vp.mAuthenticationType == VpnProfile.TYPE_STATICKEYS) + warnings.add("deprecated static key (--secret) mode") + if (vp.mUseCustomConfig && vp.mCustomConfigOptions.contains(seclevleregex)) + warnings.add("low security (@SECLEVEL=0)") + if (vp.mCompatMode > 0 ) + warnings.add("compat mode enabled") + + var cipher= vp.mCipher.toUpperCase(Locale.ROOT) + if (TextUtils.isEmpty(cipher)) + cipher = "BF-CBC"; + + for (weakCipher in weakCiphers) { + if ((vp.mDataCiphers != null && vp.mDataCiphers.toUpperCase(Locale.ROOT) + .contains(weakCipher)) + || (vp.mCompatMode in 1..20399 && (cipher == weakCipher)) + ) + warnings.add("weak cipher (${weakCipher})") + } + } + } \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java index ce6fa7f1..cf48cc48 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -20,11 +20,14 @@ import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.fragment.app.ListFragment; import android.text.Html; import android.text.Html.ImageGetter; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -81,6 +84,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn private String mLastStatusMessage; private ArrayAdapter mArrayadapter; private Intent mLastIntent; + private VpnProfile defaultVPN; @Override public void updateState(String state, String logmessage, final int localizedResId, ConnectionStatus level, Intent intent) { @@ -246,6 +250,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn updateDynamicShortcuts(); } VpnStatus.addStateListener(this); + defaultVPN = ProfileManager.getAlwaysOnVPN(requireContext()); } @Override @@ -286,7 +291,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn } private void populateVpnList() { - boolean sortByLRU = Preferences.getDefaultSharedPreferences(getActivity()).getBoolean(PREF_SORT_BY_LRU, false); + boolean sortByLRU = Preferences.getDefaultSharedPreferences(requireActivity()).getBoolean(PREF_SORT_BY_LRU, false); Collection allvpn = getPM().getProfiles(); TreeSet sortedset; if (sortByLRU) @@ -303,7 +308,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(Menu menu, @NonNull MenuInflater inflater) { menu.add(0, MENU_ADD_PROFILE, 0, R.string.menu_add_profile) .setIcon(R.drawable.ic_menu_add) .setAlphabeticShortcut('a') @@ -589,30 +594,37 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn super(context, resource, textViewResourceId); } + @NonNull @Override - public View getView(final int position, View convertView, ViewGroup parent) { + public View getView(final int position, View convertView, @NonNull ViewGroup parent) { View v = super.getView(position, convertView, parent); final VpnProfile profile = (VpnProfile) getListAdapter().getItem(position); View titleview = v.findViewById(R.id.vpn_list_item_left); - titleview.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startOrStopVPN(profile); - } - }); + titleview.setOnClickListener(v1 -> startOrStopVPN(profile)); View settingsview = v.findViewById(R.id.quickedit_settings); settingsview.setOnClickListener(view -> editVPN(profile)); - TextView subtitle = (TextView) v.findViewById(R.id.vpn_item_subtitle); + TextView subtitle = v.findViewById(R.id.vpn_item_subtitle); + SpannableStringBuilder warningText = Utils.getWarningText(requireContext(), profile); + + if (profile == defaultVPN) { + if (warningText.length() > 0) + warningText.append(" "); + warningText.append(new SpannableString("Default VPN")); + } + if (profile.getUUIDString().equals(VpnStatus.getLastConnectedVPNProfile())) { subtitle.setText(mLastStatusMessage); subtitle.setVisibility(View.VISIBLE); } else { - subtitle.setText(""); - subtitle.setVisibility(View.GONE); + subtitle.setText(warningText); + if (warningText.length() > 0) + subtitle.setVisibility(View.VISIBLE); + else + subtitle.setVisibility(View.GONE); } -- cgit v1.2.3