1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.permission; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityThread; 22 import android.content.Context; 23 import android.content.PermissionChecker; 24 import android.os.Binder; 25 import android.os.Process; 26 27 import java.util.Objects; 28 29 /** 30 * This module provides some utility methods for facilitating our permission enforcement patterns. 31 * <p> 32 * <h1>Intended usage:</h1> 33 * Close to the client-facing edge of the server, first authenticate the client, using {@link 34 * #establishIdentityDirect(Identity)}, or {@link #establishIdentityIndirect(Context, String, 35 * Identity, Identity)}, depending on whether the client is trying to authenticate as the 36 * originator or a middleman. Those methods will establish a scope with the originator in the 37 * {@link android.media.permission.IdentityContext} and a cleared binder calling identity. 38 * Typically there would be two distinct API methods for the two different options, and typically 39 * those API methods would be used to establish a client session which is associated with the 40 * originator for the lifetime of the session. 41 * <p> 42 * When performing an operation that requires permissions, use {@link 43 * #checkPermissionForPreflight(Context, Identity, String)} or {@link 44 * #checkPermissionForDataDelivery(Context, Identity, String, String)} on the originator 45 * identity. Note that this won't typically be the identity pulled from the {@link 46 * android.media.permission.IdentityContext}, since we are working with a session-based approach, 47 * the originator identity will be established once upon creation of a session, and then all 48 * interactions with this session will using the identity attached to the session. This also covers 49 * performing checks prior to invoking client callbacks for data delivery. 50 * 51 * @hide 52 */ 53 public class PermissionUtil { 54 55 /** 56 * Authenticate an originator, where the binder call is coming from a middleman. 57 * 58 * The middleman is expected to hold a special permission to act as such, or else a 59 * {@link SecurityException} will be thrown. If the call succeeds: 60 * <ul> 61 * <li>The passed middlemanIdentity argument will have its uid/pid fields overridden with 62 * those provided by binder. 63 * <li>An {@link SafeCloseable} is returned, used to established a scope in which the 64 * originator identity is available via {@link android.media.permission.IdentityContext} 65 * and in which the binder 66 * calling ID is cleared. 67 * </ul> 68 * Example usage: 69 * <pre> 70 * try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(...)) { 71 * // Within this scope we have the identity context established, and the binder calling 72 * // identity cleared. 73 * ... 74 * Identity originator = IdentityContext.getNonNull(); 75 * ... 76 * } 77 * // outside the scope, everything is back to the prior state. 78 * </pre> 79 * <p> 80 * <b>Important note:</b> The binder calling ID will be used to securely establish the identity 81 * of the middleman. However, if the middleman is on the same process as the server, 82 * the middleman must remember to clear the binder calling identity, or else the binder calling 83 * ID will reflect the process calling into the middleman, not the middleman process itself. If 84 * the middleman itself is using this API, this is typically not an issue, since this method 85 * will take care of that. 86 * 87 * @param context A {@link Context}, used for permission checks. 88 * @param middlemanPermission The permission that will be checked in order to authorize the 89 * middleman to act as such (i.e. be trusted to convey the 90 * originator 91 * identity reliably). 92 * @param middlemanIdentity The identity of the middleman. 93 * @param originatorIdentity The identity of the originator. 94 * @return A {@link SafeCloseable}, used to establish a scope, as mentioned above. 95 */ 96 public static @NonNull establishIdentityIndirect( @onNull Context context, @NonNull String middlemanPermission, @NonNull Identity middlemanIdentity, @NonNull Identity originatorIdentity)97 SafeCloseable establishIdentityIndirect( 98 @NonNull Context context, 99 @NonNull String middlemanPermission, 100 @NonNull Identity middlemanIdentity, 101 @NonNull Identity originatorIdentity) { 102 Objects.requireNonNull(context); 103 Objects.requireNonNull(middlemanPermission); 104 Objects.requireNonNull(middlemanIdentity); 105 Objects.requireNonNull(originatorIdentity); 106 107 // Override uid/pid with the secure values provided by binder. 108 middlemanIdentity.pid = Binder.getCallingPid(); 109 middlemanIdentity.uid = Binder.getCallingUid(); 110 111 // Authorize middleman to delegate identity. 112 context.enforcePermission(middlemanPermission, middlemanIdentity.pid, 113 middlemanIdentity.uid, 114 String.format("Middleman must have the %s permision.", middlemanPermission)); 115 return new CompositeSafeCloseable(IdentityContext.create(originatorIdentity), 116 ClearCallingIdentityContext.create()); 117 } 118 119 /** 120 * Authenticate an originator, where the binder call is coming directly from the originator. 121 * 122 * If the call succeeds: 123 * <ul> 124 * <li>The passed originatorIdentity argument will have its uid/pid fields overridden with 125 * those provided by binder. 126 * <li>A {@link SafeCloseable} is returned, used to established a scope in which the 127 * originator identity is available via {@link IdentityContext} and in which the binder 128 * calling ID is cleared. 129 * </ul> 130 * Example usage: 131 * <pre> 132 * try (AutoClosable ignored = PermissionUtil.establishIdentityDirect(...)) { 133 * // Within this scope we have the identity context established, and the binder calling 134 * // identity cleared. 135 * ... 136 * Identity originator = IdentityContext.getNonNull(); 137 * ... 138 * } 139 * // outside the scope, everything is back to the prior state. 140 * </pre> 141 * <p> 142 * <b>Important note:</b> The binder calling ID will be used to securely establish the identity 143 * of the client. However, if the client is on the same process as the server, and is itself a 144 * binder server, it must remember to clear the binder calling identity, or else the binder 145 * calling ID will reflect the process calling into the client, not the client process itself. 146 * If the client itself is using this API, this is typically not an issue, since this method 147 * will take care of that. 148 * 149 * @param originatorIdentity The identity of the originator. 150 * @return A {@link SafeCloseable}, used to establish a scope, as mentioned above. 151 */ 152 public static @NonNull establishIdentityDirect(@onNull Identity originatorIdentity)153 SafeCloseable establishIdentityDirect(@NonNull Identity originatorIdentity) { 154 Objects.requireNonNull(originatorIdentity); 155 156 originatorIdentity.uid = Binder.getCallingUid(); 157 originatorIdentity.pid = Binder.getCallingPid(); 158 return new CompositeSafeCloseable( 159 IdentityContext.create(originatorIdentity), 160 ClearCallingIdentityContext.create()); 161 } 162 163 /** 164 * Checks whether the given identity has the given permission to receive data. 165 * 166 * @param context A {@link Context}, used for permission checks. 167 * @param identity The identity to check. 168 * @param permission The identifier of the permission we want to check. 169 * @param reason The reason why we're requesting the permission, for auditing purposes. 170 * @return The permission check result which is either 171 * {@link PermissionChecker#PERMISSION_GRANTED} 172 * or {@link PermissionChecker#PERMISSION_SOFT_DENIED} or 173 * {@link PermissionChecker#PERMISSION_HARD_DENIED}. 174 */ checkPermissionForDataDelivery(@onNull Context context, @NonNull Identity identity, @NonNull String permission, @NonNull String reason)175 public static int checkPermissionForDataDelivery(@NonNull Context context, 176 @NonNull Identity identity, 177 @NonNull String permission, 178 @NonNull String reason) { 179 return PermissionChecker.checkPermissionForDataDelivery(context, permission, 180 identity.pid, identity.uid, identity.packageName, identity.attributionTag, 181 reason); 182 } 183 184 /** 185 * Checks whether the given identity has the given permission. 186 * 187 * @param context A {@link Context}, used for permission checks. 188 * @param identity The identity to check. 189 * @param permission The identifier of the permission we want to check. 190 * @return The permission check result which is either 191 * {@link PermissionChecker#PERMISSION_GRANTED} 192 * or {@link PermissionChecker#PERMISSION_SOFT_DENIED} or 193 * {@link PermissionChecker#PERMISSION_HARD_DENIED}. 194 */ checkPermissionForPreflight(@onNull Context context, @NonNull Identity identity, @NonNull String permission)195 public static int checkPermissionForPreflight(@NonNull Context context, 196 @NonNull Identity identity, 197 @NonNull String permission) { 198 return PermissionChecker.checkPermissionForPreflight(context, permission, 199 identity.pid, identity.uid, identity.packageName); 200 } 201 } 202