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