summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoman Chu <jcchu@tycho.ncsc.mil>2012-11-16 13:09:27 -0500
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2013-07-18 20:56:46 +0100
commitae74c84819ce2a174d3df98d9888206938b0ebee (patch)
tree9367007db64a5744f7966f6d18f5262df20b7c2c
parent88aeb650afb72aeff6335ebfc9bfee59ed5751c8 (diff)
downloadframeworks_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.txt15
-rw-r--r--core/java/android/app/admin/DeviceAdminInfo.java13
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java297
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl13
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java666
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)