1 /*
2  * Copyright (C) 2019 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 com.android.providers.media;
18 
19 import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
20 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
21 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
22 import static android.app.AppOpsManager.MODE_ALLOWED;
23 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
24 import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
25 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
26 
27 import android.app.AppGlobals;
28 import android.app.AppOpsManager;
29 import android.content.ContentProvider;
30 import android.content.Context;
31 import android.content.PermissionChecker;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager.NameNotFoundException;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.storage.StorageManager;
37 
38 import com.android.internal.util.ArrayUtils;
39 
40 import libcore.util.EmptyArray;
41 
42 public class LocalCallingIdentity {
43     public final int pid;
44     public final int uid;
45     public final String packageNameUnchecked;
46 
LocalCallingIdentity(int pid, int uid, String packageNameUnchecked)47     private LocalCallingIdentity(int pid, int uid, String packageNameUnchecked) {
48         this.pid = pid;
49         this.uid = uid;
50         this.packageNameUnchecked = packageNameUnchecked;
51     }
52 
fromBinder(ContentProvider provider)53     public static LocalCallingIdentity fromBinder(ContentProvider provider) {
54         String callingPackage = provider.getCallingPackageUnchecked();
55         if (callingPackage == null) {
56             callingPackage = AppGlobals.getInitialApplication().getOpPackageName();
57         }
58         return new LocalCallingIdentity(Binder.getCallingPid(), Binder.getCallingUid(),
59                 callingPackage);
60     }
61 
fromExternal(int uid, String packageName)62     public static LocalCallingIdentity fromExternal(int uid, String packageName) {
63         return new LocalCallingIdentity(-1, uid, packageName);
64     }
65 
fromSelf()66     public static LocalCallingIdentity fromSelf() {
67         final LocalCallingIdentity ident = new LocalCallingIdentity(
68                 android.os.Process.myPid(),
69                 android.os.Process.myUid(),
70                 AppGlobals.getInitialApplication().getOpPackageName());
71 
72         ident.packageName = ident.packageNameUnchecked;
73         ident.packageNameResolved = true;
74         ident.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
75         ident.targetSdkVersionResolved = true;
76         ident.hasPermission = ~(PERMISSION_IS_LEGACY | PERMISSION_IS_REDACTION_NEEDED);
77         ident.hasPermissionResolved = ~0;
78 
79         return ident;
80     }
81 
getContext()82     private Context getContext() {
83         return AppGlobals.getInitialApplication();
84     }
85 
86     private String packageName;
87     private boolean packageNameResolved;
88 
getPackageName()89     public String getPackageName() {
90         if (!packageNameResolved) {
91             packageName = getPackageNameInternal();
92             packageNameResolved = true;
93         }
94         return packageName;
95     }
96 
getPackageNameInternal()97     private String getPackageNameInternal() {
98         // Verify that package name is actually owned by UID
99         getContext().getSystemService(AppOpsManager.class)
100                 .checkPackage(uid, packageNameUnchecked);
101         return packageNameUnchecked;
102     }
103 
104     private String[] sharedPackageNames;
105     private boolean sharedPackageNamesResolved;
106 
getSharedPackageNames()107     public String[] getSharedPackageNames() {
108         if (!sharedPackageNamesResolved) {
109             sharedPackageNames = getSharedPackageNamesInternal();
110             sharedPackageNamesResolved = true;
111         }
112         return sharedPackageNames;
113     }
114 
getSharedPackageNamesInternal()115     private String[] getSharedPackageNamesInternal() {
116         return ArrayUtils.defeatNullable(getContext().getPackageManager().getPackagesForUid(uid));
117     }
118 
119     private int targetSdkVersion;
120     private boolean targetSdkVersionResolved;
121 
getTargetSdkVersion()122     public int getTargetSdkVersion() {
123         if (!targetSdkVersionResolved) {
124             targetSdkVersion = getTargetSdkVersionInternal();
125             targetSdkVersionResolved = true;
126         }
127         return targetSdkVersion;
128     }
129 
getTargetSdkVersionInternal()130     private int getTargetSdkVersionInternal() {
131         try {
132             final ApplicationInfo ai = getContext().getPackageManager()
133                     .getApplicationInfo(getPackageName(), 0);
134             if (ai != null) {
135                 return ai.targetSdkVersion;
136             }
137         } catch (NameNotFoundException ignored) {
138         }
139         return Build.VERSION_CODES.CUR_DEVELOPMENT;
140     }
141 
142     public static final int PERMISSION_IS_SYSTEM = 1 << 0;
143     public static final int PERMISSION_IS_LEGACY = 1 << 1;
144     public static final int PERMISSION_IS_REDACTION_NEEDED = 1 << 2;
145     public static final int PERMISSION_READ_AUDIO = 1 << 3;
146     public static final int PERMISSION_READ_VIDEO = 1 << 4;
147     public static final int PERMISSION_READ_IMAGES = 1 << 5;
148     public static final int PERMISSION_WRITE_AUDIO = 1 << 6;
149     public static final int PERMISSION_WRITE_VIDEO = 1 << 7;
150     public static final int PERMISSION_WRITE_IMAGES = 1 << 8;
151 
152     private int hasPermission;
153     private int hasPermissionResolved;
154 
hasPermission(int permission)155     public boolean hasPermission(int permission) {
156         if ((hasPermissionResolved & permission) == 0) {
157             if (hasPermissionInternal(permission)) {
158                 hasPermission |= permission;
159             }
160             hasPermissionResolved |= permission;
161         }
162         return (hasPermission & permission) != 0;
163     }
164 
hasPermissionInternal(int permission)165     private boolean hasPermissionInternal(int permission) {
166         switch (permission) {
167             case PERMISSION_IS_SYSTEM:
168                 return isSystemInternal();
169             case PERMISSION_IS_LEGACY:
170                 return isLegacyInternal();
171             case PERMISSION_IS_REDACTION_NEEDED:
172                 return isRedactionNeededInternal();
173             case PERMISSION_READ_AUDIO:
174                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
175                         .checkPermissionReadAudio(false, pid, uid, getPackageName());
176             case PERMISSION_READ_VIDEO:
177                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
178                         .checkPermissionReadVideo(false, pid, uid, getPackageName());
179             case PERMISSION_READ_IMAGES:
180                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
181                         .checkPermissionReadImages(false, pid, uid, getPackageName());
182             case PERMISSION_WRITE_AUDIO:
183                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
184                         .checkPermissionWriteAudio(false, pid, uid, getPackageName());
185             case PERMISSION_WRITE_VIDEO:
186                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
187                         .checkPermissionWriteVideo(false, pid, uid, getPackageName());
188             case PERMISSION_WRITE_IMAGES:
189                 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
190                         .checkPermissionWriteImages(false, pid, uid, getPackageName());
191             default:
192                 return false;
193         }
194     }
195 
isSystemInternal()196     private boolean isSystemInternal() {
197         if (uid == android.os.Process.SYSTEM_UID) {
198             return true;
199         }
200 
201         // Special case to speed up when MediaProvider is calling itself; we
202         // know it always has system permissions
203         if (uid == android.os.Process.myUid()) {
204             return true;
205         }
206 
207         // Determine if caller is holding runtime permission
208         final boolean hasStorage = StorageManager.checkPermissionAndAppOp(getContext(), false, 0,
209                 uid, getPackageName(), WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE);
210 
211         // We're only willing to give out broad access if they also hold
212         // runtime permission; this is a firm CDD requirement
213         final boolean hasFull = getContext()
214                 .checkPermission(WRITE_MEDIA_STORAGE, pid, uid) == PERMISSION_GRANTED;
215 
216         return hasFull && hasStorage;
217     }
218 
isLegacyInternal()219     private boolean isLegacyInternal() {
220         // TODO: keep this logic in sync with StorageManagerService
221         final boolean hasStorage = StorageManager.checkPermissionAndAppOp(getContext(), false, 0,
222                 uid, getPackageName(), WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE);
223         final boolean hasLegacy = getContext().getSystemService(AppOpsManager.class)
224                 .checkOp(OP_LEGACY_STORAGE, uid, getPackageName()) == MODE_ALLOWED;
225         return (hasLegacy && hasStorage);
226     }
227 
isRedactionNeededInternal()228     private boolean isRedactionNeededInternal() {
229         // System internals or callers holding permission have no redaction
230         if (hasPermission(PERMISSION_IS_SYSTEM) || PermissionChecker.checkPermissionForDataDelivery(getContext(),
231                 ACCESS_MEDIA_LOCATION, pid, uid, getPackageName())
232                 == PermissionChecker.PERMISSION_GRANTED) {
233             return false;
234         }
235         return true;
236     }
237 
238     private long[] ownedIds = EmptyArray.LONG;
239 
isOwned(long id)240     public boolean isOwned(long id) {
241         return ArrayUtils.contains(ownedIds, id);
242     }
243 
setOwned(long id, boolean owned)244     public void setOwned(long id, boolean owned) {
245         if (owned) {
246             ownedIds = ArrayUtils.appendLong(ownedIds, id);
247         } else {
248             ownedIds = ArrayUtils.removeLong(ownedIds, id);
249         }
250     }
251 }
252