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

Commit 5c921daa authored by Robin Lee's avatar Robin Lee
Browse files

Fix privilege escalation for preferred activities

Passing in the name of an actual admin should be enough to pass the
security check as it was. This is now fixed as the caller is not
given the opportunity to spoof its own name any more.

Change-Id: Id8be4ca4c8bf3751a1ee8125cf119fa100c81d22
parent 7638b1f3
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -51,6 +51,22 @@ import java.util.HashMap;
public final class DeviceAdminInfo implements Parcelable {
    static final String TAG = "DeviceAdminInfo";

    /**
     * A type of policy that this device admin can use: device owner meta-policy
     * for an admin that is designated as owner of the device.
     *
     * @hide
     */
    public static final int USES_POLICY_DEVICE_OWNER = -2;

    /**
     * A type of policy that this device admin can use: profile owner meta-policy
     * for admins that have been installed as owner of some user profile.
     *
     * @hide
     */
    public static final int USES_POLICY_PROFILE_OWNER = -1;

    /**
     * A type of policy that this device admin can use: limit the passwords
     * that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
+65 −63
Original line number Diff line number Diff line
@@ -170,7 +170,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        int mActivePasswordNonLetter = 0;
        int mFailedPasswordAttempts = 0;

        int mUserHandle;;
        int mUserHandle;
        int mPasswordOwner = -1;
        long mLastMaximumTimeToLock = -1;

@@ -722,6 +722,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        final int callingUid = Binder.getCallingUid();
        final int userHandle = UserHandle.getUserId(callingUid);
        final DevicePolicyData policy = getUserData(userHandle);

        List<ActiveAdmin> candidates = new ArrayList<ActiveAdmin>();

        // Build a list of admins for this uid matching the given ComponentName
        if (who != null) {
            ActiveAdmin admin = policy.mAdminMap.get(who);
            if (admin == null) {
@@ -731,22 +735,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                throw new SecurityException("Admin " + who + " is not owned by uid "
                        + Binder.getCallingUid());
            }
            if (!admin.info.usesPolicy(reqPolicy)) {
                throw new SecurityException("Admin " + admin.info.getComponent()
                        + " did not specify uses-policy for: "
                        + admin.info.getTagForPolicy(reqPolicy));
            candidates.add(admin);
        } else {
            for (ActiveAdmin admin : policy.mAdminList) {
                if (admin.getUid() == callingUid) {
                    candidates.add(admin);
                }
            }
        }

        // Try to find an admin which can use reqPolicy
        for (ActiveAdmin admin : candidates) {
            boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
            boolean ownsProfile = (getProfileOwner(userHandle) != null
                    && getProfileOwner(userHandle).equals(admin.info.getPackageName()));

            if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
                if (ownsDevice) {
                    return admin;
                }
            } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
                if (ownsDevice || ownsProfile) {
                    return admin;
                }
            } else {
            final int N = policy.mAdminList.size();
            for (int i=0; i<N; i++) {
                ActiveAdmin admin = policy.mAdminList.get(i);
                if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) {
                if (admin.info.usesPolicy(reqPolicy)) {
                    return admin;
                }
            }
        }

        if (who != null) {
            throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
                    + " did not specify uses-policy for: "
                    + candidates.get(0).info.getTagForPolicy(reqPolicy));
        } else {
            throw new SecurityException("No active admin owned by uid "
                    + Binder.getCallingUid() + " for policy #" + reqPolicy);
                    + Binder.getCallingUid() + " for policy:" + reqPolicy);
        }
    }

@@ -2966,65 +2991,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        }
    }

    private boolean isProfileOwner(String packageName, int userId) {
        String profileOwnerPackage = getProfileOwner(userId);
        // TODO: make public and connect with isProfileOwnerApp in DPM
        return profileOwnerPackage != null && profileOwnerPackage.equals(packageName);
    }

    public void addPersistentPreferredActivity(ComponentName admin, IntentFilter filter,
    public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
            ComponentName activity) {
        int callingUserId = UserHandle.getCallingUserId();
        Slog.d(LOG_TAG,"called by user " + callingUserId);
        synchronized (this) {
            ActiveAdmin aa = getActiveAdminUncheckedLocked(admin, callingUserId);
            if (aa == null) {
                throw new SecurityException("No active admin " + admin);
            } else {
                if (isProfileOwner(admin.getPackageName(), callingUserId)
                        || isDeviceOwner(admin.getPackageName())) {
            if (who == null) {
                throw new NullPointerException("ComponentName is null");
            }
            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);

            IPackageManager pm = AppGlobals.getPackageManager();
            long id = Binder.clearCallingIdentity();
            try {
                        pm.addPersistentPreferredActivity(filter, activity, callingUserId);
                pm.addPersistentPreferredActivity(filter, activity, UserHandle.getCallingUserId());
            } catch (RemoteException re) {
                // Shouldn't happen
            } finally {
                restoreCallingIdentity(id);
            }
                } else {
                    throw new SecurityException("Admin " + admin +
                            "is not device owner or profile owner" );
                }
            }
        }
    }

    public void clearPackagePersistentPreferredActivities(ComponentName admin,
            String packageName) {
        int callingUserId = UserHandle.getCallingUserId();
        Slog.d(LOG_TAG,"called by user " + callingUserId);
    public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
        synchronized (this) {
            ActiveAdmin aa = getActiveAdminUncheckedLocked(admin, callingUserId);
            if (aa == null) {
                throw new SecurityException("No active admin " + admin);
            } else {
                if (isProfileOwner(admin.getPackageName(), callingUserId)
                        || isDeviceOwner(admin.getPackageName())) {
            if (who == null) {
                throw new NullPointerException("ComponentName is null");
            }
            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);

            IPackageManager pm = AppGlobals.getPackageManager();
            long id = Binder.clearCallingIdentity();
            try {
                        pm.clearPackagePersistentPreferredActivities(packageName, callingUserId);
                pm.clearPackagePersistentPreferredActivities(packageName, UserHandle.getCallingUserId());
            } catch (RemoteException re) {
                // Shouldn't happen
            } finally {
                restoreCallingIdentity(id);
            }
                } else {
                    throw new SecurityException("Admin " + admin +
                            "is not device owner or profile owner" );
                }
            }
        }
    }
}