From 53b42af3c7416f1e237a1237320706d9bfc6ab6d Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sat, 9 Jul 2022 19:38:29 +0200 Subject: [PATCH] Ability to block newly installed apps (firewall) Closes #208 --- .../remote_capture/CaptureService.java | 53 ++++++++++++++++++- .../activities/MainActivity.java | 1 - .../fragments/FirewallStatus.java | 7 +++ .../emanuelef/remote_capture/model/Prefs.java | 2 + app/src/main/res/menu/firewall_menu.xml | 14 +++-- app/src/main/res/values/strings.xml | 3 ++ 6 files changed, 75 insertions(+), 5 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 ecfeda02..d7e066fc 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -25,8 +25,10 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.ConnectivityManager; @@ -98,6 +100,7 @@ public class CaptureService extends VpnService implements Runnable { private static final int VPN_MTU = 10000; private static final int NOTIFY_ID_VPNSERVICE = 1; private static final int NOTIFY_ID_LOW_MEMORY = 2; + private static final int NOTIFY_ID_APP_BLOCKED = 3; private static CaptureService INSTANCE; final ReentrantLock mLock = new ReentrantLock(); final Condition mCaptureStopped = mLock.newCondition(); @@ -147,6 +150,7 @@ public class CaptureService extends VpnService implements Runnable { private String mSocks5Auth; private CaptureStats mLastStats; private boolean mLowMemory; + private BroadcastReceiver mNewAppsInstallReceiver; /* The maximum connections to log into the ConnectionsRegister. Older connections are dropped. * Max estimated memory usage: less than 4 MB (+8 MB with payload mode minimal). */ @@ -464,6 +468,50 @@ public class CaptureService extends VpnService implements Runnable { mDumperThread.start(); } + if(mFirewallEnabled) { + mNewAppsInstallReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // executed on the main thread + if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) { + boolean newInstall = !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + String packageName = intent.getData().getSchemeSpecificPart(); + Log.d(TAG, "ACTION_PACKAGE_ADDED [new=" + newInstall + "]: " + packageName); + + if(newInstall && Prefs.blockNewApps(mPrefs)) { + Log.i(TAG, "Blocking newly installed app: " + packageName); + mBlocklist.addApp(packageName); + mBlocklist.save(); + reloadBlocklist(); + + AppDescriptor app = appsResolver.getByPackage(packageName, 0); + String label = (app != null) ? app.getName() : packageName; + + // Notify the user + NotificationManagerCompat man = NotificationManagerCompat.from(context); + if(man.areNotificationsEnabled()) { + Notification notification = new NotificationCompat.Builder(CaptureService.this, NOTIFY_CHAN_OTHER) + .setSmallIcon(R.drawable.ic_logo) + .setColor(ContextCompat.getColor(CaptureService.this, R.color.colorPrimary)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setContentTitle(getString(R.string.app_blocked)) + .setContentText(getString(R.string.app_blocked_info, label)) + .build(); + + man.notify(NOTIFY_ID_APP_BLOCKED, notification); + } + } + } + } + }; + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addDataScheme("package"); + registerReceiver(mNewAppsInstallReceiver, filter); + } + // Start the native capture thread mQueueFull = false; mCaptureThread = new Thread(this, "PacketCapture"); @@ -493,7 +541,10 @@ public class CaptureService extends VpnService implements Runnable { if(mBlacklistsUpdateThread != null) mBlacklistsUpdateThread.interrupt(); - unregisterNetworkCallbacks(); + if(mNewAppsInstallReceiver != null) { + unregisterReceiver(mNewAppsInstallReceiver); + mNewAppsInstallReceiver = null; + } super.onDestroy(); } diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java index 25d94518..2250498b 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java @@ -62,7 +62,6 @@ import android.view.View; import android.widget.TextView; import com.emanuelef.remote_capture.Billing; -import com.emanuelef.remote_capture.BuildConfig; import com.emanuelef.remote_capture.CaptureHelper; import com.emanuelef.remote_capture.MitmReceiver; import com.emanuelef.remote_capture.fragments.ConnectionsFragment; diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java index ff3b6a80..2d4beb44 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java @@ -133,6 +133,8 @@ public class FirewallStatus extends Fragment { updateStatus(); }); + menu.findItem(R.id.block_new_apps).setChecked(Prefs.blockNewApps(mPrefs)); + updateStatus(); } @@ -144,6 +146,11 @@ public class FirewallStatus extends Fragment { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(MainActivity.FIREWALL_DOCS_URL)); Utils.startActivity(requireContext(), browserIntent); return true; + } else if(id == R.id.block_new_apps) { + boolean checked = !item.isChecked(); + item.setChecked(checked); + mPrefs.edit().putBoolean(Prefs.PREF_BLOCK_NEW_APPS, checked).apply(); + return true; } return false; 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 2d78e164..7e94de89 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 @@ -75,6 +75,7 @@ public class Prefs { public static final String PREF_APP_VERSION = "appver"; public static final String PREF_LOCKDOWN_VPN_NOTICE_SHOWN = "vpn_lockdown_notice"; public static final String PREF_VPN_EXCEPTIONS = "vpn_exceptions"; + public static final String PREF_BLOCK_NEW_APPS = "block_new_apps"; public enum DumpMode { NONE, @@ -163,4 +164,5 @@ public class Prefs { public static boolean blockQuic(SharedPreferences p) { return(getTlsDecryptionEnabled(p) && 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 blockNewApps(SharedPreferences p) { return(p.getBoolean(PREF_BLOCK_NEW_APPS, false)); } } diff --git a/app/src/main/res/menu/firewall_menu.xml b/app/src/main/res/menu/firewall_menu.xml index c2f41ad1..fc059855 100644 --- a/app/src/main/res/menu/firewall_menu.xml +++ b/app/src/main/res/menu/firewall_menu.xml @@ -6,13 +6,21 @@ + + app:showAsAction="never" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 96984559..dd4ce31c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -379,4 +379,7 @@ Cleartext Unblock permanently Unblock for %1$dh + Block newly installed apps + App blocked + The %1$s app has been blocked by the firewall