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