Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 769b2e75 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Add facility to limit associations that are allowed between apps.

This allows the system to be configured so that certain applications
are only allowed to do top-level interactions with a hard-coded set
of other applications.  This provides static enforcement of certain
security policies like "app A can only interact with the system and
app B, and even if updated can not directly have incoming or outgoing
interactions with other apps."

For example to limit a the package com.google.android.as to only
interact with telephony and contacts (in addition to the core
system):

    <allow-association target="com.google.android.as"
        allowed="com.android.providers.telephony" />
    <allow-association target="com.google.android.as"
        allowed="com.android.providers.contacts" />

Also improve procstats output to be able to print all associations
related to a process.  (I wanted to be able to do this by package,
but we don't have enough data in associations. :p)

Bug: 111276913
Test: Manual so far
Change-Id: I61b7f2d2b5c2c3d82b278e6678b600b579b19fb7
parent a5abb4a7
Loading
Loading
Loading
Loading
+14 −1
Original line number Original line Diff line number Diff line
@@ -445,8 +445,18 @@ public final class AssociationState {
        }
        }
    }
    }


    public boolean hasProcess(String procName) {
        final int NSRC = mSources.size();
        for (int isrc = 0; isrc < NSRC; isrc++) {
            if (mSources.keyAt(isrc).mProcess.equals(procName)) {
                return true;
            }
        }
        return false;
    }

    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
            long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
            long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
        if (dumpAll) {
        if (dumpAll) {
            pw.print(prefix);
            pw.print(prefix);
            pw.print("mNumActive=");
            pw.print("mNumActive=");
@@ -456,6 +466,9 @@ public final class AssociationState {
        for (int isrc = 0; isrc < NSRC; isrc++) {
        for (int isrc = 0; isrc < NSRC; isrc++) {
            final SourceKey key = mSources.keyAt(isrc);
            final SourceKey key = mSources.keyAt(isrc);
            final SourceState src = mSources.valueAt(isrc);
            final SourceState src = mSources.valueAt(isrc);
            if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
                continue;
            }
            pw.print(prefixInner);
            pw.print(prefixInner);
            pw.print("<- ");
            pw.print("<- ");
            pw.print(key.mProcess);
            pw.print(key.mProcess);
+20 −5
Original line number Original line Diff line number Diff line
@@ -1474,6 +1474,7 @@ public final class ProcessStats implements Parcelable {
                        final int NSRVS = pkgState.mServices.size();
                        final int NSRVS = pkgState.mServices.size();
                        final int NASCS = pkgState.mAssociations.size();
                        final int NASCS = pkgState.mAssociations.size();
                        final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
                        final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
                        boolean onlyAssociations = false;
                        if (!pkgMatch) {
                        if (!pkgMatch) {
                            boolean procMatch = false;
                            boolean procMatch = false;
                            for (int iproc = 0; iproc < NPROCS; iproc++) {
                            for (int iproc = 0; iproc < NPROCS; iproc++) {
@@ -1484,9 +1485,20 @@ public final class ProcessStats implements Parcelable {
                                }
                                }
                            }
                            }
                            if (!procMatch) {
                            if (!procMatch) {
                                // Check if this app has any associations with the requested
                                // package, so that if so we print those.
                                for (int iasc = 0; iasc < NASCS; iasc++) {
                                    AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                    if (asc.hasProcess(reqPackage)) {
                                        onlyAssociations = true;
                                        break;
                                    }
                                }
                                if (!onlyAssociations) {
                                    continue;
                                    continue;
                                }
                                }
                            }
                            }
                        }
                        if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
                        if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
                            if (!printedHeader) {
                            if (!printedHeader) {
                                if (sepNeeded) pw.println();
                                if (sepNeeded) pw.println();
@@ -1502,7 +1514,7 @@ public final class ProcessStats implements Parcelable {
                            pw.print(vers);
                            pw.print(vers);
                            pw.println(":");
                            pw.println(":");
                        }
                        }
                        if ((section & REPORT_PKG_PROC_STATS) != 0) {
                        if ((section & REPORT_PKG_PROC_STATS) != 0 && !onlyAssociations) {
                            if (!dumpSummary || dumpAll) {
                            if (!dumpSummary || dumpAll) {
                                for (int iproc = 0; iproc < NPROCS; iproc++) {
                                for (int iproc = 0; iproc < NPROCS; iproc++) {
                                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
                                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
@@ -1549,7 +1561,7 @@ public final class ProcessStats implements Parcelable {
                                        now, totalTime);
                                        now, totalTime);
                            }
                            }
                        }
                        }
                        if ((section & REPORT_PKG_SVC_STATS) != 0) {
                        if ((section & REPORT_PKG_SVC_STATS) != 0 && !onlyAssociations) {
                            for (int isvc = 0; isvc < NSRVS; isvc++) {
                            for (int isvc = 0; isvc < NSRVS; isvc++) {
                                ServiceState svc = pkgState.mServices.valueAt(isvc);
                                ServiceState svc = pkgState.mServices.valueAt(isvc);
                                if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
                                if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
@@ -1578,8 +1590,10 @@ public final class ProcessStats implements Parcelable {
                            for (int iasc = 0; iasc < NASCS; iasc++) {
                            for (int iasc = 0; iasc < NASCS; iasc++) {
                                AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
                                if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
                                    if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
                                        continue;
                                        continue;
                                    }
                                    }
                                }
                                if (activeOnly && !asc.isInUse()) {
                                if (activeOnly && !asc.isInUse()) {
                                    pw.print("      (Not active association: ");
                                    pw.print("      (Not active association: ");
                                    pw.print(pkgState.mAssociations.keyAt(iasc));
                                    pw.print(pkgState.mAssociations.keyAt(iasc));
@@ -1596,7 +1610,8 @@ public final class ProcessStats implements Parcelable {
                                pw.print("        Process: ");
                                pw.print("        Process: ");
                                pw.println(asc.getProcessName());
                                pw.println(asc.getProcessName());
                                asc.dumpStats(pw, "        ", "          ", "    ",
                                asc.dumpStats(pw, "        ", "          ", "    ",
                                        now, totalTime, dumpDetails, dumpAll);
                                        now, totalTime, onlyAssociations ? reqPackage : null,
                                        dumpDetails, dumpAll);
                            }
                            }
                        }
                        }
                    }
                    }
+442 −294

File changed.

Preview size limit exceeded, changes collapsed.

+18 −5
Original line number Original line Diff line number Diff line
@@ -2019,6 +2019,13 @@ public final class ActiveServices {
                ComponentName className = new ComponentName(
                ComponentName className = new ComponentName(
                        sInfo.applicationInfo.packageName, sInfo.name);
                        sInfo.applicationInfo.packageName, sInfo.name);
                ComponentName name = comp != null ? comp : className;
                ComponentName name = comp != null ? comp : className;
                if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
                        name.getPackageName(), sInfo.applicationInfo.uid)) {
                    String msg = "association not allowed between packages "
                            + callingPackage + " and " + r.packageName;
                    Slog.w(TAG, "Service lookup failed: " + msg);
                    return new ServiceLookupResult(null, msg);
                }
                if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
                if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
                    if (isBindExternal) {
                    if (isBindExternal) {
                        if (!sInfo.exported) {
                        if (!sInfo.exported) {
@@ -2099,6 +2106,17 @@ public final class ActiveServices {
            }
            }
        }
        }
        if (r != null) {
        if (r != null) {
            if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName,
                    r.appInfo.uid)) {
                String msg = "association not allowed between packages "
                        + callingPackage + " and " + r.packageName;
                Slog.w(TAG, "Service lookup failed: " + msg);
                return new ServiceLookupResult(null, msg);
            }
            if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
                    resolvedType, r.appInfo)) {
                return new ServiceLookupResult(null, "blocked by firewall");
            }
            if (mAm.checkComponentPermission(r.permission,
            if (mAm.checkComponentPermission(r.permission,
                    callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
                    callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
                if (!r.exported) {
                if (!r.exported) {
@@ -2125,11 +2143,6 @@ public final class ActiveServices {
                    return null;
                    return null;
                }
                }
            }
            }

            if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
                    resolvedType, r.appInfo)) {
                return null;
            }
            return new ServiceLookupResult(r, null);
            return new ServiceLookupResult(r, null);
        }
        }
        return null;
        return null;
+116 −0
Original line number Original line Diff line number Diff line
@@ -657,6 +657,12 @@ public class ActivityManagerService extends IActivityManager.Stub
     */
     */
    ArraySet<String> mBackgroundLaunchBroadcasts;
    ArraySet<String> mBackgroundLaunchBroadcasts;
    /**
     * When an app has restrictions on the other apps that can have associations with it,
     * it appears here with a set of the allowed apps.
     */
    ArrayMap<String, ArraySet<String>> mAllowedAssociations;
    /**
    /**
     * All of the processes we currently have running organized by pid.
     * All of the processes we currently have running organized by pid.
     * The keys are the pid running the application.
     * The keys are the pid running the application.
@@ -2396,6 +2402,34 @@ public class ActivityManagerService extends IActivityManager.Stub
        return mBackgroundLaunchBroadcasts;
        return mBackgroundLaunchBroadcasts;
    }
    }
    boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
        if (mAllowedAssociations == null) {
            mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
        }
        // Interactions with the system uid are always allowed, since that is the core system
        // that everyone needs to be able to interact with.
        if (UserHandle.getAppId(uid1) == SYSTEM_UID) {
            return true;
        }
        if (UserHandle.getAppId(uid2) == SYSTEM_UID) {
            return true;
        }
        // We won't allow this association if either pkg1 or pkg2 has a limit on the
        // associations that are allowed with it, and the other package is not explicitly
        // specified as one of those associations.
        ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
        if (pkgs != null) {
            if (!pkgs.contains(pkg2)) {
                return false;
            }
        }
        pkgs = mAllowedAssociations.get(pkg2);
        if (pkgs != null) {
            return pkgs.contains(pkg1);
        }
        return true;
    }
    @Override
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
            throws RemoteException {
@@ -6225,6 +6259,21 @@ public class ActivityManagerService extends IActivityManager.Stub
        return state != 'Z' && state != 'X' && state != 'x' && state != 'K';
        return state != 'Z' && state != 'X' && state != 'x' && state != 'K';
    }
    }
    private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
            ProviderInfo cpi) {
        if (callingApp == null) {
            return validateAssociationAllowedLocked(cpi.packageName, cpi.applicationInfo.uid,
                    null, callingUid) ? null : "<null>";
        }
        for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
            if (!validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i), callingApp.uid,
                    cpi.packageName, cpi.applicationInfo.uid)) {
                return cpi.packageName;
            }
        }
        return null;
    }
    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, int callingUid, String callingTag, boolean stable,
            String name, IBinder token, int callingUid, String callingTag, boolean stable,
            int userId) {
            int userId) {
@@ -6296,6 +6345,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                String msg;
                String msg;
                if (r != null && cpr.canRunHere(r)) {
                if (r != null && cpr.canRunHere(r)) {
                    if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
                        throw new SecurityException("Content provider lookup "
                                + cpr.name.flattenToShortString()
                                + " failed: association not allowed with package " + msg);
                    }
                    checkTime(startTime,
                    checkTime(startTime,
                            "getContentProviderImpl: before checkContentProviderPermission");
                            "getContentProviderImpl: before checkContentProviderPermission");
                    if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                    if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6325,6 +6379,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                } catch (RemoteException e) {
                } catch (RemoteException e) {
                }
                }
                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
                    throw new SecurityException("Content provider lookup "
                            + cpr.name.flattenToShortString()
                            + " failed: association not allowed with package " + msg);
                }
                checkTime(startTime,
                checkTime(startTime,
                        "getContentProviderImpl: before checkContentProviderPermission");
                        "getContentProviderImpl: before checkContentProviderPermission");
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6422,6 +6481,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                checkTime(startTime, "getContentProviderImpl: got app info for user");
                checkTime(startTime, "getContentProviderImpl: got app info for user");
                String msg;
                String msg;
                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
                    throw new SecurityException("Content provider lookup "
                            + cpr.name.flattenToShortString()
                            + " failed: association not allowed with package " + msg);
                }
                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                        != null) {
                        != null) {
@@ -9167,6 +9231,12 @@ public class ActivityManagerService extends IActivityManager.Stub
            if (dumpAll) {
            if (dumpAll) {
                pw.println("-------------------------------------------------------------------------------");
                pw.println("-------------------------------------------------------------------------------");
            }
            dumpAllowedAssociationsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
            pw.println();
            if (dumpAll) {
                pw.println("-------------------------------------------------------------------------------");
            }
            }
            mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
            mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
            pw.println();
            pw.println();
@@ -9435,6 +9505,14 @@ public class ActivityManagerService extends IActivityManager.Stub
                    System.gc();
                    System.gc();
                    pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
                    pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
                }
                }
            } else if ("allowed-associations".equals(cmd)) {
                if (opti < args.length) {
                    dumpPackage = args[opti];
                    opti++;
                }
                synchronized (this) {
                    dumpAllowedAssociationsLocked(fd, pw, args, opti, true, dumpPackage);
                }
            } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
            } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
                if (opti < args.length) {
                if (opti < args.length) {
                    dumpPackage = args[opti];
                    dumpPackage = args[opti];
@@ -10785,6 +10863,44 @@ public class ActivityManagerService extends IActivityManager.Stub
        proto.end(handlerToken);
        proto.end(handlerToken);
    }
    }
    void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
            int opti, boolean dumpAll, String dumpPackage) {
        boolean needSep = false;
        boolean printedAnything = false;
        pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
        boolean printed = false;
        if (mAllowedAssociations != null) {
            for (int i = 0; i < mAllowedAssociations.size(); i++) {
                final String pkg = mAllowedAssociations.keyAt(i);
                final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
                boolean printedHeader = false;
                for (int j = 0; j < asc.size(); j++) {
                    if (dumpPackage == null || pkg.equals(dumpPackage)
                            || asc.valueAt(j).equals(dumpPackage)) {
                        if (!printed) {
                            pw.println("  Allowed associations (by restricted package):");
                            printed = true;
                            needSep = true;
                            printedAnything = true;
                        }
                        if (!printedHeader) {
                            pw.print("  * ");
                            pw.print(pkg);
                            pw.println(":");
                            printedHeader = true;
                        }
                        pw.print("      Allow: ");
                        pw.println(asc.valueAt(j));
                    }
                }
            }
        }
        if (!printed) {
            pw.println("  (No association restrictions)");
        }
    }
    void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
    void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
            int opti, boolean dumpAll, String dumpPackage) {
            int opti, boolean dumpAll, String dumpPackage) {
        boolean needSep = false;
        boolean needSep = false;
Loading