From c0476e87bde41311e2581f57ed8fbef3bd3a39b1 Mon Sep 17 00:00:00 2001 From: Naomi Musgrave Date: Wed, 22 Mar 2023 12:01:18 +0000 Subject: [PATCH] [MediaProjection] Secure aidl interfaces with permission checks Prevent any client from invoking critical methods on the aidl boundary, by requiring the MANAGE_MEDIA_PROJECTION permission. Method usages checked in codesearch. Bug: 275368789 Test: manual Change-Id: Ic638ed08df216195cdc192fd039467eed84150f3 --- .../media/projection/IMediaProjection.aidl | 10 ++++ .../projection/IMediaProjectionManager.aidl | 14 +++-- .../android/server/audio/AudioService.java | 3 ++ .../server/display/DisplayManagerService.java | 7 ++- .../MediaProjectionManagerService.java | 54 ++++++++++++++----- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 2bdd5c8bc977d..5f7d636fdd1eb 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -23,22 +23,32 @@ import android.os.IBinder; interface IMediaProjection { void start(IMediaProjectionCallback callback); void stop(); + boolean canProjectAudio(); boolean canProjectVideo(); boolean canProjectSecureVideo(); + + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") int applyVirtualDisplayFlags(int flags); + void registerCallback(IMediaProjectionCallback callback); + void unregisterCallback(IMediaProjectionCallback callback); /** * Returns the {@link android.os.IBinder} identifying the task to record, or {@code null} if * there is none. */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") IBinder getLaunchCookie(); /** * Updates the {@link android.os.IBinder} identifying the task to record, or {@code null} if * there is none. */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") void setLaunchCookie(in IBinder launchCookie); } diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index 97e3ec146b887..c97265d4939dd 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -28,28 +28,34 @@ interface IMediaProjectionManager { @UnsupportedAppUsage boolean hasProjectionPermission(int uid, String packageName); + /** + * Returns a new {@link IMediaProjection} instance associated with the given package. + */ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") IMediaProjection createProjection(int uid, String packageName, int type, boolean permanentGrant); + /** + * Returns {@code true} if the given {@link IMediaProjection} corresponds to the current + * projection, or {@code false} otherwise. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") boolean isCurrentProjection(IMediaProjection projection); @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") MediaProjectionInfo getActiveProjectionInfo(); - @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") void stopActiveProjection(); - @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") void notifyActiveProjectionCapturedContentResized(int width, int height); - @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible); @@ -70,6 +76,8 @@ interface IMediaProjectionManager { * @param incomingSession the nullable incoming content recording session * @param projection the non-null projection the session describes */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") void setContentRecordingSession(in ContentRecordingSession incomingSession, in IMediaProjection projection); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0f03996f7a355..16eec0dffba24 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -11599,6 +11599,7 @@ public class AudioService extends IAudioService.Stub return false; } + final long token = Binder.clearCallingIdentity(); try { if (!projectionService.isCurrentProjection(projection)) { Log.w(TAG, "App passed invalid MediaProjection token"); @@ -11608,6 +11609,8 @@ public class AudioService extends IAudioService.Stub Log.e(TAG, "Can't call .isCurrentProjection() on IMediaProjectionManager" + projectionService.asBinder(), e); return false; + } finally { + Binder.restoreCallingIdentity(token); } try { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0da4b53f6af4a..0c67ac43e5809 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1419,6 +1419,7 @@ public final class DisplayManagerService extends SystemService { } if (projection != null) { + final long firstToken = Binder.clearCallingIdentity(); try { if (!getProjectionService().isCurrentProjection(projection)) { throw new SecurityException("Cannot create VirtualDisplay with " @@ -1427,6 +1428,8 @@ public final class DisplayManagerService extends SystemService { flags = projection.applyVirtualDisplayFlags(flags); } catch (RemoteException e) { throw new SecurityException("unable to validate media projection or flags"); + } finally { + Binder.restoreCallingIdentity(firstToken); } } @@ -1494,7 +1497,7 @@ public final class DisplayManagerService extends SystemService { throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); } - final long token = Binder.clearCallingIdentity(); + final long secondToken = Binder.clearCallingIdentity(); try { final int displayId; synchronized (mSyncRoot) { @@ -1566,7 +1569,7 @@ public final class DisplayManagerService extends SystemService { return displayId; } finally { - Binder.restoreCallingIdentity(token); + Binder.restoreCallingIdentity(secondToken); } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index f6a4bf1a61452..48acc7c2ba8d3 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -16,6 +16,7 @@ package com.android.server.media.projection; +import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION; import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED; import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED; @@ -282,7 +283,7 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public IMediaProjection createProjection(int uid, String packageName, int type, boolean isPermanentGrant) { - if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) + if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant " + "projection permission"); @@ -314,16 +315,21 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public boolean isCurrentProjection(IMediaProjection projection) { + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to check " + + "if the given projection is current."); + } return MediaProjectionManagerService.this.isCurrentProjection( projection == null ? null : projection.asBinder()); } @Override // Binder call public MediaProjectionInfo getActiveProjectionInfo() { - if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) + if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " - + "projection callbacks"); + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to get " + + "active projection info"); } final long token = Binder.clearCallingIdentity(); try { @@ -333,10 +339,13 @@ public final class MediaProjectionManagerService extends SystemService } } - @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call public void stopActiveProjection() { - stopActiveProjection_enforcePermission(); + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to stop " + + "the active projection"); + } final long token = Binder.clearCallingIdentity(); try { if (mProjectionGrant != null) { @@ -347,10 +356,13 @@ public final class MediaProjectionManagerService extends SystemService } } - @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call public void notifyActiveProjectionCapturedContentResized(int width, int height) { - notifyActiveProjectionCapturedContentResized_enforcePermission(); + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify " + + "on captured content resize"); + } if (!isCurrentProjection(mProjectionGrant)) { return; } @@ -364,10 +376,13 @@ public final class MediaProjectionManagerService extends SystemService } } - @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override public void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible) { - notifyActiveProjectionCapturedContentVisibilityChanged_enforcePermission(); + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify " + + "on captured content visibility changed"); + } if (!isCurrentProjection(mProjectionGrant)) { return; } @@ -383,7 +398,7 @@ public final class MediaProjectionManagerService extends SystemService @Override //Binder call public void addCallback(final IMediaProjectionWatcherCallback callback) { - if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) + if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " + "projection callbacks"); @@ -398,7 +413,7 @@ public final class MediaProjectionManagerService extends SystemService @Override public void removeCallback(IMediaProjectionWatcherCallback callback) { - if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) + if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to remove " + "projection callbacks"); @@ -503,6 +518,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public int applyVirtualDisplayFlags(int flags) { + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to apply virtual " + + "display flags."); + } if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) { flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR @@ -651,11 +671,21 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public void setLaunchCookie(IBinder launchCookie) { + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to set launch " + + "cookie."); + } mLaunchCookie = launchCookie; } @Override // Binder call public IBinder getLaunchCookie() { + if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to get launch " + + "cookie."); + } return mLaunchCookie; }