Tapjacking protection

This commit is contained in:
vvb2060 2020-10-22 02:40:47 -07:00 committed by topjohnwu
parent 8ac1181e9a
commit 533cb8eb58
9 changed files with 63 additions and 6 deletions

View File

@ -70,6 +70,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
ensureInsets() ensureInsets()
} }
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
viewRoot.rootView.accessibilityDelegate = delegate
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.requestRefresh() viewModel.requestRefresh()
@ -79,7 +83,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event) return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
} }
override fun onEventDispatched(event: ViewEvent) = when(event) { override fun onEventDispatched(event: ViewEvent) = when (event) {
is ContextExecutor -> event(this) is ContextExecutor -> event(this)
is ActivityExecutor -> event(this) is ActivityExecutor -> event(this)
else -> Unit else -> Unit

View File

@ -50,6 +50,7 @@ object Config : PreferenceModel, DBConfig {
const val SU_AUTO_RESPONSE = "su_auto_response" const val SU_AUTO_RESPONSE = "su_auto_response"
const val SU_NOTIFICATION = "su_notification" const val SU_NOTIFICATION = "su_notification"
const val SU_REAUTH = "su_reauth" const val SU_REAUTH = "su_reauth"
const val SU_TAPJACK = "su_tapjack"
const val CHECK_UPDATES = "check_update" const val CHECK_UPDATES = "check_update"
const val UPDATE_CHANNEL = "update_channel" const val UPDATE_CHANNEL = "update_channel"
const val CUSTOM_CHANNEL = "custom_channel" const val CUSTOM_CHANNEL = "custom_channel"
@ -134,6 +135,7 @@ object Config : PreferenceModel, DBConfig {
var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal) var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal)
var suReAuth by preference(Key.SU_REAUTH, false) var suReAuth by preference(Key.SU_REAUTH, false)
var suTapjack by preference(Key.SU_TAPJACK, true)
var checkUpdate by preference(Key.CHECK_UPDATES, true) var checkUpdate by preference(Key.CHECK_UPDATES, true)
var doh by preference(Key.DOH, false) var doh by preference(Key.DOH, false)
var magiskHide by preference(Key.MAGISKHIDE, true) var magiskHide by preference(Key.MAGISKHIDE, true)

View File

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.MainDirections
@ -58,9 +59,11 @@ class DieEvent : ViewEvent(), ActivityExecutor {
} }
} }
class ShowUIEvent : ViewEvent(), ActivityExecutor { class ShowUIEvent(private val delegate: View.AccessibilityDelegate?)
: ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseUIActivity<*, *>) { override fun invoke(activity: BaseUIActivity<*, *>) {
activity.setContentView() activity.setContentView()
activity.setAccessibilityDelegate(delegate)
} }
} }

View File

@ -195,6 +195,13 @@ object SystemlessHosts : BaseSettingsItem.Blank() {
override val description = R.string.settings_hosts_summary.asTransitive() override val description = R.string.settings_hosts_summary.asTransitive()
} }
object Tapjack : BaseSettingsItem.Toggle() {
override val title = R.string.settings_su_tapjack_title.asTransitive()
override var description = R.string.settings_su_tapjack_summary.asTransitive()
override var value = Config.suTapjack
set(value) = setV(value, field, { field = it }) { Config.suTapjack = it }
}
object Biometrics : BaseSettingsItem.Toggle() { object Biometrics : BaseSettingsItem.Toggle() {
override val title = R.string.settings_su_biometric_title.asTransitive() override val title = R.string.settings_su_biometric_title.asTransitive()
override var value = Config.suBiometric override var value = Config.suBiometric

View File

@ -79,7 +79,7 @@ class SettingsViewModel(
if (Utils.showSuperUser()) { if (Utils.showSuperUser()) {
list.addAll(listOf( list.addAll(listOf(
Superuser, Superuser,
Biometrics, AccessMode, MultiuserMode, MountNamespaceMode, Tapjack, Biometrics, AccessMode, MultiuserMode, MountNamespaceMode,
AutomaticResponse, RequestTimeout, SUNotification AutomaticResponse, RequestTimeout, SUNotification
)) ))
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 23) {

View File

@ -28,8 +28,8 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE) supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
lockOrientation() lockOrientation()
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
WindowManager.LayoutParams.FLAG_SECURE) window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
fun showRequest() { fun showRequest() {

View File

@ -1,11 +1,20 @@
package com.topjohnwu.magisk.ui.surequest package com.topjohnwu.magisk.ui.surequest
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeProvider
import android.widget.Toast
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
@ -22,6 +31,7 @@ import com.topjohnwu.magisk.events.DieEvent
import com.topjohnwu.magisk.events.ShowUIEvent import com.topjohnwu.magisk.events.ShowUIEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.ui.superuser.SpinnerRvItem import com.topjohnwu.magisk.ui.superuser.SpinnerRvItem
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
@ -51,6 +61,19 @@ class SuRequestViewModel(
var grantEnabled = false var grantEnabled = false
set(value) = set(value, field, { field = it }, BR.grantEnabled) set(value) = set(value, field, { field = it }, BR.grantEnabled)
@SuppressLint("ClickableViewAccessibility")
val grantTouchListener = View.OnTouchListener { _: View, event: MotionEvent ->
// Filter obscured touches by consuming them.
if (event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0
|| event.flags and MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED != 0) {
if (event.action == MotionEvent.ACTION_UP) {
Utils.toast(R.string.touch_filtered_warning, Toast.LENGTH_SHORT)
}
return@OnTouchListener Config.suTapjack
}
false
}
private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) } private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) }
val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply { val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply {
itemBinding = ItemBinding.of { binding, _, item -> itemBinding = ItemBinding.of { binding, _, item ->
@ -104,7 +127,7 @@ class SuRequestViewModel(
timer = SuTimer(millis, 1000).apply { start() } timer = SuTimer(millis, 1000).apply { start() }
// Actually show the UI // Actually show the UI
ShowUIEvent().publish() ShowUIEvent(if (Config.suTapjack) EmptyAccessibilityDelegate else null).publish()
} }
private fun respond(action: Int) { private fun respond(action: Int) {
@ -141,4 +164,18 @@ class SuRequestViewModel(
} }
} }
// Invisible for accessibility services
object EmptyAccessibilityDelegate : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {}
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?) = true
override fun sendAccessibilityEventUnchecked(host: View?, event: AccessibilityEvent?) {}
override fun dispatchPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) = true
override fun onPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
override fun onInitializeAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {}
override fun addExtraDataToAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo, extraDataKey: String, arguments: Bundle?) {}
override fun onRequestSendAccessibilityEvent(host: ViewGroup?, child: View?, event: AccessibilityEvent?): Boolean = false
override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProvider? = null
}
} }

View File

@ -138,6 +138,7 @@
<Button <Button
android:id="@+id/grant_btn" android:id="@+id/grant_btn"
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
onTouch="@{viewModel.grantTouchListener}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"

View File

@ -56,6 +56,7 @@
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser Request</string> <string name="su_request_title">Superuser Request</string>
<string name="touch_filtered_warning">Because an app is obscuring a superuser request, Magisk cant verify your response</string>
<string name="deny">Deny</string> <string name="deny">Deny</string>
<string name="prompt">Prompt</string> <string name="prompt">Prompt</string>
<string name="grant">Grant</string> <string name="grant">Grant</string>
@ -172,6 +173,8 @@
<string name="superuser_notification">Superuser Notification</string> <string name="superuser_notification">Superuser Notification</string>
<string name="settings_su_reauth_title">Reauthenticate after upgrade</string> <string name="settings_su_reauth_title">Reauthenticate after upgrade</string>
<string name="settings_su_reauth_summary">Reauthenticate superuser permissions after an application upgrades</string> <string name="settings_su_reauth_summary">Reauthenticate superuser permissions after an application upgrades</string>
<string name="settings_su_tapjack_title">Enable Tapjacking Protection</string>
<string name="settings_su_tapjack_summary">The superuser prompt dialog will not respond to input while obscured by any other window or overlay</string>
<string name="settings_su_biometric_title">Enable Biometric Authentication</string> <string name="settings_su_biometric_title">Enable Biometric Authentication</string>
<string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string> <string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string>
<string name="no_biometric">Unsupported device or no biometric settings are enabled</string> <string name="no_biometric">Unsupported device or no biometric settings are enabled</string>