blob: bcf4b07d4d26b19f190a6ba7511627c3457daa1f [file] [log] [blame]
Todd Kennedy91a39d12017-09-27 12:37:04 -07001/*
2 * Copyright (C) 2006 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
17package com.android.server.pm.permission;
18
19import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
20import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
21import static android.content.pm.PermissionInfo.PROTECTION_NORMAL;
22import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
23import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
24
25import static com.android.server.pm.Settings.ATTR_NAME;
26import static com.android.server.pm.Settings.ATTR_PACKAGE;
27import static com.android.server.pm.Settings.TAG_ITEM;
28
29import android.annotation.IntDef;
30import android.annotation.NonNull;
31import android.annotation.Nullable;
32import android.content.pm.PackageParser;
33import android.content.pm.PackageParser.Permission;
34import android.content.pm.PermissionInfo;
Todd Kennedyc29b11a2017-10-23 15:55:59 -070035import android.content.pm.Signature;
Todd Kennedy91a39d12017-09-27 12:37:04 -070036import android.os.UserHandle;
37import android.util.Log;
38import android.util.Slog;
39
40import com.android.server.pm.DumpState;
41import com.android.server.pm.PackageManagerService;
42import com.android.server.pm.PackageSettingBase;
43
44import org.xmlpull.v1.XmlPullParser;
45import org.xmlpull.v1.XmlSerializer;
46
47import java.io.IOException;
48import java.io.PrintWriter;
49import java.lang.annotation.Retention;
50import java.lang.annotation.RetentionPolicy;
51import java.util.Arrays;
Todd Kennedyc8423932017-10-05 08:58:36 -070052import java.util.Collection;
Todd Kennedy91a39d12017-09-27 12:37:04 -070053import java.util.Map;
54import java.util.Objects;
55import java.util.Set;
56
57public final class BasePermission {
58 static final String TAG = "PackageManager";
59
60 public static final int TYPE_NORMAL = 0;
61 public static final int TYPE_BUILTIN = 1;
62 public static final int TYPE_DYNAMIC = 2;
63 @IntDef(value = {
64 TYPE_NORMAL,
65 TYPE_BUILTIN,
66 TYPE_DYNAMIC,
67 })
68 @Retention(RetentionPolicy.SOURCE)
69 public @interface PermissionType {}
70
71 @IntDef(value = {
72 PROTECTION_DANGEROUS,
73 PROTECTION_NORMAL,
74 PROTECTION_SIGNATURE,
75 PROTECTION_SIGNATURE_OR_SYSTEM,
76 })
77 @Retention(RetentionPolicy.SOURCE)
78 public @interface ProtectionLevel {}
79
80 final String name;
81
Todd Kennedyc8423932017-10-05 08:58:36 -070082 final @PermissionType int type;
Todd Kennedy91a39d12017-09-27 12:37:04 -070083
84 String sourcePackageName;
85
86 // TODO: Can we get rid of this? Seems we only use some signature info from the setting
87 PackageSettingBase sourcePackageSetting;
88
89 int protectionLevel;
90
91 PackageParser.Permission perm;
92
93 PermissionInfo pendingPermissionInfo;
94
95 /** UID that owns the definition of this permission */
96 int uid;
97
98 /** Additional GIDs given to apps granted this permission */
99 private int[] gids;
100
101 /**
102 * Flag indicating that {@link #gids} should be adjusted based on the
103 * {@link UserHandle} the granted app is running as.
104 */
105 private boolean perUser;
106
107 public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
108 name = _name;
109 sourcePackageName = _sourcePackageName;
110 type = _type;
111 // Default to most conservative protection level.
112 protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
113 }
114
115 @Override
116 public String toString() {
117 return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
118 + "}";
119 }
120
121 public String getName() {
122 return name;
123 }
124 public int getProtectionLevel() {
125 return protectionLevel;
126 }
127 public String getSourcePackageName() {
128 return sourcePackageName;
129 }
130 public PackageSettingBase getSourcePackageSetting() {
131 return sourcePackageSetting;
132 }
Todd Kennedyc29b11a2017-10-23 15:55:59 -0700133 public Signature[] getSourceSignatures() {
134 return sourcePackageSetting.getSignatures();
135 }
Todd Kennedy91a39d12017-09-27 12:37:04 -0700136 public int getType() {
137 return type;
138 }
139 public int getUid() {
140 return uid;
141 }
142 public void setGids(int[] gids, boolean perUser) {
143 this.gids = gids;
144 this.perUser = perUser;
145 }
146 public void setPermission(@Nullable Permission perm) {
147 this.perm = perm;
148 }
149 public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) {
150 this.sourcePackageSetting = sourcePackageSetting;
151 }
152
153 public int[] computeGids(int userId) {
154 if (perUser) {
155 final int[] userGids = new int[gids.length];
156 for (int i = 0; i < gids.length; i++) {
157 userGids[i] = UserHandle.getUid(userId, gids[i]);
158 }
159 return userGids;
160 } else {
161 return gids;
162 }
163 }
164
165 public int calculateFootprint(BasePermission perm) {
166 if (uid == perm.uid) {
167 return perm.name.length() + perm.perm.info.calculateFootprint();
168 }
169 return 0;
170 }
171
172 public boolean isPermission(Permission perm) {
173 return this.perm == perm;
174 }
175
176 public boolean isDynamic() {
177 return type == TYPE_DYNAMIC;
178 }
179
180
181 public boolean isNormal() {
182 return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
183 == PermissionInfo.PROTECTION_NORMAL;
184 }
185 public boolean isRuntime() {
186 return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
187 == PermissionInfo.PROTECTION_DANGEROUS;
188 }
189 public boolean isSignature() {
190 return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
191 PermissionInfo.PROTECTION_SIGNATURE;
192 }
193
194 public boolean isAppOp() {
195 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
196 }
197 public boolean isDevelopment() {
198 return isSignature()
199 && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
200 }
201 public boolean isInstaller() {
202 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
203 }
204 public boolean isInstant() {
205 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
206 }
207 public boolean isOEM() {
208 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
209 }
210 public boolean isPre23() {
211 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
212 }
213 public boolean isPreInstalled() {
214 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
215 }
216 public boolean isPrivileged() {
217 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
218 }
219 public boolean isRuntimeOnly() {
220 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
221 }
222 public boolean isSetup() {
223 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
224 }
225 public boolean isVerifier() {
226 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
227 }
Jiyong Park002fdbd2017-02-13 20:50:31 +0900228 public boolean isVendorPrivileged() {
229 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
230 }
Makoto Onuki700feef2018-02-15 10:59:41 -0800231 public boolean isSystemTextClassifier() {
232 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
233 != 0;
234 }
Todd Kennedy91a39d12017-09-27 12:37:04 -0700235
236 public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
237 if (!origPackageName.equals(sourcePackageName)) {
238 return;
239 }
240 sourcePackageName = newPackageName;
241 sourcePackageSetting = null;
242 perm = null;
243 if (pendingPermissionInfo != null) {
244 pendingPermissionInfo.packageName = newPackageName;
245 }
246 uid = 0;
247 setGids(null, false);
248 }
249
250 public boolean addToTree(@ProtectionLevel int protectionLevel,
251 @NonNull PermissionInfo info, @NonNull BasePermission tree) {
252 final boolean changed =
253 (this.protectionLevel != protectionLevel
254 || perm == null
255 || uid != tree.uid
256 || !perm.owner.equals(tree.perm.owner)
257 || !comparePermissionInfos(perm.info, info));
258 this.protectionLevel = protectionLevel;
259 info = new PermissionInfo(info);
260 info.protectionLevel = protectionLevel;
261 perm = new PackageParser.Permission(tree.perm.owner, info);
262 perm.info.packageName = tree.perm.info.packageName;
263 uid = tree.uid;
264 return changed;
265 }
266
Todd Kennedyc8423932017-10-05 08:58:36 -0700267 public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
Todd Kennedy91a39d12017-09-27 12:37:04 -0700268 if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
269 + getName() + " pkg=" + getSourcePackageName()
270 + " info=" + pendingPermissionInfo);
271 if (sourcePackageSetting == null && pendingPermissionInfo != null) {
Todd Kennedyc8423932017-10-05 08:58:36 -0700272 final BasePermission tree = findPermissionTree(permissionTrees, name);
Todd Kennedy91a39d12017-09-27 12:37:04 -0700273 if (tree != null && tree.perm != null) {
274 sourcePackageSetting = tree.sourcePackageSetting;
275 perm = new PackageParser.Permission(tree.perm.owner,
276 new PermissionInfo(pendingPermissionInfo));
277 perm.info.packageName = tree.perm.info.packageName;
278 perm.info.name = name;
279 uid = tree.uid;
280 }
281 }
282 }
283
Todd Kennedyc8423932017-10-05 08:58:36 -0700284 static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
285 @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
Todd Kennedy91a39d12017-09-27 12:37:04 -0700286 boolean chatty) {
287 final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
288 // Allow system apps to redefine non-system permissions
289 if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) {
290 final boolean currentOwnerIsSystem = (bp.perm != null
Todd Kennedyc29b11a2017-10-23 15:55:59 -0700291 && bp.perm.owner.isSystem());
292 if (p.owner.isSystem()) {
Todd Kennedy91a39d12017-09-27 12:37:04 -0700293 if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
294 // It's a built-in permission and no owner, take ownership now
295 bp.sourcePackageSetting = pkgSetting;
296 bp.perm = p;
297 bp.uid = pkg.applicationInfo.uid;
298 bp.sourcePackageName = p.info.packageName;
299 p.info.flags |= PermissionInfo.FLAG_INSTALLED;
300 } else if (!currentOwnerIsSystem) {
301 String msg = "New decl " + p.owner + " of permission "
302 + p.info.name + " is system; overriding " + bp.sourcePackageName;
303 PackageManagerService.reportSettingsProblem(Log.WARN, msg);
304 bp = null;
305 }
306 }
307 }
308 if (bp == null) {
309 bp = new BasePermission(p.info.name, p.info.packageName, TYPE_NORMAL);
310 }
311 StringBuilder r = null;
312 if (bp.perm == null) {
313 if (bp.sourcePackageName == null
314 || bp.sourcePackageName.equals(p.info.packageName)) {
Todd Kennedyc8423932017-10-05 08:58:36 -0700315 final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
Todd Kennedy91a39d12017-09-27 12:37:04 -0700316 if (tree == null
317 || tree.sourcePackageName.equals(p.info.packageName)) {
318 bp.sourcePackageSetting = pkgSetting;
319 bp.perm = p;
320 bp.uid = pkg.applicationInfo.uid;
321 bp.sourcePackageName = p.info.packageName;
322 p.info.flags |= PermissionInfo.FLAG_INSTALLED;
323 if (chatty) {
324 if (r == null) {
325 r = new StringBuilder(256);
326 } else {
327 r.append(' ');
328 }
329 r.append(p.info.name);
330 }
331 } else {
332 Slog.w(TAG, "Permission " + p.info.name + " from package "
333 + p.info.packageName + " ignored: base tree "
334 + tree.name + " is from package "
335 + tree.sourcePackageName);
336 }
337 } else {
338 Slog.w(TAG, "Permission " + p.info.name + " from package "
339 + p.info.packageName + " ignored: original from "
340 + bp.sourcePackageName);
341 }
342 } else if (chatty) {
343 if (r == null) {
344 r = new StringBuilder(256);
345 } else {
346 r.append(' ');
347 }
348 r.append("DUP:");
349 r.append(p.info.name);
350 }
351 if (bp.perm == p) {
352 bp.protectionLevel = p.info.protectionLevel;
353 }
354 if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
355 Log.d(TAG, " Permissions: " + r);
356 }
357 return bp;
358 }
359
Todd Kennedyc8423932017-10-05 08:58:36 -0700360 static BasePermission enforcePermissionTree(
361 Collection<BasePermission> permissionTrees, String permName, int callingUid) {
Todd Kennedy91a39d12017-09-27 12:37:04 -0700362 if (permName != null) {
Todd Kennedyc8423932017-10-05 08:58:36 -0700363 BasePermission bp = findPermissionTree(permissionTrees, permName);
Todd Kennedy91a39d12017-09-27 12:37:04 -0700364 if (bp != null) {
Todd Kennedyc8423932017-10-05 08:58:36 -0700365 if (bp.uid == UserHandle.getAppId(callingUid)) {
Todd Kennedy91a39d12017-09-27 12:37:04 -0700366 return bp;
367 }
368 throw new SecurityException("Calling uid " + callingUid
369 + " is not allowed to add to permission tree "
370 + bp.name + " owned by uid " + bp.uid);
371 }
372 }
373 throw new SecurityException("No permission tree found for " + permName);
374 }
375
376 public void enforceDeclaredUsedAndRuntimeOrDevelopment(PackageParser.Package pkg) {
377 int index = pkg.requestedPermissions.indexOf(name);
378 if (index == -1) {
379 throw new SecurityException("Package " + pkg.packageName
380 + " has not requested permission " + name);
381 }
382 if (!isRuntime() && !isDevelopment()) {
383 throw new SecurityException("Permission " + name
384 + " is not a changeable permission type");
385 }
386 }
387
Todd Kennedyc8423932017-10-05 08:58:36 -0700388 private static BasePermission findPermissionTree(
389 Collection<BasePermission> permissionTrees, String permName) {
390 for (BasePermission bp : permissionTrees) {
Todd Kennedy91a39d12017-09-27 12:37:04 -0700391 if (permName.startsWith(bp.name) &&
392 permName.length() > bp.name.length() &&
393 permName.charAt(bp.name.length()) == '.') {
394 return bp;
395 }
396 }
397 return null;
398 }
399
400 public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
401 if (groupName == null) {
402 if (perm == null || perm.info.group == null) {
403 return generatePermissionInfo(protectionLevel, flags);
404 }
405 } else {
406 if (perm != null && groupName.equals(perm.info.group)) {
407 return PackageParser.generatePermissionInfo(perm, flags);
408 }
409 }
410 return null;
411 }
412
413 public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) {
414 final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
415 // if we return different protection level, don't use the cached info
416 if (perm != null && !protectionLevelChanged) {
417 return PackageParser.generatePermissionInfo(perm, flags);
418 }
419 final PermissionInfo pi = new PermissionInfo();
420 pi.name = name;
421 pi.packageName = sourcePackageName;
422 pi.nonLocalizedLabel = name;
423 pi.protectionLevel = protectionLevelChanged ? adjustedProtectionLevel : protectionLevel;
424 return pi;
425 }
426
427 public static boolean readLPw(@NonNull Map<String, BasePermission> out,
428 @NonNull XmlPullParser parser) {
429 final String tagName = parser.getName();
430 if (!tagName.equals(TAG_ITEM)) {
431 return false;
432 }
433 final String name = parser.getAttributeValue(null, ATTR_NAME);
434 final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE);
435 final String ptype = parser.getAttributeValue(null, "type");
436 if (name == null || sourcePackage == null) {
437 PackageManagerService.reportSettingsProblem(Log.WARN,
438 "Error in package manager settings: permissions has" + " no name at "
439 + parser.getPositionDescription());
440 return false;
441 }
442 final boolean dynamic = "dynamic".equals(ptype);
443 BasePermission bp = out.get(name);
444 // If the permission is builtin, do not clobber it.
445 if (bp == null || bp.type != TYPE_BUILTIN) {
446 bp = new BasePermission(name.intern(), sourcePackage,
447 dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
448 }
449 bp.protectionLevel = readInt(parser, null, "protection",
450 PermissionInfo.PROTECTION_NORMAL);
451 bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
452 if (dynamic) {
453 final PermissionInfo pi = new PermissionInfo();
454 pi.packageName = sourcePackage.intern();
455 pi.name = name.intern();
456 pi.icon = readInt(parser, null, "icon", 0);
457 pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
458 pi.protectionLevel = bp.protectionLevel;
459 bp.pendingPermissionInfo = pi;
460 }
461 out.put(bp.name, bp);
462 return true;
463 }
464
465 private static int readInt(XmlPullParser parser, String ns, String name, int defValue) {
466 String v = parser.getAttributeValue(ns, name);
467 try {
468 if (v == null) {
469 return defValue;
470 }
471 return Integer.parseInt(v);
472 } catch (NumberFormatException e) {
473 PackageManagerService.reportSettingsProblem(Log.WARN,
474 "Error in package manager settings: attribute " + name
475 + " has bad integer value " + v + " at "
476 + parser.getPositionDescription());
477 }
478 return defValue;
479 }
480
481 public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
482 if (sourcePackageName == null) {
483 return;
484 }
485 serializer.startTag(null, TAG_ITEM);
486 serializer.attribute(null, ATTR_NAME, name);
487 serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
488 if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
489 serializer.attribute(null, "protection", Integer.toString(protectionLevel));
490 }
491 if (type == BasePermission.TYPE_DYNAMIC) {
492 final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
493 if (pi != null) {
494 serializer.attribute(null, "type", "dynamic");
495 if (pi.icon != 0) {
496 serializer.attribute(null, "icon", Integer.toString(pi.icon));
497 }
498 if (pi.nonLocalizedLabel != null) {
499 serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
500 }
501 }
502 }
503 serializer.endTag(null, TAG_ITEM);
504 }
505
506 private static boolean compareStrings(CharSequence s1, CharSequence s2) {
507 if (s1 == null) {
508 return s2 == null;
509 }
510 if (s2 == null) {
511 return false;
512 }
513 if (s1.getClass() != s2.getClass()) {
514 return false;
515 }
516 return s1.equals(s2);
517 }
518
519 private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
520 if (pi1.icon != pi2.icon) return false;
521 if (pi1.logo != pi2.logo) return false;
522 if (pi1.protectionLevel != pi2.protectionLevel) return false;
523 if (!compareStrings(pi1.name, pi2.name)) return false;
524 if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
525 // We'll take care of setting this one.
526 if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
527 // These are not currently stored in settings.
528 //if (!compareStrings(pi1.group, pi2.group)) return false;
529 //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
530 //if (pi1.labelRes != pi2.labelRes) return false;
531 //if (pi1.descriptionRes != pi2.descriptionRes) return false;
532 return true;
533 }
534
535 public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
536 @NonNull Set<String> permissionNames, boolean readEnforced,
537 boolean printedSomething, @NonNull DumpState dumpState) {
538 if (packageName != null && !packageName.equals(sourcePackageName)) {
539 return false;
540 }
541 if (permissionNames != null && !permissionNames.contains(name)) {
542 return false;
543 }
544 if (!printedSomething) {
545 if (dumpState.onTitlePrinted())
546 pw.println();
547 pw.println("Permissions:");
548 printedSomething = true;
549 }
550 pw.print(" Permission ["); pw.print(name); pw.print("] (");
551 pw.print(Integer.toHexString(System.identityHashCode(this)));
552 pw.println("):");
553 pw.print(" sourcePackage="); pw.println(sourcePackageName);
554 pw.print(" uid="); pw.print(uid);
555 pw.print(" gids="); pw.print(Arrays.toString(
556 computeGids(UserHandle.USER_SYSTEM)));
557 pw.print(" type="); pw.print(type);
558 pw.print(" prot=");
559 pw.println(PermissionInfo.protectionToString(protectionLevel));
560 if (perm != null) {
561 pw.print(" perm="); pw.println(perm);
562 if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
563 || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
564 pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.info.flags));
565 }
566 }
567 if (sourcePackageSetting != null) {
568 pw.print(" packageSetting="); pw.println(sourcePackageSetting);
569 }
570 if (READ_EXTERNAL_STORAGE.equals(name)) {
571 pw.print(" enforced=");
572 pw.println(readEnforced);
573 }
574 return true;
575 }
576}