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