From 361c828f654b646f968644dbadf8a1f5f8ad67d8 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Thu, 1 Feb 2024 13:58:49 +0100 Subject: [PATCH] [BACKPORT] Verify URI permission for channel sound update from NotificationListenerService Check that a privileged NotificationListenerService (CDM) has the permission to access the sound URI when updating a notification channel. Test: atest com.android.server.notification.NotificationManagerServiceTest#testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission Bug: 317357401 (cherry picked from commit 9b7bbbf5ad542ecf9ecbf8cd819b468791b443c0) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f090c0538a27d8658d8a860046d5c5e931302341) Merged-In: Ic7d2e96e43565e98d2aa29b8f2ba35c142387ba9 Change-Id: Ic7d2e96e43565e98d2aa29b8f2ba35c142387ba9 --- .../NotificationManagerService.java | 22 +++++++ .../NotificationManagerServiceTest.java | 57 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a1e8cd15fd7ee..e793dc024156a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3668,6 +3668,10 @@ public void updateNotificationChannelFromPrivilegedListener(INotificationListene Preconditions.checkNotNull(user); verifyPrivilegedListener(token, user); + + final NotificationChannel originalChannel = mRankingHelper.getNotificationChannel( + pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true); + verifyPrivilegedListenerUriPermission(Binder.getCallingUid(), channel, originalChannel); updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true); } @@ -3709,6 +3713,24 @@ private void verifyPrivilegedListener(INotificationListener token, UserHandle us } } + private void verifyPrivilegedListenerUriPermission(int sourceUid, + @NonNull NotificationChannel updateChannel, + @Nullable NotificationChannel originalChannel) { + // Check that the NLS has the required permissions to access the channel + final Uri soundUri = updateChannel.getSound(); + final Uri originalSoundUri = + (originalChannel != null) ? originalChannel.getSound() : null; + if (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) { + Binder.withCleanCallingIdentity(() -> { + mAm.checkGrantUriPermission(sourceUid, null, + ContentProvider.getUriWithoutUserId(soundUri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(soundUri, + UserHandle.getUserId(sourceUid))); + }); + } + } + private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException { int uid = 0; long identity = Binder.clearCallingIdentity(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 379290bcf0ad0..db83d8f1a4f07 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -1681,6 +1681,63 @@ public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); } + @Test + public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission() + throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); + List associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) + .thenReturn(associations); + when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), + eq(mTestNotificationChannel.getId()), anyBoolean())) + .thenReturn(mTestNotificationChannel); + final Uri soundUri = Uri.parse("content://media/test/sound/uri"); + final NotificationChannel updatedNotificationChannel = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); + updatedNotificationChannel.setSound(soundUri, + updatedNotificationChannel.getAudioAttributes()); + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + assertThrows(SecurityException.class, + () -> mBinderService.updateNotificationChannelFromPrivilegedListener(null, PKG, + Process.myUserHandle(), updatedNotificationChannel)); + verify(mPreferencesHelper, never()).updateNotificationChannel( + anyString(), anyInt(), any(), anyBoolean()); + verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), + eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); + } + + @Test + public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound() + throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); + List associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) + .thenReturn(associations); + when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), + eq(mTestNotificationChannel.getId()), anyBoolean())) + .thenReturn(mTestNotificationChannel); + final Uri soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; + final NotificationChannel updatedNotificationChannel = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); + updatedNotificationChannel.setSound(soundUri, + updatedNotificationChannel.getAudioAttributes()); + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + mBinderService.updateNotificationChannelFromPrivilegedListener( + null, PKG, Process.myUserHandle(), updatedNotificationChannel); + verify(mPreferencesHelper, times(1)).updateNotificationChannel( + anyString(), anyInt(), any(), anyBoolean()); + verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), + eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); + } + @Test public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception { mService.setRankingHelper(mRankingHelper);