diff options
author | Robert Craig <rpcraig@tycho.ncsc.mil> | 2013-03-18 06:30:15 -0400 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-18 21:02:23 +0100 |
commit | 7e092967f946dd541429f422c0087e50504d2f37 (patch) | |
tree | 681187fd02a5c87fc440332845cb844ce8c46bbe /services/java/com/android | |
parent | c6767eb4b39784833d89d11683f854d55824c64c (diff) | |
download | frameworks_base-7e092967f946dd541429f422c0087e50504d2f37.zip frameworks_base-7e092967f946dd541429f422c0087e50504d2f37.tar.gz frameworks_base-7e092967f946dd541429f422c0087e50504d2f37.tar.bz2 |
Move seinfo and install-time changes to inside PMS.
Per request by Google, move the install-time checks entirely
inside PMS.
Change-Id: I01f2a9084dfe7886087b1497070b0d7f2ad8479e
Diffstat (limited to 'services/java/com/android')
3 files changed, 526 insertions, 19 deletions
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index de0ca83..df50a5e 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -45,7 +45,6 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; -import android.content.pm.SELinuxMMAC; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -259,6 +258,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private static final String MMAC_ENFORCE_PROPERTY = "persist.mmac.enforce"; + private static final String SEPOLICY_PATH_SEPOLICY = "/data/security/sepolicy"; private static final String SEPOLICY_PATH_PROPCTXS = "/data/security/property_contexts"; @@ -3036,12 +3037,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } - boolean systemState = SELinuxMMAC.getEnforcingMode(); + boolean systemState = SystemProperties.getBoolean(MMAC_ENFORCE_PROPERTY, false); boolean enforceMMAC = mmacAdmin.enforceMMAC; if (!firstBoot || !systemState) { if (systemState != enforceMMAC) { Slog.v(TAG, "Changed MMAC enforcing status " + systemState + " to " + enforceMMAC); - SELinuxMMAC.setEnforcingMode(enforceMMAC); + SystemProperties.set(MMAC_ENFORCE_PROPERTY, enforceMMAC ? "true" : "false"); } } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index b7a9a49..f0408d6 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -85,7 +85,6 @@ import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; -import android.content.pm.SELinuxMMAC; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.ManifestDigest; @@ -1041,6 +1040,8 @@ public class PackageManagerService extends IPackageManager.Stub { readPermissions(); + mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); + mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false), mSdkVersion, mOnlyCore); long startTime = SystemClock.uptimeMillis(); @@ -1164,13 +1165,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - // Find potential SELinux install policy - long startPolicyTime = SystemClock.uptimeMillis(); - mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); - Slog.i(TAG, "Time to scan SELinux install policy: " - + ((SystemClock.uptimeMillis()-startPolicyTime)/1000f) - + " seconds"); - // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( mFrameworkDir.getPath(), OBSERVER_EVENTS, true); @@ -3659,14 +3653,6 @@ public class PackageManagerService extends IPackageManager.Stub { } mScanningPath = scanFile; - if (mFoundPolicyFile && !SELinuxMMAC.passInstallPolicyChecks(pkg) && - SELinuxMMAC.getEnforcingMode()) { - Slog.w(TAG, "Installing application package " + pkg.packageName - + " failed due to policy."); - mLastScanError = PackageManager.INSTALL_FAILED_POLICY_REJECTED_PERMISSION; - return null; - } - if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; } @@ -3895,6 +3881,12 @@ public class PackageManagerService extends IPackageManager.Stub { if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } else if (mFoundPolicyFile && !SELinuxMMAC.passInstallPolicyChecks(pkg) && + SELinuxMMAC.getEnforcingMode()) { + Slog.w(TAG, "Installing application package " + pkg.packageName + + " failed due to policy."); + mLastScanError = PackageManager.INSTALL_FAILED_POLICY_REJECTED_PERMISSION; + return null; } pkg.applicationInfo.uid = pkgSetting.appId; diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java new file mode 100644 index 0000000..fbddba7 --- /dev/null +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageParser; +import android.content.pm.Signature; +import android.os.Environment; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.util.XmlUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.TreeSet; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Centralized access to SELinux MMAC (middleware MAC) implementation. + * {@hide} + */ +public final class SELinuxMMAC { + + private static final String TAG = "SELinuxMMAC"; + private static final String MMAC_DENY = "MMAC_DENIAL:"; + private static final String MMAC_ENFORCE_PROPERTY = "persist.mmac.enforce"; + private static final boolean DEBUG_POLICY = true; + private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false; + + // Signature based policy. + private static final HashMap<Signature, InstallPolicy> SIG_POLICY = + new HashMap<Signature, InstallPolicy>(); + + // Package name based policy. + private static final HashMap<String, InstallPolicy> PKG_POLICY = + new HashMap<String, InstallPolicy>(); + + // Locations of potential install policy files. + private static final File[] INSTALL_POLICY_FILE = { + new File(Environment.getDataDirectory(), "security/mac_permissions.xml"), + new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"), + null}; + + private static void flushInstallPolicy() { + SIG_POLICY.clear(); + PKG_POLICY.clear(); + } + + /** + * Parses an MMAC install policy from a predefined list of locations. + * @param none + * @return boolean indicating whether an install policy was correctly parsed. + */ + public static boolean readInstallPolicy() { + + return readInstallPolicy(INSTALL_POLICY_FILE); + } + + /** + * Returns the current status of MMAC enforcing mode. + * @param none + * @return boolean indicating whether or not the device is in enforcing mode. + */ + public static boolean getEnforcingMode() { + return SystemProperties.getBoolean(MMAC_ENFORCE_PROPERTY, false); + } + + /** + * Sets the current status of MMAC enforcing mode. + * @param boolean value to set the enforcing state too. + */ + public static void setEnforcingMode(boolean value) { + SystemProperties.set(MMAC_ENFORCE_PROPERTY, value ? "1" : "0"); + } + + /** + * Parses an MMAC install policy given as an argument. + * @param File object representing the path of the policy. + * @return boolean indicating whether the install policy was correctly parsed. + */ + public static boolean readInstallPolicy(File policyFile) { + + return readInstallPolicy(new File[]{policyFile,null}); + } + + private static boolean readInstallPolicy(File[] policyFiles) { + + FileReader policyFile = null; + int i = 0; + while (policyFile == null && policyFiles != null && policyFiles[i] != null) { + try { + policyFile = new FileReader(policyFiles[i]); + break; + } catch (FileNotFoundException e) { + Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath()); + } + i++; + } + + if (policyFile == null) { + Slog.d(TAG, "MMAC install disabled."); + return false; + } + + Slog.d(TAG, "MMAC install enabled using file " + policyFiles[i].getPath()); + + boolean enforcing = getEnforcingMode(); + String mode = enforcing ? "enforcing" : "permissive"; + Slog.d(TAG, "MMAC install starting in " + mode + " mode."); + + flushInstallPolicy(); + + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(policyFile); + + XmlUtils.beginDocument(parser, "policy"); + while (true) { + XmlUtils.nextElement(parser); + if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { + break; + } + + String tagName = parser.getName(); + if ("signer".equals(tagName)) { + String cert = parser.getAttributeValue(null, "signature"); + if (cert == null) { + Slog.w(TAG, "<signer> without signature at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + Signature signature; + try { + signature = new Signature(cert); + } catch (IllegalArgumentException e) { + Slog.w(TAG, "<signer> with bad signature at " + + parser.getPositionDescription(), e); + XmlUtils.skipCurrentTag(parser); + continue; + } + if (signature == null) { + Slog.w(TAG, "<signer> with null signature at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + InstallPolicy type = determineInstallPolicyType(parser, true); + if (type != null) { + if (DEBUG_POLICY_INSTALL) { + // Pretty print the cert + int rowLength = 75; + int certLength = cert.length(); + int rows = certLength / rowLength; + Slog.i(TAG, "<signer> tag:"); + for (int j = 0; j <= rows; j++) { + int start = rowLength * j; + int rowEndIndex = (rowLength * j) + rowLength; + int end = rowEndIndex < certLength ? rowEndIndex : certLength; + Slog.i(TAG, cert.substring(start, end)); + } + Slog.i(TAG, " Assigned: " + type); + } + + SIG_POLICY.put(signature, type); + } + } else if ("default".equals(tagName)) { + InstallPolicy type = determineInstallPolicyType(parser, true); + if (type != null) { + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "<default> tag assigned " + type); + + // The 'null' signature is the default seinfo value + SIG_POLICY.put(null, type); + } + } else if ("package".equals(tagName)) { + String pkgName = parser.getAttributeValue(null, "name"); + if (pkgName == null) { + Slog.w(TAG, "<package> without name at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + InstallPolicy type = determineInstallPolicyType(parser, false); + if (type != null) { + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "<package> outer tag: (" + pkgName + + ") assigned " + type); + + PKG_POLICY.put(pkgName, type); + } + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } catch (XmlPullParserException e) { + Slog.w(TAG, "Got execption parsing ", e); + } catch (IOException e) { + Slog.w(TAG, "Got execption parsing ", e); + } + try { + policyFile.close(); + } catch (IOException e) { + //omit + } + return true; + } + + private static InstallPolicy determineInstallPolicyType(XmlPullParser parser, + boolean notInsidePackageTag) throws + IOException, XmlPullParserException { + + final HashSet<String> denyPolicyPerms = new HashSet<String>(); + final HashSet<String> allowPolicyPerms = new HashSet<String>(); + + final HashMap<String, InstallPolicy> pkgPolicy = new HashMap<String, InstallPolicy>(); + + int type; + int outerDepth = parser.getDepth(); + boolean allowAll = false; + String seinfo = null; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if ("seinfo".equals(tagName)) { + String seinfoValue = parser.getAttributeValue(null, "value"); + if (seinfoValue != null) { + seinfo = seinfoValue; + } else { + Slog.w(TAG, "<seinfo> without value at " + + parser.getPositionDescription()); + } + } else if ("allow-permission".equals(tagName)) { + String permName = parser.getAttributeValue(null, "name"); + if (permName != null) { + allowPolicyPerms.add(permName); + } else { + Slog.w(TAG, "<allow-permission> without name at " + + parser.getPositionDescription()); + } + } else if ("deny-permission".equals(tagName)) { + String permName = parser.getAttributeValue(null, "name"); + if (permName != null) { + denyPolicyPerms.add(permName); + } else { + Slog.w(TAG, "<deny-permission> without name at " + + parser.getPositionDescription()); + } + } else if ("allow-all".equals(tagName)) { + allowAll = true; + } else if ("package".equals(tagName) && notInsidePackageTag) { + String pkgName = parser.getAttributeValue(null, "name"); + if (pkgName != null) { + InstallPolicy policyType = determineInstallPolicyType(parser, false); + if (policyType != null) { + pkgPolicy.put(pkgName, policyType); + if (DEBUG_POLICY_INSTALL) { + Slog.i(TAG, "<package> inner tag: (" + pkgName + + ") assigned " + policyType); + } + } + continue; + } else { + Slog.w(TAG, "<package> inner tag without name at " + + parser.getPositionDescription()); + } + } + XmlUtils.skipCurrentTag(parser); + } + + // Order is important. Provide the least amount of privilege. + InstallPolicy permPolicyType = null; + if (denyPolicyPerms.size() > 0) { + permPolicyType = new BlackListPolicy(denyPolicyPerms, pkgPolicy, seinfo); + } else if (allowPolicyPerms.size() > 0) { + permPolicyType = new WhiteListPolicy(allowPolicyPerms, pkgPolicy, seinfo); + } else if (allowAll) { + permPolicyType = new InstallPolicy(null, pkgPolicy, seinfo); + } else if (!pkgPolicy.isEmpty()) { + // Consider the case where outside tag has no perms attached + // but has an inner package stanza. All the above cases assume that + // the outer stanza has permission tags, but here we want to ensure + // we capture the inner but deny all outer. + permPolicyType = new DenyPolicy(null, pkgPolicy, seinfo); + } + + return permPolicyType; + } + + static class InstallPolicy { + + final HashSet<String> policyPerms; + final HashMap<String, InstallPolicy> pkgPolicy; + final private String seinfo; + + InstallPolicy(HashSet<String> policyPerms, HashMap<String, InstallPolicy> pkgPolicy, + String seinfo) { + + this.policyPerms = policyPerms; + this.pkgPolicy = pkgPolicy; + this.seinfo = seinfo; + } + + boolean passedPolicyChecks(PackageParser.Package pkg) { + // ensure that local package policy takes precedence + if (pkgPolicy.containsKey(pkg.packageName)) { + return pkgPolicy.get(pkg.packageName).passedPolicyChecks(pkg); + } + return true; + } + + String getSEinfo(String pkgName) { + if (pkgPolicy.containsKey(pkgName)) { + return pkgPolicy.get(pkgName).getSEinfo(pkgName); + } + return seinfo; + } + + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("["); + if (policyPerms != null) { + out.append(TextUtils.join(",\n", new TreeSet<String>(policyPerms))); + } else { + out.append("allow-all"); + } + out.append("]"); + return out.toString(); + } + } + + static class WhiteListPolicy extends InstallPolicy { + + WhiteListPolicy(HashSet<String> policyPerms, HashMap<String, InstallPolicy> pkgPolicy, + String seinfo) { + + super(policyPerms, pkgPolicy, seinfo); + } + + @Override + public boolean passedPolicyChecks(PackageParser.Package pkg) { + // ensure that local package policy takes precedence + if (pkgPolicy.containsKey(pkg.packageName)) { + return pkgPolicy.get(pkg.packageName).passedPolicyChecks(pkg); + } + + Iterator itr = pkg.requestedPermissions.iterator(); + while (itr.hasNext()) { + String perm = (String)itr.next(); + if (!policyPerms.contains(perm)) { + Slog.w(TAG, MMAC_DENY + " Policy whitelist rejected package " + + pkg.packageName + ". The rejected permission is " + perm + + " The maximal set allowed is: " + toString()); + return false; + } + } + return true; + } + + @Override + public String toString() { + return "allowed-permissions => \n" + super.toString(); + } + } + + static class BlackListPolicy extends InstallPolicy { + + BlackListPolicy(HashSet<String> policyPerms, HashMap<String, InstallPolicy> pkgPolicy, + String seinfo) { + + super(policyPerms, pkgPolicy, seinfo); + } + + @Override + public boolean passedPolicyChecks(PackageParser.Package pkg) { + // ensure that local package policy takes precedence + if (pkgPolicy.containsKey(pkg.packageName)) { + return pkgPolicy.get(pkg.packageName).passedPolicyChecks(pkg); + } + + Iterator itr = pkg.requestedPermissions.iterator(); + while (itr.hasNext()) { + String perm = (String)itr.next(); + if (policyPerms.contains(perm)) { + Slog.w(TAG, MMAC_DENY + " Policy blacklisted permission " + perm + + " for package " + pkg.packageName); + return false; + } + } + return true; + } + + @Override + public String toString() { + return "denied-permissions => \n" + super.toString(); + } + } + + static class DenyPolicy extends InstallPolicy { + + DenyPolicy(HashSet<String> policyPerms, HashMap<String, InstallPolicy> pkgPolicy, + String seinfo) { + + super(policyPerms, pkgPolicy, seinfo); + } + + @Override + public boolean passedPolicyChecks(PackageParser.Package pkg) { + // ensure that local package policy takes precedence + if (pkgPolicy.containsKey(pkg.packageName)) { + return pkgPolicy.get(pkg.packageName).passedPolicyChecks(pkg); + } + return false; + } + + @Override + public String toString() { + return "deny-all"; + } + } + + /** + * Detemines if the package passes policy. If the package does pass + * policy checks then an seinfo label is also assigned to the package. + * @param PackageParser.Package object representing the package + * to installed and labeled. + * @return boolean Indicates whether the package passed policy. + */ + public static boolean passInstallPolicyChecks(PackageParser.Package pkg) { + + // We just want one of the signatures to match. + for (Signature s : pkg.mSignatures) { + if (s == null) { + continue; + } + + // Check for a non default signature policy. + if (SIG_POLICY.containsKey(s)) { + InstallPolicy policy = SIG_POLICY.get(s); + if (policy.passedPolicyChecks(pkg)) { + String seinfo = pkg.applicationInfo.seinfo = policy.getSEinfo(pkg.packageName); + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "package (" + pkg.packageName + ") installed with " + + " seinfo=" + (seinfo == null ? "null" : seinfo)); + return true; + } + } + } + + // Check for a global per-package policy. + if (PKG_POLICY.containsKey(pkg.packageName)) { + boolean passed = false; + InstallPolicy policy = PKG_POLICY.get(pkg.packageName); + if (policy.passedPolicyChecks(pkg)) { + String seinfo = pkg.applicationInfo.seinfo = policy.getSEinfo(pkg.packageName); + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "package (" + pkg.packageName + ") installed with " + + " seinfo=" + (seinfo == null ? "null" : seinfo)); + passed = true; + } + return passed; + } + + // Check for a default policy. + if (SIG_POLICY.containsKey(null)) { + boolean passed = false; + InstallPolicy policy = SIG_POLICY.get(null); + if (policy.passedPolicyChecks(pkg)) { + String seinfo = pkg.applicationInfo.seinfo = policy.getSEinfo(pkg.packageName); + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "package (" + pkg.packageName + ") installed with " + + " seinfo=" + (seinfo == null ? "null" : seinfo)); + passed = true; + } + return passed; + } + + // If we get here it's because this package had no policy. + return false; + } +} |