BasePermission.java revision 700feef8a60e06784d28d1db9502e650df854cad
1/* 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.content.pm.Signature; 36import 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; 52import java.util.Collection; 53import 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 82 final @PermissionType int type; 83 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 } 133 public Signature[] getSourceSignatures() { 134 return sourcePackageSetting.getSignatures(); 135 } 136 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 } 228 public boolean isVendorPrivileged() { 229 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0; 230 } 231 public boolean isSystemTextClassifier() { 232 return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) 233 != 0; 234 } 235 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 267 public void updateDynamicPermission(Collection<BasePermission> permissionTrees) { 268 if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" 269 + getName() + " pkg=" + getSourcePackageName() 270 + " info=" + pendingPermissionInfo); 271 if (sourcePackageSetting == null && pendingPermissionInfo != null) { 272 final BasePermission tree = findPermissionTree(permissionTrees, name); 273 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 284 static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p, 285 @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees, 286 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 291 && bp.perm.owner.isSystem()); 292 if (p.owner.isSystem()) { 293 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)) { 315 final BasePermission tree = findPermissionTree(permissionTrees, p.info.name); 316 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 360 static BasePermission enforcePermissionTree( 361 Collection<BasePermission> permissionTrees, String permName, int callingUid) { 362 if (permName != null) { 363 BasePermission bp = findPermissionTree(permissionTrees, permName); 364 if (bp != null) { 365 if (bp.uid == UserHandle.getAppId(callingUid)) { 366 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 388 private static BasePermission findPermissionTree( 389 Collection<BasePermission> permissionTrees, String permName) { 390 for (BasePermission bp : permissionTrees) { 391 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} 577