diff options
Diffstat (limited to 'app/src/main/java/se/leap')
6 files changed, 111 insertions, 35 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java index be2fe4f4..f4531ff8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java @@ -1,12 +1,35 @@ package se.leap.bitmaskclient.base.fragments; +import static android.content.Context.MODE_PRIVATE; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static se.leap.bitmaskclient.R.string.advanced_settings; +import static se.leap.bitmaskclient.base.models.Constants.GATEWAY_PINNING; +import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; +import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; +import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake; +import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarTitle; + +import android.app.AlertDialog; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.Toast; import androidx.annotation.NonNull; @@ -18,6 +41,7 @@ import androidx.fragment.app.FragmentTransaction; import java.util.Set; import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.FragmentManagerEnhanced; import se.leap.bitmaskclient.base.MainActivity; @@ -28,24 +52,6 @@ import se.leap.bitmaskclient.base.views.IconTextEntry; import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.firewall.FirewallManager; -import static android.content.Context.MODE_PRIVATE; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; -import static se.leap.bitmaskclient.R.string.advanced_settings; -import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; -import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; -import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; -import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake; -import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarTitle; - public class SettingsFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener { private FirewallManager firewallManager; @@ -74,6 +80,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh initUseSnowflakeEntry(view); initFirewallEntry(view); initTetheringEntry(view); + initGatewayPinningEntry(view); setActionBarTitle(this, advanced_settings); return view; } @@ -207,6 +214,41 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh }); } + private void initGatewayPinningEntry(View rootView) { + if (!BuildConfig.BUILD_TYPE.equals("debug")) { + return; + } + Context context = this.getContext(); + if (context == null) { + return; + } + IconTextEntry gatewayPinning = rootView.findViewById(R.id.gateway_pinning); + String pinnedGateway = PreferenceHelper.getPinnedGateway(rootView.getContext()); + gatewayPinning.setSubtitle(pinnedGateway != null ? pinnedGateway : "Connect to a specific Gateway for debugging purposes"); + + gatewayPinning.setOnClickListener(v -> { + EditText gatewayPinningEditText = new EditText(rootView.getContext()); + gatewayPinningEditText.setText(pinnedGateway); + new AlertDialog.Builder(context) + .setTitle("Gateway Pinning") + .setMessage("Enter the domain name of the gateway") + .setView(gatewayPinningEditText) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + if (gatewayPinningEditText.getText() != null) { + String editTextInput = gatewayPinningEditText.getText().toString(); + if (!TextUtils.isEmpty(editTextInput)) { + PreferenceHelper.setPreferredCity(context, null); + PreferenceHelper.pinGateway(context, editTextInput); + } else { + PreferenceHelper.pinGateway(context, null); + } + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create().show(); + }); + } + public void showTetheringAlert() { try { @@ -245,6 +287,8 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh initPreferUDPEntry(rootView); } else if (key.equals(USE_IPv6_FIREWALL)) { initFirewallEntry(getView()); + } if (key.equals(GATEWAY_PINNING)) { + initGatewayPinningEntry(rootView); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java index 86b438f8..bde909ba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java @@ -43,6 +43,7 @@ public interface Constants { String PREFERRED_CITY = "preferred_city"; String USE_SNOWFLAKE = "use_snowflake"; String PREFER_UDP = "prefer_UDP"; + String GATEWAY_PINNING = "gateway_pinning"; ////////////////////////////////////////////// diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java index 27943022..ca1261a8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java @@ -16,6 +16,8 @@ */ package se.leap.bitmaskclient.base.utils; +import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK; + import android.content.Context; import android.content.res.Resources; import android.os.Build; @@ -37,7 +39,6 @@ import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -47,7 +48,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -55,20 +55,20 @@ import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.providersetup.ProviderAPI; -import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK; - /** * Stores constants, and implements auxiliary methods used across all Bitmask Android classes. * Wraps BuildConfigFields for to support easier unit testing * * @author parmegv * @author MeanderingCode + * @author cyberta */ public class ConfigHelper { final public static String NG_1024 = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3"; final public static BigInteger G = new BigInteger("2"); final public static Pattern IPv4_PATTERN = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$"); + final public static Pattern PEM_CERTIFICATE_PATTERN = Pattern.compile("((-----BEGIN CERTIFICATE-----)([A-Za-z0-9+/=\\n]+)(-----END CERTIFICATE-----)+)"); public static boolean checkErroneousDownload(String downloadedString) { try { @@ -103,23 +103,26 @@ public class ConfigHelper { } public static ArrayList<X509Certificate> parseX509CertificatesFromString(String certificateString) { - Collection<? extends Certificate> certificates; + ArrayList<X509Certificate> certificates = new ArrayList<>(); CertificateFactory cf; try { cf = CertificateFactory.getInstance("X.509"); - certificateString = certificateString.replaceAll("-----BEGIN CERTIFICATE-----", "").trim().replaceAll("-----END CERTIFICATE-----", "").trim(); - byte[] certBytes = Base64.decode(certificateString); - try (InputStream caInput = new ByteArrayInputStream(certBytes)) { - certificates = cf.generateCertificates(caInput); - if (certificates != null) { - for (Certificate cert : certificates) { - System.out.println("ca=" + ((X509Certificate) cert).getSubjectDN()); - } - return (ArrayList<X509Certificate>) certificates; + Matcher matcher = PEM_CERTIFICATE_PATTERN.matcher(certificateString); + while (matcher.find()) { + String certificate = matcher.group(3); + if (certificate == null) continue; + byte[] certBytes = Base64.decode(certificate.trim()); + try (InputStream caInput = new ByteArrayInputStream(certBytes)) { + X509Certificate x509certificate = (X509Certificate) cf.generateCertificate(caInput); + certificates.add(x509certificate); + System.out.println("ca=" + x509certificate.getSubjectDN() + ", SAN= " + x509certificate.getSubjectAlternativeNames()); + } catch (IOException | CertificateException | NullPointerException | IllegalArgumentException | ClassCastException e) { + e.printStackTrace(); } } - } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException | ClassCastException e) { + return certificates; + } catch (CertificateException e) { e.printStackTrace(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index fe9100cb..08bfbdc3 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -26,6 +26,7 @@ import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_WIFI; import static se.leap.bitmaskclient.base.models.Constants.ALWAYS_ON_SHOW_DIALOG; import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; import static se.leap.bitmaskclient.base.models.Constants.EXCLUDED_APPS; +import static se.leap.bitmaskclient.base.models.Constants.GATEWAY_PINNING; import static se.leap.bitmaskclient.base.models.Constants.LAST_UPDATE_CHECK; import static se.leap.bitmaskclient.base.models.Constants.LAST_USED_PROFILE; import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY; @@ -154,6 +155,14 @@ public class PreferenceHelper { putBoolean(context, PREFER_UDP, prefer); } + public static String getPinnedGateway(Context context) { + return getString(context, GATEWAY_PINNING, null); + } + + public static void pinGateway(Context context, String value) { + putString(context, GATEWAY_PINNING, value); + } + public static boolean getUseBridges(SharedPreferences preferences) { return preferences.getBoolean(USE_BRIDGES, false); } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 76ec9650..a11c7e34 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -41,6 +41,7 @@ import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Connection.TransportType; +import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.base.models.Location; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; @@ -338,9 +339,11 @@ public class GatewaysManager { if (gateways.get(aux.getHost()) == null) { addGateway(aux); } - } catch (JSONException | ConfigParser.ConfigParseError | IOException e) { + } catch (JSONException | IOException e) { e.printStackTrace(); VpnStatus.logError("Unable to parse gateway config!"); + } catch (ConfigParser.ConfigParseError e) { + VpnStatus.logError("Unable to parse gateway config: " + e.getLocalizedMessage()); } } } catch (NullPointerException npe) { @@ -419,6 +422,9 @@ public class GatewaysManager { private void configureFromCurrentProvider() { Provider provider = ProviderObservable.getInstance().getCurrentProvider(); parseDefaultGateways(provider); + if (BuildConfig.BUILD_TYPE.equals("debug") && handleGatewayPinning()) { + return; + } if (hasSortedGatewaysWithLoad(provider)) { parseGatewaysWithLoad(provider); } else { @@ -427,5 +433,17 @@ public class GatewaysManager { } + private boolean handleGatewayPinning() { + String host = PreferenceHelper.getPinnedGateway(this.context); + if (host == null) { + return false; + } + Gateway gateway = gateways.get(host); + gateways.clear(); + if (gateway != null) { + gateways.put(host, gateway); + } + return true; + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java index d2603533..a869210e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java @@ -196,7 +196,8 @@ public class VpnNotificationManager { } public void cancelAll() { - compatNotificationManager.cancelAll(); + compatNotificationManager.cancel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID.hashCode()); + compatNotificationManager.cancel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID.hashCode()); } |