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

Commit 2772baed authored by Sarup Dalwani's avatar Sarup Dalwani Committed by Android (Google) Code Review
Browse files

Merge "Adding Access Control in CrossProfileIntentFilter"

parents bc42fa7c c6597405
Loading
Loading
Loading
Loading
+62 −1
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.pm;
package com.android.server.pm;


import android.annotation.IntDef;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.os.UserHandle;
@@ -30,6 +31,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserException;


import java.io.IOException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


/**
/**
 * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
 * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
@@ -41,13 +44,52 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
    private static final String ATTR_FLAGS = "flags";
    private static final String ATTR_FLAGS = "flags";
    private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
    private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
    private static final String ATTR_FILTER = "filter";
    private static final String ATTR_FILTER = "filter";
    private static final String ATTR_ACCESS_CONTROL = "accessControl";


    private static final String TAG = "CrossProfileIntentFilter";
    private static final String TAG = "CrossProfileIntentFilter";


    /**
     * AccessControlLevel provides level of access for user to create/modify
     * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}.
     * Each AccessControlLevel have value assigned, the higher the value
     * implies higher restriction for creation/modification.
     * AccessControlLevel allows us to protect against malicious changes in user's
     * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter}
     * leading to unprecedented results.
     */
    @IntDef(prefix = {"ACCESS_LEVEL_"}, value = {
            ACCESS_LEVEL_ALL,
            ACCESS_LEVEL_SYSTEM,
            ACCESS_LEVEL_SYSTEM_ADD_ONLY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface AccessControlLevel {
    }

    /**
     * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow
     * access(addition/modification/removal) for CrossProfileIntentFilter.
     * This is the default access control level.
     */
    public static final int ACCESS_LEVEL_ALL = 0;

    /**
     * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to
     * access(addition/modification/removal) CrossProfileIntentFilter.
     */
    public static final int ACCESS_LEVEL_SYSTEM = 10;

    /**
     * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add
     * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed.
     */
    public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20;

    // If the intent matches the IntentFilter, then it can be forwarded to this userId.
    // If the intent matches the IntentFilter, then it can be forwarded to this userId.
    final int mTargetUserId;
    final int mTargetUserId;
    final String mOwnerPackage; // packageName of the app.
    final String mOwnerPackage; // packageName of the app.
    final int mFlags;
    final int mFlags;
    final int mAccessControlLevel;


    // The cache for snapshots, so they are not rebuilt if the base object has not
    // The cache for snapshots, so they are not rebuilt if the base object has not
    // changed.
    // changed.
@@ -65,10 +107,16 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {


    CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
    CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
            int flags) {
            int flags) {
        this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL);
    }

    CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
            int flags, @AccessControlLevel int accessControlLevel) {
        super(filter);
        super(filter);
        mTargetUserId = targetUserId;
        mTargetUserId = targetUserId;
        mOwnerPackage = ownerPackage;
        mOwnerPackage = ownerPackage;
        mFlags = flags;
        mFlags = flags;
        mAccessControlLevel = accessControlLevel;
        mSnapshot = makeCache();
        mSnapshot = makeCache();
    }
    }


@@ -77,12 +125,18 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
        this(filter.mFilter, ownerPackage, targetUserId, flags);
        this(filter.mFilter, ownerPackage, targetUserId, flags);
    }
    }


    CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId,
            int flags, @AccessControlLevel int accessControlLevel) {
        this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel);
    }

    // Copy constructor used only to create a snapshot.
    // Copy constructor used only to create a snapshot.
    private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
    private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
        super(f);
        super(f);
        mTargetUserId = f.mTargetUserId;
        mTargetUserId = f.mTargetUserId;
        mOwnerPackage = f.mOwnerPackage;
        mOwnerPackage = f.mOwnerPackage;
        mFlags = f.mFlags;
        mFlags = f.mFlags;
        mAccessControlLevel = f.mAccessControlLevel;
        mSnapshot = new SnapshotCache.Sealed();
        mSnapshot = new SnapshotCache.Sealed();
    }
    }


@@ -98,9 +152,14 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
        return mOwnerPackage;
        return mOwnerPackage;
    }
    }


    public @AccessControlLevel int getAccessControlLevel() {
        return mAccessControlLevel;
    }

    CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
    CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
        mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
        mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
        mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
        mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
        mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL);
        mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
        mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
        mSnapshot = makeCache();
        mSnapshot = makeCache();


@@ -151,6 +210,7 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
        serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId);
        serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId);
        serializer.attributeInt(null, ATTR_FLAGS, mFlags);
        serializer.attributeInt(null, ATTR_FLAGS, mFlags);
        serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
        serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
        serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel);
        serializer.startTag(null, ATTR_FILTER);
        serializer.startTag(null, ATTR_FILTER);
        mFilter.writeToXml(serializer);
        mFilter.writeToXml(serializer);
        serializer.endTag(null, ATTR_FILTER);
        serializer.endTag(null, ATTR_FILTER);
@@ -165,7 +225,8 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
    boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
    boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
        return mTargetUserId == other.mTargetUserId
        return mTargetUserId == other.mTargetUserId
                && mOwnerPackage.equals(other.mOwnerPackage)
                && mOwnerPackage.equals(other.mOwnerPackage)
                && mFlags == other.mFlags;
                && mFlags == other.mFlags
                && mAccessControlLevel == other.mAccessControlLevel;
    }
    }


    public CrossProfileIntentFilter snapshot() {
    public CrossProfileIntentFilter snapshot() {
+13 −2
Original line number Original line Diff line number Diff line
@@ -3350,6 +3350,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
        int callingUid = Binder.getCallingUid();
        int callingUid = Binder.getCallingUid();
        enforceOwnerRights(snapshot, ownerPackage, callingUid);
        enforceOwnerRights(snapshot, ownerPackage, callingUid);

        // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter}
        // for source and target user
        mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid,
                /* addCrossProfileIntentFilter */ true);

        PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
        PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
                UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
                UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
        if (intentFilter.countActions() == 0) {
        if (intentFilter.countActions() == 0) {
@@ -3358,7 +3364,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        }
        }
        synchronized (mLock) {
        synchronized (mLock) {
            CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
            CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
                    ownerPackage, targetUserId, flags);
                    ownerPackage, targetUserId, flags, mUserManager
                    .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId));
            CrossProfileIntentResolver resolver =
            CrossProfileIntentResolver resolver =
                    mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
                    mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
            ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
            ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
@@ -4572,7 +4579,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
                ArraySet<CrossProfileIntentFilter> set =
                ArraySet<CrossProfileIntentFilter> set =
                        new ArraySet<>(resolver.filterSet());
                        new ArraySet<>(resolver.filterSet());
                for (CrossProfileIntentFilter filter : set) {
                for (CrossProfileIntentFilter filter : set) {
                    if (filter.getOwnerPackage().equals(ownerPackage)) {
                    //Only remove if calling user is allowed based on access control of
                    // {@link CrossProfileIntentFilter}
                    if (filter.getOwnerPackage().equals(ownerPackage)
                            && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId,
                            filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) {
                        resolver.removeFilter(filter);
                        resolver.removeFilter(filter);
                    }
                    }
                }
                }
+97 −0
Original line number Original line Diff line number Diff line
@@ -1866,6 +1866,103 @@ public class UserManagerService extends IUserManager.Stub {
        return mLocalService.exists(userId);
        return mLocalService.exists(userId);
    }
    }


    /**
     * Returns user's {@link  CrossProfileIntentFilter.AccessControlLevel}, which is derived from
     * {@link UserTypeDetails}. If user does not have defined their access control level,
     * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
     */
    private @CrossProfileIntentFilter.AccessControlLevel int
                getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) {
        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
        return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl()
                : CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
    }

    /**
     * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given
     * source and target user.
     * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
     * @param targetUserId target user where we can resolve given intent filter
     * @param callingUid user accessing api
     * @param addCrossProfileIntentFilter if the operation is addition or not.
     * @throws SecurityException is calling user is not allowed to access.
     */
    public void enforceCrossProfileIntentFilterAccess(
            int sourceUserId, int targetUserId,
            int callingUid, boolean addCrossProfileIntentFilter) {
        if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId,
                addCrossProfileIntentFilter)) {
            throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user "
                    + callingUid);
        }
    }

    /**
     * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source
     * and target user. There are following rules of access
     * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL},
     *  irrespective of user we would allow access(addition/modification/removal)
     * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM},
     *  only system/root user would be able to access(addition/modification/removal)
     * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY},
     *  only system/root user would be able to add but not modify/remove. Once added, it cannot be
     *  modified or removed
     * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
     * @param targetUserId target user where we can resolve given intent filter
     * @param addCrossProfileIntentFilter if the operation is addition or not.
     * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user
     */
    public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId,
            boolean addCrossProfileIntentFilter) {
        int effectiveAccessControl =
                getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId);

        /*
        For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not
        system or root disallowing access to {@link CrossProfileIntentFilter}
         */
        if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl
                && !PackageManagerServiceUtils.isSystemOrRoot()) {
            return false;
        }

        /*
        For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only
        system user to add {@link CrossProfileIntentFilter}. All users(including system) are
        disallowed to modify/remove.
         */
        if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl
                && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) {
            return false;
        }
        return true;
    }

    /**
     * Returns {@link CrossProfileIntentFilter.AccessControlLevel}
     * that should be assigned to {@link CrossProfileIntentFilter}
     * computed from source user's and target user's
     * {@link CrossProfileIntentFilter.AccessControlLevel}.
     * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge
     * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured
     * between Primary user and Clone profile should have access level of
     * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest
     * access value from source or target. The higher value means higher restrictions.
     * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored
     * @param targetUserId userId of target user for whom Cross Profile access would be allowed
     * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or
     * target user.
     */
    public @CrossProfileIntentFilter.AccessControlLevel int
                getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) {
        int sourceAccessControlLevel,
                targetAccessControlLevel, effectiveAccessControl;
        sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId);
        targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId);
        effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel);
        return effectiveAccessControl;
    }

    @Override
    @Override
    public void setUserName(@UserIdInt int userId, String name) {
    public void setUserName(@UserIdInt int userId, String name) {
        checkManageUsersPermission("rename users");
        checkManageUsersPermission("rename users");
+35 −2
Original line number Original line Diff line number Diff line
@@ -163,6 +163,14 @@ public final class UserTypeDetails {
     */
     */
    private final boolean mIsCredentialSharableWithParent;
    private final boolean mIsCredentialSharableWithParent;


    /**
     * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile.
     *
     * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
     */
    private final @CrossProfileIntentFilter.AccessControlLevel int
            mCrossProfileIntentFilterAccessControl;

    private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
    private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
            int maxAllowedPerParent,
            int maxAllowedPerParent,
@@ -174,7 +182,8 @@ public final class UserTypeDetails {
            @Nullable Bundle defaultSecureSettings,
            @Nullable Bundle defaultSecureSettings,
            @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
            @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
            boolean isMediaSharedWithParent,
            boolean isMediaSharedWithParent,
            boolean isCredentialSharableWithParent) {
            boolean isCredentialSharableWithParent,
            @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
        this.mName = name;
        this.mName = name;
        this.mEnabled = enabled;
        this.mEnabled = enabled;
        this.mMaxAllowed = maxAllowed;
        this.mMaxAllowed = maxAllowed;
@@ -195,6 +204,7 @@ public final class UserTypeDetails {
        this.mDarkThemeBadgeColors = darkThemeBadgeColors;
        this.mDarkThemeBadgeColors = darkThemeBadgeColors;
        this.mIsMediaSharedWithParent = isMediaSharedWithParent;
        this.mIsMediaSharedWithParent = isMediaSharedWithParent;
        this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
        this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
        this.mCrossProfileIntentFilterAccessControl = accessControlLevel;
    }
    }


    /**
    /**
@@ -327,6 +337,16 @@ public final class UserTypeDetails {
        return mIsCredentialSharableWithParent;
        return mIsCredentialSharableWithParent;
    }
    }


    /**
     * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly
     * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
     * @return user's {@link CrossProfileIntentFilter.AccessControlLevel}
     */
    public @CrossProfileIntentFilter.AccessControlLevel int
            getCrossProfileIntentFilterAccessControl() {
        return mCrossProfileIntentFilterAccessControl;
    }

    /** Returns a {@link Bundle} representing the default user restrictions. */
    /** Returns a {@link Bundle} representing the default user restrictions. */
    @NonNull Bundle getDefaultRestrictions() {
    @NonNull Bundle getDefaultRestrictions() {
        return BundleUtils.clone(mDefaultRestrictions);
        return BundleUtils.clone(mDefaultRestrictions);
@@ -420,6 +440,8 @@ public final class UserTypeDetails {
        private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
        private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
        private boolean mIsMediaSharedWithParent = false;
        private boolean mIsMediaSharedWithParent = false;
        private boolean mIsCredentialSharableWithParent = false;
        private boolean mIsCredentialSharableWithParent = false;
        private @CrossProfileIntentFilter.AccessControlLevel int
                mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL;


        public Builder setName(String name) {
        public Builder setName(String name) {
            mName = name;
            mName = name;
@@ -519,6 +541,16 @@ public final class UserTypeDetails {
            return this;
            return this;
        }
        }


        /**
         * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user.
         * @param accessControlLevel default access control for user
         */
        public Builder setCrossProfileIntentFilterAccessControl(
                @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
            mCrossProfileIntentFilterAccessControl = accessControlLevel;
            return this;
        }

        /**
        /**
         * Sets shared media property for the user.
         * Sets shared media property for the user.
         * @param isCredentialSharableWithParent  the value to be set, true or false
         * @param isCredentialSharableWithParent  the value to be set, true or false
@@ -571,7 +603,8 @@ public final class UserTypeDetails {
                    mDefaultSecureSettings,
                    mDefaultSecureSettings,
                    mDefaultCrossProfileIntentFilters,
                    mDefaultCrossProfileIntentFilters,
                    mIsMediaSharedWithParent,
                    mIsMediaSharedWithParent,
                    mIsCredentialSharableWithParent);
                    mIsCredentialSharableWithParent,
                    mCrossProfileIntentFilterAccessControl);
        }
        }


        private boolean hasBadge() {
        private boolean hasBadge() {
+2 −0
Original line number Original line Diff line number Diff line
@@ -122,6 +122,8 @@ public final class UserTypeFactory {
                .setLabel(0)
                .setLabel(0)
                .setDefaultRestrictions(null)
                .setDefaultRestrictions(null)
                .setIsMediaSharedWithParent(true)
                .setIsMediaSharedWithParent(true)
                .setCrossProfileIntentFilterAccessControl(
                        CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM)
                .setIsCredentialSharableWithParent(true);
                .setIsCredentialSharableWithParent(true);
    }
    }