From f1f2c847560e7ebb741909bd398c7d220d5078dc Mon Sep 17 00:00:00 2001 From: lucychang Date: Thu, 22 Apr 2021 13:54:43 +0800 Subject: [PATCH] Adds install source checks for accessibility service Adds install source checks for accessibility services. An accessibility category service must be installed from the given allow list or not from the default installer if no allow list provided. Bug: 182959209 Test: atest AccessibilitySecurityPolicyTest Change-Id: I023c15b9ebd87785e2f7eb1e218bec4ad006e52f --- core/res/res/values/config.xml | 3 + core/res/res/values/symbols.xml | 2 + .../AccessibilitySecurityPolicy.java | 65 +++++++++- .../AccessibilitySecurityPolicyTest.java | 116 ++++++++++++++---- 4 files changed, 156 insertions(+), 30 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fbc96783597d1..d3ea52eb8888b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4892,4 +4892,7 @@ false false + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a8d29d45ec0c4..b9f1e203552a4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4357,6 +4357,8 @@ + + diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index fd355d8da341b..dc2628f0bc0bf 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER; + import android.Manifest; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; @@ -24,7 +26,9 @@ import android.app.AppOpsManager; import android.appwidget.AppWidgetManagerInternal; import android.content.ComponentName; import android.content.Context; +import android.content.pm.InstallSourceInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; @@ -33,11 +37,13 @@ import android.os.IBinder; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.view.accessibility.AccessibilityEvent; import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import libcore.util.EmptyArray; @@ -666,13 +672,66 @@ public class AccessibilitySecurityPolicy { /** * Identifies whether the accessibility service is true and designed for accessibility. An - * accessibility service is considered as accessibility category if - * {@link AccessibilityServiceInfo#isAccessibilityTool} is true. + * accessibility service is considered as accessibility category if meets all conditions below: + *
    + *
  • {@link AccessibilityServiceInfo#isAccessibilityTool} is true
  • + *
  • is installed from the trusted install source
  • + *
* * @param serviceInfo The accessibility service's serviceInfo. * @return Returns true if it is a true accessibility service. */ public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) { - return serviceInfo.isAccessibilityTool(); + if (!serviceInfo.isAccessibilityTool()) { + return false; + } + if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) { + return hasTrustedSystemInstallSource( + serviceInfo.getResolveInfo().serviceInfo.packageName); + } + return true; + } + + /** Returns true if the {@code installedPackage} is installed from the trusted install source. + */ + private boolean hasTrustedSystemInstallSource(String installedPackage) { + try { + InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo( + installedPackage); + if (installSourceInfo == null) { + return false; + } + final String installSourcePackageName = installSourceInfo.getInitiatingPackageName(); + if (installSourcePackageName == null || !mPackageManager.getPackageInfo( + installSourcePackageName, + 0).applicationInfo.isSystemApp()) { + return false; + } + return isTrustedInstallSource(installSourcePackageName); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage); + } + return false; + } + + /** Returns true if the {@code installerPackage} is a trusted install source. */ + private boolean isTrustedInstallSource(String installerPackage) { + final String[] allowedInstallingSources = mContext.getResources().getStringArray( + com.android.internal.R.array + .config_accessibility_allowed_install_source); + + if (allowedInstallingSources.length == 0) { + //Filters unwanted default installers if no allowed install sources. + String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService( + PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER, + mCurrentUserId)); + return !TextUtils.equals(defaultInstaller, installerPackage); + } + for (int i = 0; i < allowedInstallingSources.length; i++) { + if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) { + return true; + } + } + return false; } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java index 45f43e8b672fe..ee00cb24a991e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java @@ -43,9 +43,12 @@ import android.appwidget.AppWidgetManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.InstallSourceInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.SigningInfo; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -81,8 +84,10 @@ public class AccessibilitySecurityPolicyTest { private static final int APP_PID = 2000; private static final int SYSTEM_PID = 558; private static final int TEST_USER_ID = UserHandle.USER_SYSTEM; + private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility"; private static final ComponentName TEST_COMPONENT_NAME = new ComponentName( - "com.android.server.accessibility", "AccessibilitySecurityPolicyTest"); + TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest"); + private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package"; private static final int[] ALWAYS_DISPATCH_EVENTS = { AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, @@ -142,19 +147,40 @@ public class AccessibilitySecurityPolicyTest { @Mock private AccessibilityServiceInfo mMockA11yServiceInfo; @Mock + private ResolveInfo mMockResolveInfo; + @Mock + private ServiceInfo mMockServiceInfo; + @Mock + private ApplicationInfo mMockApplicationInfo; + @Mock + private ApplicationInfo mMockSourceApplicationInfo; + @Mock + private PackageInfo mMockSourcePackageInfo; + @Mock private PolicyWarningUIController mPolicyWarningUIController; @Before - public void setUp() { + public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); mContext.setMockPackageManager(mMockPackageManager); mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager); mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager); mContext.getOrCreateTestableResources().addOverride( R.dimen.accessibility_focus_highlight_stroke_width, 1); + mContext.getOrCreateTestableResources().addOverride(R.array + .config_accessibility_allowed_install_source, + new String[]{ALLOWED_INSTALL_PACKAGE_NAME}); + when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME); when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo); + when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn( + mMockSourcePackageInfo); + + mMockResolveInfo.serviceInfo = mMockServiceInfo; + mMockServiceInfo.applicationInfo = mMockApplicationInfo; + mMockServiceInfo.packageName = TEST_PACKAGE_NAME; + mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo; mA11ySecurityPolicy = new AccessibilitySecurityPolicy( mPolicyWarningUIController, mContext, mMockA11yUserManager); @@ -595,28 +621,7 @@ public class AccessibilitySecurityPolicyTest { } @Test - public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() { - final ArrayList boundServices = new ArrayList<>(); - boundServices.add(mMockA11yServiceConnection); - when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true); - - mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); - - verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any()); - } - - @Test - public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() { - onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction(); - - mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>()); - - verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(), - any()); - } - - @Test - public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() { + public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() { final ArrayList boundServices = new ArrayList<>(); boundServices.add(mMockA11yServiceConnection); when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false); @@ -628,8 +633,8 @@ public class AccessibilitySecurityPolicyTest { } @Test - public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() { - onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction(); + public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() { + onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction(); mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>()); @@ -637,9 +642,66 @@ public class AccessibilitySecurityPolicyTest { eq(TEST_COMPONENT_NAME)); } + @Test + public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() { + final ArrayList boundServices = new ArrayList<>(); + boundServices.add(mMockA11yServiceConnection); + when(mMockApplicationInfo.isSystemApp()).thenReturn(true); + when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); + + verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any()); + } + + @Test + public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() { + onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction(); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>()); + + verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(), + any()); + } + + @Test + public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction() + throws PackageManager.NameNotFoundException { + final ArrayList boundServices = new ArrayList<>(); + boundServices.add(mMockA11yServiceConnection); + when(mMockApplicationInfo.isSystemApp()).thenReturn(false); + final InstallSourceInfo installSourceInfo = new InstallSourceInfo( + ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null, + ALLOWED_INSTALL_PACKAGE_NAME); + when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn( + installSourceInfo); + when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true); + when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); + + verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any()); + } + + @Test + public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction() + throws PackageManager.NameNotFoundException { + final ArrayList boundServices = new ArrayList<>(); + boundServices.add(mMockA11yServiceConnection); + when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true); + final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null); + when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn( + installSourceInfo); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); + + verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID), + eq(TEST_COMPONENT_NAME)); + } + @Test public void onSwitchUser_differentUser_activateUIControllerAction() { - onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction(); + onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction(); mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());