BasePermission.java revision c842393bc55509b094d3a71f164fcdbadf5c7997
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.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;
51import java.util.Collection;
52import 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
81    final @PermissionType int type;
82
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
256    public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
257        if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
258                + getName() + " pkg=" + getSourcePackageName()
259                + " info=" + pendingPermissionInfo);
260        if (sourcePackageSetting == null && pendingPermissionInfo != null) {
261            final BasePermission tree = findPermissionTree(permissionTrees, name);
262            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
273    static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
274            @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
275            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)) {
304                final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
305                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
349    static BasePermission enforcePermissionTree(
350            Collection<BasePermission> permissionTrees, String permName, int callingUid) {
351        if (permName != null) {
352            BasePermission bp = findPermissionTree(permissionTrees, permName);
353            if (bp != null) {
354                if (bp.uid == UserHandle.getAppId(callingUid)) {
355                    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
377    private static BasePermission findPermissionTree(
378            Collection<BasePermission> permissionTrees, String permName) {
379        for (BasePermission bp : permissionTrees) {
380            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}
566