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

Commit ae74c848 authored by Joman Chu's avatar Joman Chu Committed by Ricardo Cerqueira
Browse files

Add APIs to allow Device Admins to change SELinux settings

These calls, added to the Device Admin API, will allow Device Admin apps
to change various SELinux settings, including:
* Toggling SELinux enforcing and permissive modes
* Toggle SELinux booleans
* Load a new SELinux policy file (sepolicy)
* Load new SELinux context files ({property,file,seapp}_contexts)

In order to use these APIs, a Device Admin must first request
USES_POLICY_ENFORCE_SELINUX, then become a SELinux Admin by calling
setSELinuxAdmin(). All other set* calls relevant to SELinux are guarded
by a check against whether the admin is a SELinux Admin.

Otherwise, the style of the set* calls are very similar to the other
calls setting device policy in the Device Admin API. That is, these
calls change the Admin's internal state and then call a sync method to
update the device's state to the Admin's state.

Change-Id: I01f2a9084dfe7886087b1497070b0d7f2ad8476e
parent 88aeb650
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -4220,6 +4220,7 @@ package android.app.admin {
    field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
    field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
    field public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9; // 0x9
    field public static final int USES_POLICY_ENFORCE_SELINUX = 10; // 0xa
    field public static final int USES_POLICY_EXPIRE_PASSWORD = 6; // 0x6
    field public static final int USES_POLICY_FORCE_LOCK = 3; // 0x3
    field public static final int USES_POLICY_LIMIT_PASSWORD = 0; // 0x0
@@ -4270,15 +4271,21 @@ package android.app.admin {
    method public int getPasswordMinimumSymbols(android.content.ComponentName);
    method public int getPasswordMinimumUpperCase(android.content.ComponentName);
    method public int getPasswordQuality(android.content.ComponentName);
    method public java.util.List<java.lang.String> getSELinuxBooleanNames(android.content.ComponentName);
    method public boolean getSELinuxBooleanValue(android.content.ComponentName, java.lang.String);
    method public boolean getSELinuxEnforcing(android.content.ComponentName);
    method public boolean getStorageEncryption(android.content.ComponentName);
    method public int getStorageEncryptionStatus();
    method public boolean hasGrantedPolicy(android.content.ComponentName, int);
    method public boolean isActivePasswordSufficient();
    method public boolean isAdminActive(android.content.ComponentName);
    method public boolean isCustomPolicyFile(android.content.ComponentName, int);
    method public boolean isSELinuxAdmin(android.content.ComponentName);
    method public void lockNow();
    method public void removeActiveAdmin(android.content.ComponentName);
    method public boolean resetPassword(java.lang.String, int);
    method public void setCameraDisabled(android.content.ComponentName, boolean);
    method public boolean setCustomPolicyFile(android.content.ComponentName, int, byte[]);
    method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
    method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
    method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -4292,6 +4299,9 @@ package android.app.admin {
    method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
    method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
    method public void setPasswordQuality(android.content.ComponentName, int);
    method public boolean setSELinuxAdmin(android.content.ComponentName, boolean);
    method public boolean setSELinuxBooleanValue(android.content.ComponentName, java.lang.String, boolean);
    method public boolean setSELinuxEnforcing(android.content.ComponentName, boolean);
    method public int setStorageEncryption(android.content.ComponentName, boolean);
    method public void wipeData(int);
    field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
@@ -4315,6 +4325,11 @@ package android.app.admin {
    field public static final int PASSWORD_QUALITY_SOMETHING = 65536; // 0x10000
    field public static final int PASSWORD_QUALITY_UNSPECIFIED = 0; // 0x0
    field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
    field public static final int SEPOLICY_FILE_COUNT = 4; // 0x4
    field public static final int SEPOLICY_FILE_FILECTXS = 2; // 0x2
    field public static final int SEPOLICY_FILE_PROPCTXS = 1; // 0x1
    field public static final int SEPOLICY_FILE_SEAPPCTXS = 3; // 0x3
    field public static final int SEPOLICY_FILE_SEPOLICY = 0; // 0x0
    field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
  }
+12 −1
Original line number Diff line number Diff line
@@ -146,6 +146,14 @@ public final class DeviceAdminInfo implements Parcelable {
     */
    public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;

    /**
     * A type of policy that this device admin can use: enforce SELinux policy.
     *
     * <p>To control this policy, the device admin must have a "enforce-selinux"
     * tag in the "uses-policies" section of its meta-data.
     */
    public static final int USES_POLICY_ENFORCE_SELINUX = 10;

    /** @hide */
    public static class PolicyInfo {
        public final int ident;
@@ -197,6 +205,9 @@ public final class DeviceAdminInfo implements Parcelable {
                USES_POLICY_DISABLE_KEYGUARD_FEATURES, "disable-keyguard-features",
                com.android.internal.R.string.policylab_disableKeyguardFeatures,
                com.android.internal.R.string.policydesc_disableKeyguardFeatures));
        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_ENFORCE_SELINUX, "enforce-selinux",
                com.android.internal.R.string.policylab_enforceSelinux,
                com.android.internal.R.string.policydesc_enforceSelinux));

        for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
            PolicyInfo pi = sPoliciesDisplayOrder.get(i);
@@ -389,7 +400,7 @@ public final class DeviceAdminInfo implements Parcelable {
     * {@link #USES_POLICY_RESET_PASSWORD}, {@link #USES_POLICY_FORCE_LOCK},
     * {@link #USES_POLICY_WIPE_DATA},
     * {@link #USES_POLICY_EXPIRE_PASSWORD}, {@link #USES_ENCRYPTED_STORAGE},
     * {@link #USES_POLICY_DISABLE_CAMERA}.
     * {@link #USES_POLICY_DISABLE_CAMERA}, {@link #USES_POLICY_ENFORCE_SELINUX}.
     */
    public boolean usesPolicy(int policyIdent) {
        return (mUsesPolicies & (1<<policyIdent)) != 0;
+297 −0
Original line number Diff line number Diff line
@@ -1420,6 +1420,303 @@ public class DevicePolicyManager {
        return KEYGUARD_DISABLE_FEATURES_NONE;
    }

    /**
     * Called by an application that is administering the device to start or stop
     * controlling SELinux policies, enforcement, booleans, etc. When an admin app
     * gives up control of SELinux policies, the policy in place prior to the app
     * taking control will be applied.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * <p>When an application gains control of SELinux settings, it is called an
     * SELinux administrator. Admistration applications will call this with true and
     * ensure this method returned true before attempting to toggle SELinux settings.
     * When apps intend to stop controlling SELinux settings, apps should call this
     * with false.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated,
     * must be self
     * @param control true if the admin wishes to control SELinux, false if the admin
     * wishes to give back control of SELinux
     * @return true if the operation succeeded, false if the operation failed or
     * SELinux was not enabled on the device.
     */
    public boolean setSELinuxAdmin(ComponentName admin, boolean control) {
        return setSELinuxAdmin(admin, control, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean setSELinuxAdmin(ComponentName admin, boolean control, int userHandle) {
        if (mService != null) {
            try {
                return mService.setSELinuxAdmin(admin, control, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * Checks whether an admin app has control over SELinux policy.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated,
     * must be self
     * @return true if admin app can control SELinux policy, false otherwise
     */
    public boolean isSELinuxAdmin(ComponentName admin) {
        return isSELinuxAdmin(admin, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean isSELinuxAdmin(ComponentName admin, int userHandle) {
        if (mService != null) {
            try {
                return mService.isSELinuxAdmin(admin, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * Called by a SELinux admin to set SELinux into enforcing or permissive mode.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param enforcing true for enforcing mode, false for permissive mode.
     * @return false if Android was unable to set the desired mode
     */
    public boolean setSELinuxEnforcing(ComponentName admin, boolean enforcing) {
        return setSELinuxEnforcing(admin, enforcing, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean setSELinuxEnforcing(ComponentName admin, boolean enforcing, int userHandle) {
        if (mService != null) {
            try {
                return mService.setSELinuxEnforcing(admin, enforcing, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false; // I guess this doesn't fit the spec, but it never happens...
    }

    /**
     * Determine whether or not SELinux policies are currently being enforced
     * by the current admin.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * <p>The returned value is only meaningful if the current admin is a
     * SELinux admin.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     */
    public boolean getSELinuxEnforcing(ComponentName admin) {
        return getSELinuxEnforcing(admin, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean getSELinuxEnforcing(ComponentName admin, int userHandle) {
        if (mService != null) {
            try {
                return mService.getSELinuxEnforcing(admin, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * Get a list of the SELinux booleans available on the system.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * <p>The returned value is only meaningful if the current admin is a
     * SELinux admin.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     */
    public List<String> getSELinuxBooleanNames(ComponentName admin) {
        return getSELinuxBooleanNames(admin, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public List<String> getSELinuxBooleanNames(ComponentName admin, int userHandle) {
        if (mService != null) {
            try {
                return mService.getSELinuxBooleanNames(admin, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return null;
    }

    /**
     * Get the value of a SELinux boolean.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * <p>The returned value is only meaningful if the current admin is a
     * SELinux admin.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param name the name of the SELinux boolean
     * @return the value of the SELinux boolean
     */
    public boolean getSELinuxBooleanValue(ComponentName admin, String name) {
        return getSELinuxBooleanValue(admin, name, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean getSELinuxBooleanValue(ComponentName admin, String name, int userHandle) {
        if (mService != null) {
            try {
                return mService.getSELinuxBooleanValue(admin, name, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * Set the value of a SELinux boolean.
     *
     * <p>The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} to be able to call
     * this method; if it has not, a security exception will be thrown.
     *
     * <p>The returned value is only meaningful if the current admin is a
     * SELinux admin.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param name the name of the SELinux boolean
     * @param value the desired value for the boolean
     * @return false if Android was unable to set the desired mode
     */
    public boolean setSELinuxBooleanValue(ComponentName admin, String name,
            boolean value) {
        return setSELinuxBooleanValue(admin, name, value, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean setSELinuxBooleanValue(ComponentName admin, String name,
            boolean value, int userHandle) {
        if (mService != null) {
            try {
                return mService.setSELinuxBooleanValue(admin, name, value, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    // Before changing these values, be sure to update
    // DevicePolicyManagerService.java's POLICY_DESCRIPTIONS array.
    public static final int SEPOLICY_FILE_SEPOLICY = 0;
    public static final int SEPOLICY_FILE_PROPCTXS = 1;
    public static final int SEPOLICY_FILE_FILECTXS = 2;
    public static final int SEPOLICY_FILE_SEAPPCTXS = 3;
    public static final int SEPOLICY_FILE_COUNT = SEPOLICY_FILE_SEAPPCTXS+1;

    /**
     * Sets a new policy file and reloads it at the proper time.
     *
     * <p>For {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, and {@link #SEPOLICY_FILE_SEAPPCTXS}, the admin
     * must have requested {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX}
     * before calling this method. If it has not, a security exception will be
     * thrown.
     *
     * <p>For {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, and {@link #SEPOLICY_FILE_SEAPPCTXS}, these
     * files are reloaded before returning from the DevicePolicyManager.
     *
     * <p>For {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, and {@link #SEPOLICY_FILE_SEAPPCTXS}, the
     * returned value is only meaingful if the current admin is a SELinux
     * admin.
     *
     * @param admin which {@link DeviceAdminReceiver} this request is associated with
     * @param policyType one of {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, or {@link #SEPOLICY_FILE_SEAPPCTXS}
     * @param policy the new policy file in bytes, or null if you wish to revert to
     * the default policy
     * @return false if Android was unable to set the new policy
     */
    public boolean setCustomPolicyFile(ComponentName admin, int policyType, byte[] policy) {
        return setCustomPolicyFile(admin, policyType, policy, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean setCustomPolicyFile(ComponentName admin, int policyType, byte[] policy, int userHandle) {
        if (mService != null) {
            try {
                return mService.setCustomPolicyFile(admin, policyType, policy, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * Determine whether this admin set a custom policy file.
     *
     * <p>For {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, and {@link #SEPOLICY_FILE_SEAPPCTXS}, the admin
     * must have requested {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX}
     * before calling this method. If it has not, a security exception will be
     * thrown.
     *
     * <p>For {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, and {@link #SEPOLICY_FILE_SEAPPCTXS}, the
     * returned value is only meaingful if the current admin is a SELinux
     * admin.
     *
     * @param admin which {@link DeviceAdminReceiver} this request is associated with
     * @param policyType one of {@link #SEPOLICY_FILE_SEPOLICY}, {@link #SEPOLICY_FILE_PROPCTXS},
     * {@link #SEPOLICY_FILE_FILECTXS}, or {@link #SEPOLICY_FILE_SEAPPCTXS}
     * @return true if the admin set a custom policy file
     */
    public boolean isCustomPolicyFile(ComponentName admin, int policyType) {
        return isCustomPolicyFile(admin, policyType, UserHandle.myUserId());
    }

    /** @hide per-user version */
    public boolean isCustomPolicyFile(ComponentName admin, int policyType, int userHandle) {
        if (mService != null) {
            try {
                return mService.isCustomPolicyFile(admin, policyType, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy server", e);
            }
        }
        return false;
    }

    /**
     * @hide
     */
+13 −0
Original line number Diff line number Diff line
@@ -93,6 +93,19 @@ interface IDevicePolicyManager {
    void removeActiveAdmin(in ComponentName policyReceiver, int userHandle);
    boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);

    boolean setSELinuxAdmin(in ComponentName who, boolean control, int userHandle);
    boolean isSELinuxAdmin(in ComponentName who, int userHandle);

    boolean setSELinuxEnforcing(in ComponentName who, boolean enforcing, int userHandle);
    boolean getSELinuxEnforcing(in ComponentName who, int userHandle);

    List<String> getSELinuxBooleanNames(in ComponentName who, int userHandle);
    boolean getSELinuxBooleanValue(in ComponentName who, String name, int userHandle);
    boolean setSELinuxBooleanValue(in ComponentName who, String name, boolean value, int userHandle);

    boolean setCustomPolicyFile(in ComponentName who, int policyType, in byte[] policy, int userHandle);
    boolean isCustomPolicyFile(in ComponentName who, int policyType, int userHandle);

    void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
        int numbers, int symbols, int nonletter, int userHandle);
    void reportFailedPasswordAttempt(int userHandle);
+4 −0
Original line number Diff line number Diff line
@@ -1820,6 +1820,10 @@
    <string name="policylab_disableKeyguardFeatures">Disable features in keyguard</string>
    <!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
    <string name="policydesc_disableKeyguardFeatures">Prevent use of some features in keyguard.</string>
    <!-- Title of policy access to start enforcing SELinux policy [CHAR LIMIT=30]-->
    <string name="policylab_enforceSelinux">Enforce SELinux</string>
    <!-- Description of policy access to start enforcing SELinux policy [CHAR LIMIT=110]-->
    <string name="policydesc_enforceSelinux">Toggle SELinux policy enforcing or permissive mode.</string>

    <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
    <!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
Loading