From 4a7a55d504cf9303513d0d8d1237cf413ff08e2b Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 20:04:51 +0100 Subject: [PATCH] Ability to block QUIC always or only on decryption Commit ddec1a8 limited the ability to block QUIC to only connections matching the decryption rules. Some users may still want to always block QUIC for different reasons, so this commit makes it possible to choose the block policy to apply. See #369 --- .../remote_capture/CaptureService.java | 2 +- .../activities/prefs/SettingsActivity.java | 12 ++++----- .../remote_capture/model/CaptureSettings.java | 6 ++--- .../emanuelef/remote_capture/model/Prefs.java | 25 ++++++++++++++++--- app/src/main/jni/core/capture_vpn.c | 14 +++++++---- app/src/main/jni/core/pcapdroid.h | 9 ++++++- app/src/main/res/values/arrays.xml | 11 ++++++++ app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/root_preferences.xml | 10 +++++--- docs/app_api.md | 2 +- 10 files changed, 70 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index 7e104165..6d9fb267 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -1309,7 +1309,7 @@ public class CaptureService extends VpnService implements Runnable { public int getVpnMTU() { return VPN_MTU; } - public int blockQuick() { return(mSettings.block_quic ? 1 : 0); } + public int getBlockQuickMode() { return mSettings.block_quic_mode.ordinal(); } // returns 1 if dumpPcapData should be called public int pcapDumpEnabled() { diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java index 223befad..240c5266 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java @@ -132,13 +132,13 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment public static class SettingsFragment extends PreferenceFragmentCompat { private SwitchPreference mTlsDecryption; - private SwitchPreference mBlockQuic; private SwitchPreference mFullPayloadEnabled; private SwitchPreference mRootCaptureEnabled; private SwitchPreference mAutoBlockPrivateDNS; private EditTextPreference mMitmproxyOpts; private DropDownPreference mIpMode; private DropDownPreference mCapInterface; + private DropDownPreference mBlockQuic; private Preference mVpnExceptions; private Preference mSocks5Settings; private Preference mDnsSettings; @@ -167,7 +167,8 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment setupSecurityPrefs(); setupOtherPrefs(); - socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled()); + socks5ProxyHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled()); + mBlockQuic.setVisible(!rootCaptureEnabled()); rootCaptureHideShow(rootCaptureEnabled()); Intent intent = requireActivity().getIntent(); @@ -309,7 +310,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment mMitmWizard.setVisible((boolean) newValue); mMitmproxyOpts.setVisible((boolean) newValue); - socks5ProxyAndQuicHideShow((boolean) newValue, rootCaptureEnabled()); + socks5ProxyHideShow((boolean) newValue, rootCaptureEnabled()); return true; }); @@ -343,9 +344,8 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment mSocks5Settings = requirePreference("socks5_settings"); } - private void socks5ProxyAndQuicHideShow(boolean tlsDecryption, boolean rootEnabled) { + private void socks5ProxyHideShow(boolean tlsDecryption, boolean rootEnabled) { mSocks5Settings.setVisible(!tlsDecryption && !rootEnabled); - mBlockQuic.setVisible(tlsDecryption && !rootEnabled); } private void setupOtherPrefs() { @@ -406,7 +406,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment } else { mAutoBlockPrivateDNS.setVisible(true); mBlockQuic.setVisible(true); - socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), false); + socks5ProxyHideShow(mTlsDecryption.isChecked(), false); } mIpMode.setVisible(!enabled); diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java index 5c752308..a7aa65d1 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java @@ -26,7 +26,7 @@ public class CaptureSettings implements Serializable { public boolean root_capture; public boolean pcapdroid_trailer; public boolean full_payload; - public boolean block_quic; + public Prefs.BlockQuicMode block_quic_mode; public boolean auto_block_private_dns; public boolean pcapng_format; public String capture_interface; @@ -54,7 +54,7 @@ public class CaptureSettings implements Serializable { capture_interface = Prefs.getCaptureInterface(prefs); tls_decryption = Prefs.getTlsDecryptionEnabled(prefs); full_payload = Prefs.getFullPayloadMode(prefs); - block_quic = Prefs.blockQuic(prefs); + block_quic_mode = Prefs.getBlockQuicMode(prefs); auto_block_private_dns = Prefs.isPrivateDnsBlockingEnabled(prefs); mitmproxy_opts = Prefs.getMitmproxyOpts(prefs); pcapng_format = Prefs.isPcapngEnabled(ctx, prefs); @@ -82,7 +82,7 @@ public class CaptureSettings implements Serializable { max_dump_size = getInt(intent, Prefs.PREF_MAX_DUMP_SIZE, 0); tls_decryption = getBool(intent, Prefs.PREF_TLS_DECRYPTION_KEY, false); full_payload = false; - block_quic = getBool(intent, Prefs.PREF_BLOCK_QUIC, false); + block_quic_mode = Prefs.getBlockQuicMode(getString(intent, Prefs.PREF_BLOCK_QUIC, Prefs.BLOCK_QUIC_MODE_DEFAULT)); auto_block_private_dns = getBool(intent, Prefs.PREF_AUTO_BLOCK_PRIVATE_DNS, true); mitmproxy_opts = getString(intent, Prefs.PREF_MITMPROXY_OPTS, ""); pcapng_format = getBool(intent, Prefs.PREF_PCAPNG_ENABLED, false) && Billing.newInstance(ctx).isPurchased(Billing.PCAPNG_SKU); diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java index 474434d3..b9b7712c 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java @@ -41,6 +41,11 @@ public class Prefs { public static final String IP_MODE_BOTH = "both"; public static final String IP_MODE_DEFAULT = IP_MODE_IPV4_ONLY; + public static final String BLOCK_QUIC_MODE_NEVER = "never"; + public static final String BLOCK_QUIC_MODE_ALWAYS = "always"; + public static final String BLOCK_QUIC_MODE_TO_DECRYPT = "to_decrypt"; + public static final String BLOCK_QUIC_MODE_DEFAULT = BLOCK_QUIC_MODE_NEVER; + public static final String PAYLOAD_MODE_NONE = "none"; public static final String PAYLOAD_MODE_MINIMAL = "minimal"; public static final String PAYLOAD_MODE_FULL = "full"; @@ -83,7 +88,7 @@ public class Prefs { public static final String PREF_TLS_DECRYPTION_SETUP_DONE = "tls_decryption_setup_ok"; public static final String PREF_CA_INSTALLATION_SKIPPED = "ca_install_skipped"; public static final String PREF_FULL_PAYLOAD = "full_payload"; - public static final String PREF_BLOCK_QUIC = "block_quic"; + public static final String PREF_BLOCK_QUIC = "block_quic_mode"; public static final String PREF_AUTO_BLOCK_PRIVATE_DNS = "auto_block_private_dns"; public static final String PREF_APP_VERSION = "appver"; public static final String PREF_LOCKDOWN_VPN_NOTICE_SHOWN = "vpn_lockdown_notice"; @@ -113,6 +118,12 @@ public class Prefs { BOTH, } + public enum BlockQuicMode { + NEVER, + ALWAYS, + TO_DECRYPT + } + public enum PayloadMode { NONE, MINIMAL, @@ -136,6 +147,14 @@ public class Prefs { } } + public static BlockQuicMode getBlockQuicMode(String pref) { + switch (pref) { + case BLOCK_QUIC_MODE_ALWAYS: return BlockQuicMode.ALWAYS; + case BLOCK_QUIC_MODE_TO_DECRYPT: return BlockQuicMode.TO_DECRYPT; + default: return BlockQuicMode.NEVER; + } + } + public static PayloadMode getPayloadMode(String pref) { switch (pref) { case PAYLOAD_MODE_MINIMAL: return PayloadMode.MINIMAL; @@ -182,6 +201,7 @@ public class Prefs { public static String getSocks5Password(SharedPreferences p) { return(p.getString(PREF_SOCKS5_PASSWORD_KEY, "")); } public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); } public static IpMode getIPMode(SharedPreferences p) { return(getIPMode(p.getString(PREF_IP_MODE, IP_MODE_DEFAULT))); } + public static BlockQuicMode getBlockQuicMode(SharedPreferences p) { return(getBlockQuicMode(p.getString(PREF_BLOCK_QUIC, BLOCK_QUIC_MODE_DEFAULT))); } public static boolean useEnglishLanguage(SharedPreferences p){ return("english".equals(p.getString(PREF_APP_LANGUAGE, "system")));} public static boolean isRootCaptureEnabled(SharedPreferences p) { return(Utils.isRootAvailable() && p.getBoolean(PREF_ROOT_CAPTURE, false)); } public static boolean isPcapdroidTrailerEnabled(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER, false)); } @@ -202,7 +222,6 @@ public class Prefs { public static boolean startAtBoot(SharedPreferences p) { return(p.getBoolean(PREF_START_AT_BOOT, false)); } public static boolean isTLSDecryptionSetupDone(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_SETUP_DONE, false)); } public static boolean getFullPayloadMode(SharedPreferences p) { return(p.getBoolean(PREF_FULL_PAYLOAD, false)); } - public static boolean blockQuic(SharedPreferences p) { return(p.getBoolean(PREF_BLOCK_QUIC, false)); } public static boolean isPrivateDnsBlockingEnabled(SharedPreferences p) { return(p.getBoolean(PREF_AUTO_BLOCK_PRIVATE_DNS, true)); } public static boolean lockdownVpnNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_LOCKDOWN_VPN_NOTICE_SHOWN, false)); } public static boolean trailerNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER_NOTICE_SHOWN, false)); } @@ -224,7 +243,7 @@ public class Prefs { "\nTLSDecryption: " + getTlsDecryptionEnabled(p) + "\nTLSSetupOk: " + isTLSDecryptionSetupDone(p) + "\nCAInstallSkipped: " + MitmAddon.isCAInstallationSkipped(ctx) + - "\nBlockQuic: " + blockQuic(p) + + "\nBlockQuic: " + getBlockQuicMode(p) + "\nRootCapture: " + isRootCaptureEnabled(p) + "\nSocks5: " + getSocks5Enabled(p) + "\nBlockPrivateDns: " + isPrivateDnsBlockingEnabled(p) + diff --git a/app/src/main/jni/core/capture_vpn.c b/app/src/main/jni/core/capture_vpn.c index 1ca852c1..e653a228 100644 --- a/app/src/main/jni/core/capture_vpn.c +++ b/app/src/main/jni/core/capture_vpn.c @@ -385,10 +385,14 @@ static bool should_proxify(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn /* ******************************************************* */ void vpn_process_ndpi(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) { - if(pd->vpn.block_quic && (data->l7proto == NDPI_PROTOCOL_QUIC) && - pd->tls_decryption.enabled && matches_decryption_whitelist(pd, tuple, data)) { - data->blacklisted_internal = true; - data->to_block = true; + if(data->l7proto == NDPI_PROTOCOL_QUIC) { + block_quic_mode_t block_mode = pd->vpn.block_quic_mode; + + if ((block_mode == BLOCK_QUIC_MODE_ALWAYS) || + ((block_mode == BLOCK_QUIC_MODE_TO_DECRYPT) && matches_decryption_whitelist(pd, tuple, data))) { + data->blacklisted_internal = true; + data->to_block = true; + } } if(block_private_dns && !data->to_block && @@ -449,7 +453,7 @@ int run_vpn(pcapdroid_t *pd) { #if ANDROID pd->vpn.resolver = init_uid_resolver(pd->sdk_ver, pd->env, pd->capture_service); pd->vpn.known_dns_servers = blacklist_init(); - pd->vpn.block_quic = getIntPref(pd->env, pd->capture_service, "blockQuick"); + pd->vpn.block_quic_mode = getIntPref(pd->env, pd->capture_service, "getBlockQuickMode"); pd->vpn.ipv4.enabled = (bool) getIntPref(pd->env, pd->capture_service, "getIPv4Enabled"); pd->vpn.ipv4.dns_server = getIPv4Pref(pd->env, pd->capture_service, "getDnsServer"); diff --git a/app/src/main/jni/core/pcapdroid.h b/app/src/main/jni/core/pcapdroid.h index 045b3078..c90badb0 100644 --- a/app/src/main/jni/core/pcapdroid.h +++ b/app/src/main/jni/core/pcapdroid.h @@ -65,6 +65,13 @@ typedef enum { PAYLOAD_MODE_FULL } payload_mode_t; +// NOTE: sync with Prefs.BlockQuicMode +typedef enum { + BLOCK_QUIC_MODE_NEVER = 0, + BLOCK_QUIC_MODE_ALWAYS, + BLOCK_QUIC_MODE_TO_DECRYPT +} block_quic_mode_t; + typedef struct { jint incr_id; // an incremental number which identifies a specific connection @@ -205,7 +212,7 @@ typedef struct pcapdroid { union { struct { int tunfd; - bool block_quic; + block_quic_mode_t block_quic_mode; blacklist_t *known_dns_servers; uid_resolver_t *resolver; diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 122ce756..d33152e2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -49,4 +49,15 @@ @string/theme_light @string/theme_dark + + + never + always + to_decrypt + + + @string/never + @string/always + @string/for_connections_to_decrypt + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e21871a..7c7dc3ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -497,4 +497,7 @@ redirected This connection has been redirected due to a port mapping rule How to use DoH / DNSCrypt with PCAPdroid + Never + Always + Only for connections to decrypt diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 38d2865e..3923a96f 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -94,12 +94,14 @@ app:summary="@string/pcapng_format_summary" app:defaultValue="true" /> - + app:defaultValue="never" + app:useSimpleSummaryProvider="true" />