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 /services | |
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
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/DevicePolicyManagerService.java | 666 |
1 files changed, 666 insertions, 0 deletions
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) |