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        PermissionInfo permissionInfo;
415        if (perm != null) {
416            final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
417            permissionInfo = PackageParser.generatePermissionInfo(perm, flags);
418            if (protectionLevelChanged && permissionInfo == perm.info) {
419                // if we return different protection level, don't use the cached info
420                permissionInfo = new PermissionInfo(permissionInfo);
421                permissionInfo.protectionLevel = adjustedProtectionLevel;
422            }
423            return permissionInfo;
424        }
425        permissionInfo = new PermissionInfo();
426        permissionInfo.name = name;
427        permissionInfo.packageName = sourcePackageName;
428        permissionInfo.nonLocalizedLabel = name;
429        permissionInfo.protectionLevel = protectionLevel;
430        return permissionInfo;
431    }
432
433    public static boolean readLPw(@NonNull Map<String, BasePermission> out,
434            @NonNull XmlPullParser parser) {
435        final String tagName = parser.getName();
436        if (!tagName.equals(TAG_ITEM)) {
437            return false;
438        }
439        final String name = parser.getAttributeValue(null, ATTR_NAME);
440        final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE);
441        final String ptype = parser.getAttributeValue(null, "type");
442        if (name == null || sourcePackage == null) {
443            PackageManagerService.reportSettingsProblem(Log.WARN,
444                    "Error in package manager settings: permissions has" + " no name at "
445                            + parser.getPositionDescription());
446            return false;
447        }
448        final boolean dynamic = "dynamic".equals(ptype);
449        BasePermission bp = out.get(name);
450        // If the permission is builtin, do not clobber it.
451        if (bp == null || bp.type != TYPE_BUILTIN) {
452            bp = new BasePermission(name.intern(), sourcePackage,
453                    dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
454        }
455        bp.protectionLevel = readInt(parser, null, "protection",
456                PermissionInfo.PROTECTION_NORMAL);
457        bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
458        if (dynamic) {
459            final PermissionInfo pi = new PermissionInfo();
460            pi.packageName = sourcePackage.intern();
461            pi.name = name.intern();
462            pi.icon = readInt(parser, null, "icon", 0);
463            pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
464            pi.protectionLevel = bp.protectionLevel;
465            bp.pendingPermissionInfo = pi;
466        }
467        out.put(bp.name, bp);
468        return true;
469    }
470
471    private static int readInt(XmlPullParser parser, String ns, String name, int defValue) {
472        String v = parser.getAttributeValue(ns, name);
473        try {
474            if (v == null) {
475                return defValue;
476            }
477            return Integer.parseInt(v);
478        } catch (NumberFormatException e) {
479            PackageManagerService.reportSettingsProblem(Log.WARN,
480                    "Error in package manager settings: attribute " + name
481                            + " has bad integer value " + v + " at "
482                            + parser.getPositionDescription());
483        }
484        return defValue;
485    }
486
487    public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
488        if (sourcePackageName == null) {
489            return;
490        }
491        serializer.startTag(null, TAG_ITEM);
492        serializer.attribute(null, ATTR_NAME, name);
493        serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
494        if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
495            serializer.attribute(null, "protection", Integer.toString(protectionLevel));
496        }
497        if (type == BasePermission.TYPE_DYNAMIC) {
498            final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
499            if (pi != null) {
500                serializer.attribute(null, "type", "dynamic");
501                if (pi.icon != 0) {
502                    serializer.attribute(null, "icon", Integer.toString(pi.icon));
503                }
504                if (pi.nonLocalizedLabel != null) {
505                    serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
506                }
507            }
508        }
509        serializer.endTag(null, TAG_ITEM);
510    }
511
512    private static boolean compareStrings(CharSequence s1, CharSequence s2) {
513        if (s1 == null) {
514            return s2 == null;
515        }
516        if (s2 == null) {
517            return false;
518        }
519        if (s1.getClass() != s2.getClass()) {
520            return false;
521        }
522        return s1.equals(s2);
523    }
524
525    private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
526        if (pi1.icon != pi2.icon) return false;
527        if (pi1.logo != pi2.logo) return false;
528        if (pi1.protectionLevel != pi2.protectionLevel) return false;
529        if (!compareStrings(pi1.name, pi2.name)) return false;
530        if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
531        // We'll take care of setting this one.
532        if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
533        // These are not currently stored in settings.
534        //if (!compareStrings(pi1.group, pi2.group)) return false;
535        //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
536        //if (pi1.labelRes != pi2.labelRes) return false;
537        //if (pi1.descriptionRes != pi2.descriptionRes) return false;
538        return true;
539    }
540
541    public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
542            @NonNull Set<String> permissionNames, boolean readEnforced,
543            boolean printedSomething, @NonNull DumpState dumpState) {
544        if (packageName != null && !packageName.equals(sourcePackageName)) {
545            return false;
546        }
547        if (permissionNames != null && !permissionNames.contains(name)) {
548            return false;
549        }
550        if (!printedSomething) {
551            if (dumpState.onTitlePrinted())
552                pw.println();
553            pw.println("Permissions:");
554            printedSomething = true;
555        }
556        pw.print("  Permission ["); pw.print(name); pw.print("] (");
557                pw.print(Integer.toHexString(System.identityHashCode(this)));
558                pw.println("):");
559        pw.print("    sourcePackage="); pw.println(sourcePackageName);
560        pw.print("    uid="); pw.print(uid);
561                pw.print(" gids="); pw.print(Arrays.toString(
562                        computeGids(UserHandle.USER_SYSTEM)));
563                pw.print(" type="); pw.print(type);
564                pw.print(" prot=");
565                pw.println(PermissionInfo.protectionToString(protectionLevel));
566        if (perm != null) {
567            pw.print("    perm="); pw.println(perm);
568            if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
569                    || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
570                pw.print("    flags=0x"); pw.println(Integer.toHexString(perm.info.flags));
571            }
572        }
573        if (sourcePackageSetting != null) {
574            pw.print("    packageSetting="); pw.println(sourcePackageSetting);
575        }
576        if (READ_EXTERNAL_STORAGE.equals(name)) {
577            pw.print("    enforced=");
578            pw.println(readEnforced);
579        }
580        return true;
581    }
582}
583