From 00984fc224e56e5e37bfb1f1b844fe4e4442a507 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 15 Feb 2022 18:13:19 +0100 Subject: [PATCH] Certificate export now uses the MitmService API --- .../emanuelef/remote_capture/MitmAddon.java | 225 ++++++++++++++++++ .../remote_capture/MitmReceiver.java | 122 +++++----- .../com/emanuelef/remote_capture/Utils.java | 11 + .../activities/MainActivity.java | 2 +- .../activities/SettingsActivity.java | 2 +- .../fragments/mitmwizard/GrantPermission.java | 2 +- .../fragments/mitmwizard/InstallAddon.java | 2 +- .../mitmwizard/InstallCertificate.java | 82 ++++--- .../interfaces/MitmListener.java | 28 +++ .../remote_capture/model/MitmAddon.java | 78 ------ app/src/main/res/values/strings.xml | 1 + 11 files changed, 374 insertions(+), 181 deletions(-) create mode 100644 app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java create mode 100644 app/src/main/java/com/emanuelef/remote_capture/interfaces/MitmListener.java delete mode 100644 app/src/main/java/com/emanuelef/remote_capture/model/MitmAddon.java diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java new file mode 100644 index 00000000..0cde76d0 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java @@ -0,0 +1,225 @@ +/* + * This file is part of PCAPdroid. + * + * PCAPdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PCAPdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PCAPdroid. If not, see . + * + * Copyright 2022 - Emanuele Faranda + */ + +package com.emanuelef.remote_capture; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceManager; + +import com.emanuelef.remote_capture.interfaces.MitmListener; +import com.emanuelef.remote_capture.model.Prefs; + +import java.io.IOException; +import java.lang.ref.WeakReference; + +public class MitmAddon { + /* API */ + public static final String PACKAGE_NAME = "com.pcapdroid.mitm"; + public static final String MITM_PERMISSION = "com.pcapdroid.permission.MITM"; + public static final String MITM_SERVICE = PACKAGE_NAME + ".MitmService"; + + public static final int MSG_START_MITM = 1; + public static final int MSG_GET_CA_CERTIFICATE = 2; + public static final String CERTIFICATE_RESULT = "certificate"; + /* END API */ + + private static final String TAG = "MitmAddon"; + private final Context mContext; + private final MitmListener mReceiver; + private final Messenger mMessenger; + private Messenger mService; + + public MitmAddon(Context ctx, MitmListener receiver) { + // Important: the application context is required here, otherwise bind/unbind will not work properly + mContext = ctx.getApplicationContext(); + mReceiver = receiver; + mMessenger = new Messenger(new ReplyHandler(receiver)); + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "Service connected"); + mService = new Messenger(service); + mReceiver.onMitmServiceConnect(); + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "Service disconnected"); + disconnect(); // call unbind to prevent new connections + mReceiver.onMitmServiceDisconnect(); + } + }; + + public static boolean isInstalled(Context ctx) { + try { + ctx.getPackageManager().getPackageInfo(PACKAGE_NAME, 0); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + public static boolean hasMitmPermission(Context ctx) { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + return ctx.checkSelfPermission(MitmAddon.MITM_PERMISSION) == PackageManager.PERMISSION_GRANTED; + + return true; + } + + public static void setDecryptionSetupDone(Context ctx, boolean done) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + prefs.edit() + .putBoolean(Prefs.PREF_TLS_DECRYPTION_SETUP_DONE, done) + .apply(); + } + + public static boolean needsSetup(Context ctx) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + + if(!Prefs.isTLSDecryptionSetupDone(prefs)) + return true; + + // Perform some other quick checks just in case the env has changed + if(!isInstalled(ctx) || !hasMitmPermission(ctx)) { + setDecryptionSetupDone(ctx, false); + return true; + } + + return false; + } + + private static class ReplyHandler extends Handler { + private final WeakReference mReceiver; + + ReplyHandler(MitmListener receiver) { + mReceiver = new WeakReference<>(receiver); + } + + @Override + public void handleMessage(@NonNull Message msg) { + Log.d(TAG, "Message: " + msg.what); + + MitmListener receiver = mReceiver.get(); + if(receiver == null) + return; + + if(msg.what == MitmAddon.MSG_GET_CA_CERTIFICATE) { + String ca_pem = null; + + if(msg.getData() != null) { + Bundle res = msg.getData(); + ca_pem = res.getString(MitmAddon.CERTIFICATE_RESULT); + } + + receiver.onMitmGetCaCertificateResult(ca_pem); + } + } + } + + // Asynchronously connect to the service. The onConnect callback will be called. + public boolean connect(int extra_flags) { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(MitmAddon.PACKAGE_NAME, MitmAddon.MITM_SERVICE)); + + if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | extra_flags)) { + mContext.unbindService(mConnection); + return false; + } + return true; + } + + // This must be always called after connect, e.g. in the OnDestroy + public void disconnect() { + if(mService != null) { + Log.d(TAG, "Unbinding service..."); + mContext.unbindService(mConnection); + mService = null; + } + } + + public boolean isConnected() { + return (mService != null); + } + + public boolean requestCaCertificate() { + if(mService == null) { + Log.e(TAG, "Not connected"); + return false; + } + + Message msg = Message.obtain(null, MitmAddon.MSG_GET_CA_CERTIFICATE); + msg.replyTo = mMessenger; + try { + mService.send(msg); + return true; + } catch (RemoteException e) { + e.printStackTrace(); + return false; + } + } + + // Start the mitm proxy and returns a ParcelFileDescriptor for the data communication. + // The proxy can be stopped by closing the descriptor and then calling disconnect(). + public ParcelFileDescriptor startProxy(int port) { + if(mService == null) { + Log.e(TAG, "Not connected"); + return null; + } + + ParcelFileDescriptor[] pair; + try { + pair = ParcelFileDescriptor.createReliableSocketPair(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + Message msg = Message.obtain(null, MitmAddon.MSG_START_MITM, port, 0, pair[0]); + + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + Utils.safeClose(pair[0]); + Utils.safeClose(pair[1]); + return null; + } + + // The other end of the pipe is sent, close it locally + Utils.safeClose(pair[0]); + + return pair[1]; + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java index 10f01d17..89bbe29c 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java @@ -22,22 +22,21 @@ package com.emanuelef.remote_capture; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.util.Log; import android.util.LruCache; +import com.emanuelef.remote_capture.activities.MitmSetupWizard; import com.emanuelef.remote_capture.interfaces.ConnectionsListener; +import com.emanuelef.remote_capture.interfaces.MitmListener; import com.emanuelef.remote_capture.model.ConnectionDescriptor; -import com.emanuelef.remote_capture.model.MitmAddon; + +import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; import java.util.NoSuchElementException; import java.util.StringTokenizer; @@ -53,70 +52,23 @@ import java.util.StringTokenizer; * * The raw payload data follows the header. */ -public class MitmReceiver implements Runnable, ConnectionsListener { +public class MitmReceiver implements Runnable, ConnectionsListener, MitmListener { private static final String TAG = "MitmReceiver"; public static final int MAX_PLAINTEXT_LENGTH = 1024; // sync with pcapdroid.h public static final int TLS_DECRYPTION_PROXY_PORT = 7780; private Thread mThread; private final ConnectionsRegister mReg; private final Context mContext; - private Messenger mService; - private boolean bound; + private final MitmAddon mAddon; private ParcelFileDescriptor mSocketFd; - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "Service connected"); - mService = new Messenger(service); - - ParcelFileDescriptor[] pair; - try { - // Create a pair of connected fds - pair = ParcelFileDescriptor.createReliableSocketPair(); - } catch (IOException e) { - e.printStackTrace(); - return; - } - mSocketFd = pair[1]; - - Message msg = Message.obtain(null, MitmAddon.MSG_START_MITM, TLS_DECRYPTION_PROXY_PORT, 0, pair[0]); - - try { - mService.send(msg); - } catch (RemoteException e) { - e.printStackTrace(); - Utils.safeClose(pair[0]); - Utils.safeClose(pair[1]); - return; - } - bound = true; - - // Sent, close here - Utils.safeClose(pair[0]); - - if(mThread != null) - mThread.interrupt(); - - mThread = new Thread(MitmReceiver.this); - mThread.start(); - } - - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "Service disconnected"); - mService = null; - Utils.safeClose(mSocketFd); - mSocketFd = null; - bound = false; - } - }; - // Shared state private final LruCache mPortToConnId = new LruCache<>(64); public MitmReceiver(Context ctx) { - // Important: the application context is required here, otherwise bind/unbind will not work properly - mContext = ctx.getApplicationContext(); + mContext = ctx; mReg = CaptureService.requireConnsRegister(); + mAddon = new MitmAddon(mContext, this); } public boolean start() throws IOException { @@ -125,8 +77,7 @@ public class MitmReceiver implements Runnable, ConnectionsListener { Intent intent = new Intent(); intent.setComponent(new ComponentName(MitmAddon.PACKAGE_NAME, MitmAddon.MITM_SERVICE)); - if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) { - mContext.unbindService(mConnection); + if(!mAddon.connect(Context.BIND_IMPORTANT)) { Utils.showToastLong(mContext, R.string.mitm_start_failed); return false; } @@ -152,19 +103,17 @@ public class MitmReceiver implements Runnable, ConnectionsListener { } mThread = null; - if(bound) { - Log.d(TAG, "Unbinding service..."); - mContext.unbindService(mConnection); - bound = false; - } + mAddon.disconnect(); Log.d(TAG, "stop done"); } @Override public void run() { + Log.d(TAG, "Receiving data..."); + try(DataInputStream istream = new DataInputStream(new ParcelFileDescriptor.AutoCloseInputStream(mSocketFd))) { - while(bound) { + while(mAddon.isConnected()) { String payload_type; int port; int payload_len; @@ -214,6 +163,8 @@ public class MitmReceiver implements Runnable, ConnectionsListener { if(mSocketFd != null) // ignore termination e.printStackTrace(); } + + Log.d(TAG, "End receiving data"); } @Override @@ -232,6 +183,47 @@ public class MitmReceiver implements Runnable, ConnectionsListener { } } + @Override + public void onMitmServiceConnect() { + // when connected, verify that the certificate is installed before starting the proxy. + // will continue on onMitmGetCaCertificateResult. + if(!mAddon.requestCaCertificate()) + mAddon.disconnect(); + } + + @Override + public void onMitmGetCaCertificateResult(@Nullable String ca_pem) { + if(!Utils.isCAInstalled(ca_pem)) { + // The certificate has been uninstalled from the system + Utils.showToastLong(mContext, R.string.cert_reinstall_required); + MitmAddon.setDecryptionSetupDone(mContext, false); + CaptureService.stopService(); + return; + } + + // Certificate installation verified, start the proxy + mSocketFd = mAddon.startProxy(TLS_DECRYPTION_PROXY_PORT); + if(mSocketFd == null) { + mAddon.disconnect(); + return; + } + + if(mThread != null) + mThread.interrupt(); + + mThread = new Thread(MitmReceiver.this); + mThread.start(); + } + + @Override + public void onMitmServiceDisconnect() { + Utils.safeClose(mSocketFd); + mSocketFd = null; + + // Stop the capture if running + CaptureService.stopService(); + } + ConnectionDescriptor getConnByLocalPort(int local_port) { Integer conn_id; diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 352e3e82..4f1a9f61 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -975,4 +975,15 @@ public class Utils { return false; } } + + public static boolean isCAInstalled(String ca_pem) { + if(ca_pem == null) + return false; + + X509Certificate ca_cert = x509FromPem(ca_pem); + if(ca_cert == null) + return false; + + return isCAInstalled(ca_cert); + } } 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 99e07fe7..ce497a5d 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 @@ -70,7 +70,7 @@ import com.emanuelef.remote_capture.model.AppState; import com.emanuelef.remote_capture.CaptureService; import com.emanuelef.remote_capture.model.CaptureSettings; import com.emanuelef.remote_capture.model.ListInfo; -import com.emanuelef.remote_capture.model.MitmAddon; +import com.emanuelef.remote_capture.MitmAddon; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java index c91626a1..7a5a8803 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java @@ -42,7 +42,7 @@ import androidx.preference.SwitchPreference; import com.emanuelef.remote_capture.Billing; import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.Utils; -import com.emanuelef.remote_capture.model.MitmAddon; +import com.emanuelef.remote_capture.MitmAddon; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.R; diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/GrantPermission.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/GrantPermission.java index b8124840..20f957c7 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/GrantPermission.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/GrantPermission.java @@ -30,7 +30,7 @@ import androidx.annotation.Nullable; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; -import com.emanuelef.remote_capture.model.MitmAddon; +import com.emanuelef.remote_capture.MitmAddon; public class GrantPermission extends StepFragment { private final ActivityResultLauncher requestPermissionLauncher = diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallAddon.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallAddon.java index 4af5afdd..e5f4e188 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallAddon.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallAddon.java @@ -26,7 +26,7 @@ import androidx.annotation.Nullable; import android.view.View; import com.emanuelef.remote_capture.R; -import com.emanuelef.remote_capture.model.MitmAddon; +import com.emanuelef.remote_capture.MitmAddon; public class InstallAddon extends StepFragment { @Override diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallCertificate.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallCertificate.java index fbe59898..3c19cff7 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallCertificate.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/mitmwizard/InstallCertificate.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.util.Log; import android.view.View; import androidx.activity.result.ActivityResult; @@ -35,18 +36,19 @@ import androidx.annotation.Nullable; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; -import com.emanuelef.remote_capture.model.MitmAddon; +import com.emanuelef.remote_capture.interfaces.MitmListener; +import com.emanuelef.remote_capture.MitmAddon; import java.io.IOException; import java.io.PrintWriter; import java.security.cert.X509Certificate; -public class InstallCertificate extends StepFragment { +public class InstallCertificate extends StepFragment implements MitmListener { + private static final String TAG = "InstallCertificate"; + private MitmAddon mAddon; private String mCaPem; private X509Certificate mCaCert; - private final ActivityResultLauncher mitmCtrlLauncher = - registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::mitmCtrlResult); private final ActivityResultLauncher certFileLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::certFileResult); @@ -57,15 +59,15 @@ public class InstallCertificate extends StepFragment { mStepButton.setText(R.string.export_action); mStepButton.setEnabled(false); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClassName(MitmAddon.PACKAGE_NAME, MitmAddon.CONTROL_ACTIVITY); - intent.putExtra(MitmAddon.ACTION_EXTRA, MitmAddon.ACTION_GET_CA_CERTIFICATE); + mAddon = new MitmAddon(requireContext(), this); + if(!mAddon.connect(0)) + certFail(); + } - try { - mitmCtrlLauncher.launch(intent); - } catch (ActivityNotFoundException e) { - mStepLabel.setText(R.string.no_intent_handler_found); - } + @Override + public void onDestroyView() { + mAddon.disconnect(); + super.onDestroyView(); } @Override @@ -76,28 +78,6 @@ public class InstallCertificate extends StepFragment { super.onResume(); } - private void mitmCtrlResult(final ActivityResult result) { - if((result.getResultCode() == Activity.RESULT_OK) && (result.getData() != null)) { - Intent res = result.getData(); - mCaPem = res.getStringExtra(MitmAddon.CERTIFICATE_RESULT); - - if(mCaPem != null) { - //Log.d(TAG, "certificate: " + cert_str); - mCaCert = Utils.x509FromPem(mCaPem); - - if(mCaCert != null) { - if(Utils.isCAInstalled(mCaCert)) - certOk(); - else { - MitmAddon.setDecryptionSetupDone(requireContext(), false); - installCaCertificate(); - } - } else - certFail(); - } - } - } - private void certOk() { MitmAddon.setDecryptionSetupDone(requireContext(), true); mStepLabel.setText(R.string.cert_installed_correctly); @@ -154,4 +134,38 @@ public class InstallCertificate extends StepFragment { Utils.showToastLong(ctx, R.string.cert_exported_now_installed); } } + + @Override + public void onMitmGetCaCertificateResult(@Nullable String ca_pem) { + mAddon.disconnect(); + mCaPem = ca_pem; + + if(mCaPem != null) { + Log.d(TAG, "Got certificate"); + //Log.d(TAG, "certificate: " + cert_str); + mCaCert = Utils.x509FromPem(mCaPem); + + if(mCaCert != null) { + if(Utils.isCAInstalled(mCaCert)) + certOk(); + else { + MitmAddon.setDecryptionSetupDone(requireContext(), false); + installCaCertificate(); + } + } else + certFail(); + } + } + + @Override + public void onMitmServiceConnect() { + if(!mAddon.requestCaCertificate()) + certFail(); + } + + @Override + public void onMitmServiceDisconnect() { + if(mCaPem == null) + certFail(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/emanuelef/remote_capture/interfaces/MitmListener.java b/app/src/main/java/com/emanuelef/remote_capture/interfaces/MitmListener.java new file mode 100644 index 00000000..f0d7ce0d --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/interfaces/MitmListener.java @@ -0,0 +1,28 @@ +/* + * This file is part of PCAPdroid. + * + * PCAPdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PCAPdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PCAPdroid. If not, see . + * + * Copyright 2020-21 - Emanuele Faranda + */ + +package com.emanuelef.remote_capture.interfaces; + +import org.jetbrains.annotations.Nullable; + +public interface MitmListener { + void onMitmGetCaCertificateResult(@Nullable String ca_pem); + void onMitmServiceConnect(); + void onMitmServiceDisconnect(); +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/MitmAddon.java b/app/src/main/java/com/emanuelef/remote_capture/model/MitmAddon.java deleted file mode 100644 index 579bf254..00000000 --- a/app/src/main/java/com/emanuelef/remote_capture/model/MitmAddon.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of PCAPdroid. - * - * PCAPdroid is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * PCAPdroid is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with PCAPdroid. If not, see . - * - * Copyright 2022 - Emanuele Faranda - */ - -package com.emanuelef.remote_capture.model; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; - -import androidx.preference.PreferenceManager; - -public class MitmAddon { - public static final String PACKAGE_NAME = "com.pcapdroid.mitm"; - public static final String MITM_PERMISSION = "com.pcapdroid.permission.MITM"; - - public static final String MITM_SERVICE = PACKAGE_NAME + ".MitmService"; - public static final int MSG_START_MITM = 1; - - public static final String CONTROL_ACTIVITY = PACKAGE_NAME + ".MitmCtrl"; - public static final String ACTION_EXTRA = "action"; - public static final String ACTION_GET_CA_CERTIFICATE = "getCAcert"; - public static final String CERTIFICATE_RESULT = "certificate"; - - public static boolean isInstalled(Context ctx) { - try { - ctx.getPackageManager().getPackageInfo(PACKAGE_NAME, 0); - return true; - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - - public static boolean hasMitmPermission(Context ctx) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - return ctx.checkSelfPermission(MitmAddon.MITM_PERMISSION) == PackageManager.PERMISSION_GRANTED; - - return true; - } - - public static void setDecryptionSetupDone(Context ctx, boolean done) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - prefs.edit() - .putBoolean(Prefs.PREF_TLS_DECRYPTION_SETUP_DONE, done) - .apply(); - } - - public static boolean needsSetup(Context ctx) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - - if(!Prefs.isTLSDecryptionSetupDone(prefs)) - return true; - - // Perform some other quick checks just in case the env has changed - if(!isInstalled(ctx) || !hasMitmPermission(ctx)) { - setDecryptionSetupDone(ctx, false); - return true; - } - - return false; - } -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0767248a..fd48cec0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -279,5 +279,6 @@ CA certificate installation failed Certificate exported, now install it from the Android settings The CA certificate is installed + The CA certificate is not installed, run the mitm setup wizard Done \ No newline at end of file