diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java b/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java index f80eb711..e765d905 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java @@ -106,6 +106,7 @@ public class CaptureCtrl extends AppCompatActivity { Intent intent = getIntent(); String action = intent.getStringExtra("action"); + String api_key = intent.getStringExtra("api_key"); if(action == null) { Log.e(TAG, "no action provided"); @@ -118,6 +119,17 @@ public class CaptureCtrl extends AppCompatActivity { return; } + if(api_key != null) { + // authenticate via API key + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String my_key = Prefs.getApiKey(prefs); + + if (!my_key.isEmpty() && my_key.equals(api_key)) { + processRequest(intent, action); + return; + } + } + // Check if a control permission rule was set mPermissions = PCAPdroid.getInstance().getCtrlPermissions(); AppDescriptor app = getCallingApp(); 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 a0991299..c774df07 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 @@ -132,6 +132,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig public static final String PAID_FEATURES_URL = DOCS_URL + "/paid_features"; public static final String FIREWALL_DOCS_URL = PAID_FEATURES_URL + "#51-firewall"; public static final String MALWARE_DETECTION_DOCS_URL = PAID_FEATURES_URL + "#52-malware-detection"; + public static final String API_DOCS_URL = GITHUB_PROJECT_URL + "/blob/master/docs/app_api.md"; public static final String PCAPNG_DOCS_URL = PAID_FEATURES_URL + "#53-pcapng-format"; private final ActivityResultLauncher sslkeyfileExportLauncher = diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/EditCtrlPermissions.java b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/EditCtrlPermissions.java index eecc0163..da5d3675 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/EditCtrlPermissions.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/EditCtrlPermissions.java @@ -18,6 +18,9 @@ */ package com.emanuelef.remote_capture.activities.prefs; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; import android.os.Bundle; import android.view.ActionMode; import android.view.Menu; @@ -28,21 +31,31 @@ import android.widget.AbsListView; import android.widget.ListView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.view.MenuProvider; +import androidx.preference.PreferenceManager; + import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; import com.emanuelef.remote_capture.activities.BaseActivity; +import com.emanuelef.remote_capture.activities.MainActivity; import com.emanuelef.remote_capture.adapters.CtrlPermissionsAdapter; import com.emanuelef.remote_capture.model.CtrlPermissions; +import com.emanuelef.remote_capture.model.Prefs; +import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Base64; -public class EditCtrlPermissions extends BaseActivity { +public class EditCtrlPermissions extends BaseActivity implements MenuProvider { private static final String TAG = "EditCtrlPermissions"; private TextView mEmptyText; private CtrlPermissionsAdapter mAdapter; private ListView mListView; private CtrlPermissions mPermissions; + private MenuItem mShowApiKey; private final ArrayList mSelected = new ArrayList<>(); @Override @@ -51,9 +64,11 @@ public class EditCtrlPermissions extends BaseActivity { setTitle(R.string.control_permissions); setContentView(R.layout.simple_list_activity); + addMenuProvider(this); findViewById(R.id.simple_list).setFitsSystemWindows(true); mEmptyText = findViewById(R.id.list_empty); + mEmptyText.setText(R.string.no_permissions_set_info); mListView = findViewById(R.id.listview); mPermissions = PCAPdroid.getInstance().getCtrlPermissions(); @@ -126,7 +141,81 @@ public class EditCtrlPermissions extends BaseActivity { recheckListSize(); } + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + inflater.inflate(R.menu.ctrl_permissions_menu, menu); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + mShowApiKey = menu.findItem(R.id.show_api_key); + + if (Prefs.getApiKey(prefs).isEmpty()) + mShowApiKey.setVisible(false); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem item) { + int id = item.getItemId(); + + if(id == R.id.user_guide) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(MainActivity.API_DOCS_URL)); + Utils.startActivity(this, browserIntent); + return true; + } else if (id == R.id.generate_api_key) { + generateApiKey(false); + return true; + } else if (id == R.id.show_api_key) { + showApiKey(); + return true; + } + + return false; + } + private void recheckListSize() { mEmptyText.setVisibility((mAdapter.getCount() == 0) ? View.VISIBLE : View.GONE); } + + private void generateApiKey(boolean confirmOverwrite) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + if (!confirmOverwrite && !Prefs.getApiKey(prefs).isEmpty()) { + new AlertDialog.Builder(this) + .setTitle(R.string.warning) + .setMessage(R.string.api_key_discard_confirm) + .setPositiveButton(R.string.ok, (dialog, whichButton) -> generateApiKey(true)) + .setNegativeButton(R.string.cancel_action, (dialog, whichButton) -> {}) + .show(); + return; + } + + final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + final int key_length = 32; + SecureRandom random = new SecureRandom(); + StringBuilder apiKey = new StringBuilder(key_length); + for (int i = 0; i < key_length; i++) { + int index = random.nextInt(chars.length()); + apiKey.append(chars.charAt(index)); + } + + prefs.edit() + .putString(Prefs.PREF_API_KEY, apiKey.toString()) + .apply(); + + if (mShowApiKey != null) + mShowApiKey.setVisible(true); + showApiKey(); + } + + private void showApiKey() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String key = Prefs.getApiKey(prefs); + if (key.isEmpty()) + return; + + new AlertDialog.Builder(this) + .setTitle(R.string.api_key) + .setMessage(key) + .setPositiveButton(R.string.ok, (dialogInterface, i) -> {}) + .setNeutralButton(R.string.copy_to_clipboard, (dialogInterface, i) -> + Utils.copyToClipboard(this, key)).show(); + } } 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 2b77d271..867088b2 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 @@ -461,14 +461,11 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment mIpMode = requirePreference(Prefs.PREF_IP_MODE); Preference ctrlPerm = requirePreference("control_permissions"); - if(!PCAPdroid.getInstance().getCtrlPermissions().hasRules()) - ctrlPerm.setVisible(false); - else - ctrlPerm.setOnPreferenceClickListener(preference -> { - Intent intent = new Intent(requireContext(), EditCtrlPermissions.class); - startActivity(intent); - return true; - }); + ctrlPerm.setOnPreferenceClickListener(preference -> { + Intent intent = new Intent(requireContext(), EditCtrlPermissions.class); + startActivity(intent); + return true; + }); } private void rootCaptureHideShow(boolean enabled) { 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 ad1451af..e71cb1a9 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 @@ -110,6 +110,7 @@ public class Prefs { public static final String PREF_PCAPNG_ENABLED = "pcapng_format"; public static final String PREF_RESTART_ON_DISCONNECT = "restart_on_disconnect"; public static final String PREF_IGNORED_MITM_VERSION = "ignored_mitm_version"; + public static final String PREF_API_KEY = "api_key"; public enum DumpMode { NONE, @@ -238,6 +239,7 @@ public class Prefs { public static String getDnsServerV4(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V4, "1.1.1.1")); } public static String getDnsServerV6(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V6, "2606:4700:4700::1111")); } public static boolean isIgnoredMitmVersion(SharedPreferences p, String v) { return p.getString(PREF_IGNORED_MITM_VERSION, "").equals(v); } + public static String getApiKey(SharedPreferences p) { return(p.getString(PREF_API_KEY, "")); } // Gets a StringSet from the prefs // The preference should either be a StringSet or a String diff --git a/app/src/main/res/menu/ctrl_permissions_menu.xml b/app/src/main/res/menu/ctrl_permissions_menu.xml new file mode 100644 index 00000000..48545861 --- /dev/null +++ b/app/src/main/res/menu/ctrl_permissions_menu.xml @@ -0,0 +1,24 @@ + + + + + + + + + \ 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 2e599b6a..49f1a78a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -217,6 +217,11 @@ Control permissions Check which apps are allowed to control the PCAPdroid capture %1$s: %2$s + No permissions set. Invoke PCAPdroid via StartActivityForResult to show the permissions prompt + Generate API key + Show API key + API key + Do you really want to discard the current API key and generate a new one? Country ASN Country: %1$s