Commit 533cb8eb authored by vvb2060's avatar vvb2060 Committed by topjohnwu

Tapjacking protection

parent 8ac1181e
...@@ -70,6 +70,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : ...@@ -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> : ...@@ -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
......
...@@ -50,6 +50,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -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 { ...@@ -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)
......
...@@ -4,6 +4,7 @@ import android.app.Activity ...@@ -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 { ...@@ -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)
} }
} }
......
...@@ -195,6 +195,13 @@ object SystemlessHosts : BaseSettingsItem.Blank() { ...@@ -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
......
...@@ -79,7 +79,7 @@ class SettingsViewModel( ...@@ -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) {
......
...@@ -28,8 +28,8 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques ...@@ -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() {
......
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 ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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
}
} }
...@@ -138,6 +138,7 @@ ...@@ -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"
......
...@@ -56,6 +56,7 @@ ...@@ -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 can’t 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 @@ ...@@ -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>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment