From f6515cca9ae5bafa8dfe9c654b1e554d05f5ea68 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Mon, 29 Jul 2024 15:00:02 +0200 Subject: Check for NLS service intent filter when rebinding services Also, after updating packages with NLS components, check the approved services and remove from approved list if missing the NLS service intent filter. Flag: EXEMPT security fix Test: atest ManagedServicesTest Bug: 347674739 Change-Id: I6ba585d123fbe88cbff2011b62f02774dd1efda5 (cherry picked from commit 4bab9e28e4463c38c6aff80e9eced2b113786385) Merged-In: I6ba585d123fbe88cbff2011b62f02774dd1efda5 --- .../server/notification/ManagedServices.java | 12 +++- .../server/notification/ManagedServicesTest.java | 74 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index fd73e8c4b16f..7ee11f5182c0 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -879,7 +879,7 @@ abstract public class ManagedServices { || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } - return componentHasBindPermission(component, userId); + return isValidService(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { @@ -1229,11 +1229,12 @@ abstract public class ManagedServices { if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) { final ComponentName component = ComponentName.unflattenFromString( approvedPackageOrComponent); - if (component != null && !componentHasBindPermission(component, userId)) { + if (component != null && !isValidService(component, userId)) { approved.removeAt(j); if (DEBUG) { Slog.v(TAG, "Removing " + approvedPackageOrComponent - + " from approved list; no bind permission found " + + " from approved list; no bind permission or " + + "service interface filter found " + mConfig.bindPermission); } } @@ -1252,6 +1253,11 @@ abstract public class ManagedServices { } } + protected boolean isValidService(ComponentName component, int userId) { + return componentHasBindPermission(component, userId) && queryPackageForServices( + component.getPackageName(), userId).contains(component); + } + protected boolean isValidEntry(String packageOrComponent, int userId) { return hasMatchingServices(packageOrComponent, userId); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index f818725f0067..4ab6ccfb6446 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -1009,6 +1009,80 @@ public class ManagedServicesTest extends UiServiceTestCase { assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); } + @Test + public void testUpgradeAppNoIntentFilterNoRebind() throws Exception { + Context context = spy(getContext()); + doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any()); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, + mIpm, APPROVAL_BY_COMPONENT); + + List packages = new ArrayList<>(); + packages.add("package"); + addExpectedServices(service, packages, 0); + + final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1"); + final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2"); + + // Both components are approved initially + mExpectedPrimaryComponentNames.clear(); + mExpectedPrimaryPackages.clear(); + mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2"); + mExpectedSecondaryComponentNames.clear(); + mExpectedSecondaryPackages.clear(); + + loadXml(service); + + //Components keep bind permission + when(mIpm.getServiceInfo(any(), anyInt(), anyInt())).thenAnswer( + (Answer) invocation -> { + ComponentName invocationCn = invocation.getArgument(0); + if (invocationCn != null) { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = invocationCn.getPackageName(); + serviceInfo.name = invocationCn.getClassName(); + serviceInfo.permission = service.getConfig().bindPermission; + serviceInfo.metaData = null; + return serviceInfo; + } + return null; + } + ); + + //Component package/C1 loses serviceInterface intent filter + ManagedServices.Config config = service.getConfig(); + when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())). + thenAnswer(new Answer>() { + @Override + public List answer(InvocationOnMock invocationOnMock) + throws Throwable { + Object[] args = invocationOnMock.getArguments(); + Intent invocationIntent = (Intent) args[0]; + if (invocationIntent != null) { + if (invocationIntent.getAction().equals(config.serviceInterface) + && packages.contains(invocationIntent.getPackage())) { + List dummyServices = new ArrayList<>(); + ResolveInfo resolveInfo = new ResolveInfo(); + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = invocationIntent.getPackage(); + serviceInfo.name = approvedComponent.getClassName(); + serviceInfo.permission = service.getConfig().bindPermission; + resolveInfo.serviceInfo = serviceInfo; + dummyServices.add(resolveInfo); + return dummyServices; + } + } + return new ArrayList<>(); + } + }); + + // Trigger package update + service.onPackagesChanged(false, new String[]{"package"}, new int[]{0}); + + assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent)); + assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); + } + @Test public void testSetPackageOrComponentEnabled() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { -- cgit v1.2.3-59-g8ed1b