diff options
author | Joman Chu <jcchu@tycho.ncsc.mil> | 2012-11-16 13:09:27 -0500 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-18 20:56:46 +0100 |
commit | ae74c84819ce2a174d3df98d9888206938b0ebee (patch) | |
tree | 9367007db64a5744f7966f6d18f5262df20b7c2c | |
parent | 88aeb650afb72aeff6335ebfc9bfee59ed5751c8 (diff) | |
download | frameworks_base-ae74c84819ce2a174d3df98d9888206938b0ebee.zip frameworks_base-ae74c84819ce2a174d3df98d9888206938b0ebee.tar.gz frameworks_base-ae74c84819ce2a174d3df98d9888206938b0ebee.tar.bz2 |
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
-rw-r--r-- | api/current.txt | 15 | ||||
-rw-r--r-- | core/java/android/app/admin/DeviceAdminInfo.java | 13 | ||||
-rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 297 | ||||
-rw-r--r-- | core/java/android/app/admin/IDevicePolicyManager.aidl | 13 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 4 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 2 | ||||
-rw-r--r-- | services/java/com/android/server/DevicePolicyManagerService.java | 666 |
7 files changed, 1009 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt index 0ff0fbc..e188121 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 } diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 66fc816..6849a96 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -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; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 719f050..a80898e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1421,6 +1421,303 @@ public class DevicePolicyManager { } /** + * 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 */ public void setActiveAdmin(ComponentName policyReceiver, boolean refreshing) { diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e061ab3..3e998fd 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -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); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e061032..f67e9b2 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -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. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e9aa1e4..06f9acb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -863,6 +863,8 @@ <java-symbol type="string" name="media_route_status_available" /> <java-symbol type="string" name="media_route_status_not_available" /> <java-symbol type="string" name="owner_name" /> + <java-symbol type="string" name="policylab_enforceSelinux" /> + <java-symbol type="string" name="policydesc_enforceSelinux" /> <java-symbol type="plurals" name="abbrev_in_num_days" /> <java-symbol type="plurals" name="abbrev_in_num_hours" /> diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 6a62809..911ca50 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -56,6 +56,7 @@ import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; @@ -79,9 +80,11 @@ import java.io.IOException; import java.io.PrintWriter; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -132,12 +135,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } + + /** Return the Admin that controls SELinux, or null if there is none. */ + ActiveAdmin findSELinuxAdminLocked() { + final int N = mAdminList.size(); + for (int i = 0; i < N; ++i) { + ActiveAdmin ap = mAdminList.get(i); + if (ap.isSELinuxAdmin) { + // Device admin controls SELinux + return ap; + } + } + // No device admin controls SELinux + return null; + } } final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>(); Handler mHandler = new Handler(); + Map<String, Boolean> seboolsOrig = null; + BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -172,6 +191,62 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } }; + private static abstract class PolicyFileDescription { + /** Path to the policy file */ + final String path; + + /** Admin has to be allowed to use this policy type before calling + * these functions. Typically {@link DeviceAdminInfo#USES_POLICY_ENFORCE_SELINUX} */ + final int reqPolicy; + + PolicyFileDescription(String _path, int _reqPolicy) { + path = _path; + reqPolicy = _reqPolicy; + } + + /** Does this admin have exclusive control of the policy */ + abstract boolean isPolicyAdmin(ActiveAdmin admin); + + /** Called after policy is written to the file system */ + abstract boolean doPolicyReload(); + } + + private static class SELinuxPolicyDescription extends PolicyFileDescription { + SELinuxPolicyDescription(String path) { + super(path, DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + } + + @Override + boolean isPolicyAdmin(ActiveAdmin admin) { + return admin.isSELinuxAdmin; + } + + @Override + boolean doPolicyReload() { + SystemProperties.set("selinux.reload_policy", "1"); + return true; + } + } + + private static final String SEPOLICY_PATH_SEPOLICY = "/data/system/sepolicy"; + + private static final String SEPOLICY_PATH_PROPCTXS = "/data/system/property_contexts"; + + private static final String SEPOLICY_PATH_FILECTXS = "/data/system/file_contexts"; + + private static final String SEPOLICY_PATH_SEAPPCTXS = "/data/system/seapp_contexts"; + + private static final PolicyFileDescription[] POLICY_DESCRIPTIONS = { + // 0 = SEPOLICY_FILE_SEPOLICY + new SELinuxPolicyDescription(SEPOLICY_PATH_SEPOLICY), + // 1 = SEPOLICY_FILE_PROPCTXS + new SELinuxPolicyDescription(SEPOLICY_PATH_PROPCTXS), + // 2 = SEPOLICY_FILE_FILECTXS + new SELinuxPolicyDescription(SEPOLICY_PATH_FILECTXS), + // 3 = SEPOLICY_FILE_SEAPPCTXS + new SELinuxPolicyDescription(SEPOLICY_PATH_SEAPPCTXS), + }; + static class ActiveAdmin { final DeviceAdminInfo info; @@ -224,8 +299,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String globalProxySpec = null; String globalProxyExclusionList = null; + boolean isSELinuxAdmin = false; + boolean enforceSELinux = false; + Map<String, Boolean> sebools = null; + + boolean[] isCustomPolicyFile = new boolean[DevicePolicyManager.SEPOLICY_FILE_COUNT]; + ActiveAdmin(DeviceAdminInfo _info) { info = _info; + if (info != null && getUserHandle().getIdentifier() == UserHandle.USER_OWNER) { + for (int i = 0; i < isCustomPolicyFile.length; ++i) { + isCustomPolicyFile[i] = false; + } + } else { + isCustomPolicyFile = null; + } } int getUid() { return info.getActivityInfo().applicationInfo.uid; } @@ -334,6 +422,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, "value", Integer.toString(disabledKeyguardFeatures)); out.endTag(null, "disable-keyguard-features"); } + if (isSELinuxAdmin) { + out.startTag(null, "selinux-admin"); + out.attribute(null, "value", Boolean.toString(isSELinuxAdmin)); + out.endTag(null, "selinux-admin"); + if (enforceSELinux) { + out.startTag(null, "enforce-selinux"); + out.attribute(null, "value", Boolean.toString(enforceSELinux)); + out.endTag(null, "enforce-selinux"); + } + Set<String> bools = sebools.keySet(); + for (String s : bools) { + out.startTag(null, "selinux-boolean"); + out.attribute(null, "name", s); + out.attribute(null, "value", sebools.get(s).toString()); + out.endTag(null, "selinux-boolean"); + } + boolean isCustomSELinux = isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY]; + if (isCustomSELinux) { + out.startTag(null, "selinux-sepolicy"); + out.attribute(null, "value", Boolean.toString(isCustomSELinux)); + out.endTag(null, "selinux-sepolicy"); + } + boolean isCustomPropCtxs = isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS]; + if (isCustomPropCtxs) { + out.startTag(null, "selinux-propctxs"); + out.attribute(null, "value", Boolean.toString(isCustomPropCtxs)); + out.endTag(null, "selinux-propctxs"); + } + boolean isCustomFileCtxs = isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS]; + if (isCustomFileCtxs) { + out.startTag(null, "selinux-filectxs"); + out.attribute(null, "value", Boolean.toString(isCustomFileCtxs)); + out.endTag(null, "selinux-filectxs"); + } + boolean isCustomSEAppCtxs = isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS]; + if (isCustomSEAppCtxs) { + out.startTag(null, "selinux-seappctxs"); + out.attribute(null, "value", Boolean.toString(isCustomSEAppCtxs)); + out.endTag(null, "selinux-seappctxs"); + } + } } void readFromXml(XmlPullParser parser) @@ -405,6 +534,32 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("disable-keyguard-features".equals(tag)) { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, "value")); + } else if ("selinux-admin".equals(tag)) { + isSELinuxAdmin = Boolean.parseBoolean( + parser.getAttributeValue(null, "value")); + if (isSELinuxAdmin) { + sebools = new HashMap<String, Boolean>(); + } + } else if ("enforce-selinux".equals(tag)) { + enforceSELinux = Boolean.parseBoolean( + parser.getAttributeValue(null, "value")); + } else if ("selinux-boolean".equals(tag)) { + sebools.put( + parser.getAttributeValue(null, "name"), + Boolean.parseBoolean( + parser.getAttributeValue(null, "value"))); + } else if ("selinux-sepolicy".equals(tag)) { + this.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY] = + Boolean.parseBoolean(parser.getAttributeValue(null, "value")); + } else if ("selinux-propctxs".equals(tag)) { + this.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS] = + Boolean.parseBoolean(parser.getAttributeValue(null, "value")); + } else if ("selinux-filectxs".equals(tag)) { + this.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS] = + Boolean.parseBoolean(parser.getAttributeValue(null, "value")); + } else if ("selinux-seappctxs".equals(tag)) { + this.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS] = + Boolean.parseBoolean(parser.getAttributeValue(null, "value")); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } @@ -463,6 +618,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(disableCamera); pw.print(prefix); pw.print("disabledKeyguardFeatures="); pw.println(disabledKeyguardFeatures); + pw.print(prefix); pw.print("isSELinuxAdmin="); + pw.println(isSELinuxAdmin); + pw.print(prefix); pw.print("enforceSELinux="); + pw.println(enforceSELinux); + pw.print(prefix); pw.print("sebools="); + pw.println(sebools); + pw.print(prefix); pw.print("customSELinuxPolicy="); + pw.println(isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY]); + pw.print(prefix); pw.print("customPropertyContexts="); + pw.println(isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS]); + pw.print(prefix); pw.print("customFileContexts="); + pw.println(isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS]); + pw.print(prefix); pw.print("customSEappContexts="); + pw.println(isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS]); } } @@ -689,6 +858,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DevicePolicyData policy = getUserData(userHandle); boolean doProxyCleanup = admin.info.usesPolicy( DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); + boolean doSELinuxCleanup = admin.info.usesPolicy( + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX) && admin.isSELinuxAdmin; policy.mAdminList.remove(admin); policy.mAdminMap.remove(adminReceiver); validatePasswordOwnerLocked(policy); @@ -696,6 +867,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (doProxyCleanup) { resetGlobalProxyLocked(getUserData(userHandle)); } + if (doSELinuxCleanup) { + syncSELinuxPolicyLocked(policy, + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY], + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS], + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS], + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS]); + } saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(policy); } @@ -935,6 +1113,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { validatePasswordOwnerLocked(policy); syncDeviceCapabilitiesLocked(policy); updateMaximumTimeToLockLocked(policy); + syncSELinuxPolicyLocked(policy, false); } static void validateQualityConstant(int quality) { @@ -992,7 +1171,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void systemReady() { + assert DevicePolicyManager.SEPOLICY_FILE_COUNT == POLICY_DESCRIPTIONS.length; synchronized (this) { + saveOriginalSELinuxSettings(); loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER); } } @@ -2313,10 +2494,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) { enforceCrossUserPermission(userHandle); + synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES); if (ap.disabledKeyguardFeatures != which) { @@ -2364,6 +2547,489 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private void saveOriginalSELinuxSettings() { + // SELinux booleans + String[] seboolNames = SELinux.getBooleanNames(); + seboolsOrig = new HashMap<String, Boolean>(seboolNames.length); + for (String sebool : seboolNames) { + boolean value = SELinux.getBooleanValue(sebool); + seboolsOrig.put(sebool, value); + } + seboolsOrig = Collections.unmodifiableMap(seboolsOrig); + } + + // Possible SELinux Admin API states: + // 1: Caller has ENFORCE_SELINUX = {T,F} + // 2: Caller is a SELinux admin = {T,F} + // 3: There is a SELinux admin on the system = {T,F} + // Invariants: + // a) 1=F -> 2=F + // b) 3=F -> 2=F for all admin apps + // States: + // TTT, TTF, TFT, TFF, FTT, FTF, FFT, FFF + // TTT, TFT, TFF, FFT, FFF + // TTF fails b, + // FTT fails a + // FTF fails a,b + + // Cases = 16 + @Override + public boolean isSELinuxAdmin(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + //Case F** = 4 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + //Case T** = 4 + return admin.isSELinuxAdmin; + } + } + + // Cases = 16 + @Override + public boolean setSELinuxAdmin(ComponentName who, boolean control, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + // Case F**(*) = 8 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + + // Case TT*(T) = 2 + // Case TF*(F) = 2 + if (admin.isSELinuxAdmin == control) { + return true; + } + + DevicePolicyData policy = getUserData(userHandle); + ActiveAdmin curAdmin = policy.findSELinuxAdminLocked(); + + // Case TFF(T) = 1 + if (control && curAdmin == null) { + Slog.v(TAG, "SELinux admin set to " + admin.info.getComponent()); + admin.isSELinuxAdmin = true; + + admin.sebools = new HashMap<String, Boolean>(seboolsOrig.size()); + Set<String> seboolnames = seboolsOrig.keySet(); + for (String sebool : seboolnames) { + boolean value = seboolsOrig.get(sebool); + admin.sebools.put(sebool, value); + } + + saveSettingsLocked(userHandle); + return true; + } + + // Case TTT(F) = 1 + if (!control && curAdmin.equals(admin)) { + boolean setSEpolicyFile = admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY]; + boolean setPropertyContextsFile = admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS]; + boolean setFileContextsFile = admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS]; + boolean setSEappContextsFile = admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS]; + + Slog.v(TAG, admin.info.getComponent() + " is no longer a SELinux admin"); + + admin.isSELinuxAdmin = false; + admin.enforceSELinux = false; + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEPOLICY] = false; + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_PROPCTXS] = false; + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_FILECTXS] = false; + admin.isCustomPolicyFile[DevicePolicyManager.SEPOLICY_FILE_SEAPPCTXS] = false; + + saveSettingsLocked(userHandle); + syncSELinuxPolicyLocked(policy, setSEpolicyFile, + setPropertyContextsFile, setFileContextsFile, + setSEappContextsFile); + return true; + } + + //Case TTF(F) = 1 + //Case TFT(T) = 1 + return false; + } + } + + /** Resets the state the SELinux values in an ActiveAdmin to the current state of system */ + private static void resetSELinuxAdmin(ActiveAdmin admin) { + String[] seboolsnames = SELinux.getBooleanNames(); + admin.enforceSELinux = SELinux.isSELinuxEnforced(); + admin.sebools = new HashMap<String, Boolean>(seboolsnames.length); + for (String bool : seboolsnames) { + admin.sebools.put(bool, SELinux.getBooleanValue(bool)); + } + } + + private boolean syncSELinuxPolicyLocked(DevicePolicyData policy, boolean removeAllPolicy) { + return syncSELinuxPolicyLocked(policy, removeAllPolicy, removeAllPolicy, + removeAllPolicy, removeAllPolicy); + } + + /** + * Sync's the current SELinux admin's policies to the device. If there is + * no SELinux admin, then this will set SELinux to permissive mode, + * restore the SELinux boolean values from when the system booted, + * and may remove the {@link SELINUX_POLICY_PATH}, + * {@link PROPERTY_CONTEXTS_PATH}, {@link FILE_CONTEXTS_PATH}, and + * {@link SEAPP_CONTEXTS_PATH} files. + * @return true if policies were synced successfully + */ + private boolean syncSELinuxPolicyLocked(DevicePolicyData policy, + boolean removeSELinuxPolicy, boolean removePropertyContexts, + boolean removeFileContexts, boolean removeSEappContexts) { + if (SELinux.isSELinuxEnabled() && policy.mUserHandle == UserHandle.USER_OWNER) { + ActiveAdmin selinuxAdmin = policy.findSELinuxAdminLocked(); + if (selinuxAdmin == null) { + // No admin, so create a fake one and restore it. + selinuxAdmin = new ActiveAdmin(null); + selinuxAdmin.sebools = seboolsOrig; + selinuxAdmin.enforceSELinux = false; + } + + boolean systemState = SELinux.isSELinuxEnforced(); + boolean desiredState = selinuxAdmin.enforceSELinux; + if (systemState != desiredState) { + Slog.v(TAG, "SELinux enforcing was " + systemState + ", to be set to " + desiredState); + boolean res = SELinux.setSELinuxEnforce(desiredState); + Slog.v(TAG, "Change in SELinux enforcing state " + (res ? "succeeded" : "failed")); + if (res == false) { + // this really shouldn't ever happen + if (selinuxAdmin.info != null) { // null is fake ActiveAdmin + resetSELinuxAdmin(selinuxAdmin); + } + return false; + } + } + + Set<String> sebools = selinuxAdmin.sebools.keySet(); + for (String sebool : sebools) { + systemState = SELinux.getBooleanValue(sebool); + desiredState = selinuxAdmin.sebools.get(sebool); + if (systemState != desiredState) { + Slog.v(TAG, "SELinux boolean " + sebool + " was " + systemState + ", to be set to " + desiredState); + boolean res = SELinux.setBooleanValue(sebool, desiredState); + Slog.v(TAG, "Change in SELinux boolean " + sebool + " " + (res ? "succeeded" : "failed")); + if (res == false) { + // this really shouldn't ever happen + if (selinuxAdmin.info != null) { // null is fake ActiveAdmin + resetSELinuxAdmin(selinuxAdmin); + } + return false; + } + } + } + + if (removeSELinuxPolicy || removePropertyContexts + || removeFileContexts || removeSEappContexts) { + boolean ret = true; + File polfile; + polfile = new File(SEPOLICY_PATH_SEPOLICY); + if (removeSELinuxPolicy && polfile.exists() && !polfile.delete()) { + ret = false; + } + polfile = new File(SEPOLICY_PATH_PROPCTXS); + if (removePropertyContexts && polfile.exists() && !polfile.delete()) { + ret = false; + } + polfile = new File(SEPOLICY_PATH_FILECTXS); + if (removeFileContexts && polfile.exists() && !polfile.delete()) { + ret = false; + } + polfile = new File(SEPOLICY_PATH_SEAPPCTXS); + if (removeSEappContexts && polfile.exists() && !polfile.delete()) { + ret = false; + } + SystemProperties.set("selinux.reload_policy", "1"); + return ret; + + } else { //Not removing any policy files + return true; + } + + } else { //SELinux not enabled or user is not owner + return false; + } + } + + @Override + public boolean getSELinuxEnforcing(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + // Case: F** = 4 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + // Case: T** = 4 + return admin.isSELinuxAdmin && admin.enforceSELinux; + } + } + + @Override + public boolean setSELinuxEnforcing(ComponentName who, boolean enforcing, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + // Case F**(*) = 8 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + + // Case TF*(*) = 4 + if (!admin.isSELinuxAdmin) { + return false; + } + + // Case TT*(*) = 4 + if (admin.enforceSELinux != enforcing) { + admin.enforceSELinux = enforcing; + saveSettingsLocked(userHandle); + } + DevicePolicyData policy = getUserData(userHandle); + return syncSELinuxPolicyLocked(policy, false); + } + } + + @Override + public List<String> getSELinuxBooleanNames(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return null; + } + // Case F** = 4 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + + // Case TF* = 2 + if (!admin.isSELinuxAdmin) { + return null; + } + + // Case TT* = 2 + return new ArrayList<String>(admin.sebools.keySet()); + } + } + + @Override + public boolean getSELinuxBooleanValue(ComponentName who, String name, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + // Case F** = 4 + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + + // Case TF* = 2 + if (!admin.isSELinuxAdmin) { + return false; + } + + // Case TT* = 2 + return admin.sebools.containsKey(name) && admin.sebools.get(name); + } + } + + @Override + public boolean setSELinuxBooleanValue(ComponentName who, String name, boolean value, + int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_ENFORCE_SELINUX); + + if (!admin.isSELinuxAdmin) { + return false; + } + + if (!admin.sebools.containsKey(name)) { + throw new IllegalArgumentException(name + " is not a valid SELinux boolean"); + } + + if (value != admin.sebools.put(name, value)) { + saveSettingsLocked(userHandle); + } + DevicePolicyData policy = getUserData(userHandle); + return syncSELinuxPolicyLocked(policy, false); + } + } + + @Override + public boolean isCustomPolicyFile(ComponentName who, int policyType, int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + if (policyType >= DevicePolicyManager.SEPOLICY_FILE_COUNT) { + throw new IllegalArgumentException("policyType is unknown"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + PolicyFileDescription desc = POLICY_DESCRIPTIONS[policyType]; + ActiveAdmin admin = getActiveAdminForCallerLocked(who, desc.reqPolicy); + return desc.isPolicyAdmin(admin) && admin.isCustomPolicyFile[policyType]; + } + } + + @Override + public boolean setCustomPolicyFile(ComponentName who, int policyType, byte[] policy, + int userHandle) { + enforceCrossUserPermission(userHandle); + synchronized (this) { + // Check for permissions + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + if (policyType >= DevicePolicyManager.SEPOLICY_FILE_COUNT) { + throw new IllegalArgumentException("policyType is unknown"); + } + // Only owner can set SELinux settings + if (userHandle != UserHandle.USER_OWNER + || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + Slog.w(TAG, "Only owner is allowed to set SELinux settings. User " + + UserHandle.getCallingUserId() + " is not permitted."); + return false; + } + PolicyFileDescription desc = POLICY_DESCRIPTIONS[policyType]; + File polFile = new File(desc.path); + File polFileTmp = new File(desc.path + ".tmp"); + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + desc.reqPolicy); + if (!desc.isPolicyAdmin(admin)) { + return false; + } + + boolean newPolicy = policy != null; + if (newPolicy != admin.isCustomPolicyFile[policyType]) { + admin.isCustomPolicyFile[policyType] = newPolicy; + saveSettingsLocked(userHandle); + } + + boolean ret = writePolicyFile(polFile, polFileTmp, policy); + desc.doPolicyReload(); + return ret; + } + } + + /* Are there better ways than passing a byte[]? This might involve a lot + * of copying. Can we pass file descriptors? Can we pass a path (and what + * are the security implications)? If SELinux is enforcing, can system + * domain access another app's files? + * + * byte[] allows admin apps to set a policy without ever having to write + * the file to to storage, eg an admin app receiving a new policy file over + * data connection. + * + * We don't need to save this policy file somewhere in the ActiveAdmin/xml + * because it's written to /data/system, which is persistent. + */ + private boolean writePolicyFile(File policyFile, File tempPolicyFile, byte[] policy) { + if (policy == null) { + if (policyFile.exists() && !policyFile.delete()) { + return false; + } + return true; + } else { + if (policy.length == 0) { + return false; + } + JournaledFile journal = new JournaledFile(policyFile, tempPolicyFile); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(journal.chooseForWrite(), false); + stream.write(policy); + stream.flush(); + stream.close(); + journal.commit(); + } catch (IOException err) { + if (stream != null) { + try { + stream.close(); + } catch (IOException ex) { + //ignore + } + } + journal.rollback(); + return false; + } + return true; + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) |