diff options
-rw-r--r-- | services/java/com/android/server/pm/SELinuxMMAC.java | 248 |
1 files changed, 174 insertions, 74 deletions
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java index 04f43d9..1d68afa 100644 --- a/services/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -48,12 +48,11 @@ public final class SELinuxMMAC { private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false; // Signature seinfo values read from policy. - private static final HashMap<Signature, String> sSigSeinfo = - new HashMap<Signature, String>(); + private static HashMap<Signature, Policy> sSigSeinfo = + new HashMap<Signature, Policy>(); - // Package name seinfo values read from policy. - private static final HashMap<String, String> sPackageSeinfo = - new HashMap<String, String>(); + // Default seinfo read from policy. + private static String sDefaultSeinfo = null; // Locations of potential install policy files. private static final File[] INSTALL_POLICY_FILE = { @@ -61,9 +60,45 @@ public final class SELinuxMMAC { new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"), null}; + // Signature policy stanzas + static class Policy { + private String seinfo; + private final HashMap<String, String> pkgMap; + + Policy() { + seinfo = null; + pkgMap = new HashMap<String, String>(); + } + + void putSeinfo(String seinfoValue) { + seinfo = seinfoValue; + } + + void putPkg(String pkg, String seinfoValue) { + pkgMap.put(pkg, seinfoValue); + } + + // Valid policy stanza means there exists a global + // seinfo value or at least one package policy. + boolean isValid() { + return (seinfo != null) || (!pkgMap.isEmpty()); + } + + String checkPolicy(String pkgName) { + // Check for package name seinfo value first. + String seinfoValue = pkgMap.get(pkgName); + if (seinfoValue != null) { + return seinfoValue; + } + + // Return the global seinfo value. + return seinfo; + } + } + private static void flushInstallPolicy() { sSigSeinfo.clear(); - sPackageSeinfo.clear(); + sDefaultSeinfo = null; } /** @@ -87,6 +122,10 @@ public final class SELinuxMMAC { } private static boolean readInstallPolicy(File[] policyFiles) { + // Temp structures to hold the rules while we parse the xml file. + // We add all the rules together once we know there's no structural problems. + HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>(); + String defaultSeinfo = null; FileReader policyFile = null; int i = 0; @@ -107,8 +146,6 @@ public final class SELinuxMMAC { Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath()); - flushInstallPolicy(); - try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(policyFile); @@ -138,63 +175,49 @@ public final class SELinuxMMAC { XmlUtils.skipCurrentTag(parser); continue; } - String seinfo = readSeinfoTag(parser); - if (seinfo != null) { - if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo=" - + seinfo); - - sSigSeinfo.put(signature, seinfo); + Policy policy = readPolicyTags(parser); + if (policy.isValid()) { + sigSeinfo.put(signature, policy); } } else if ("default".equals(tagName)) { - String seinfo = readSeinfoTag(parser); - if (seinfo != null) { - if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo); - - // The 'null' signature is the default seinfo value - sSigSeinfo.put(null, seinfo); - } - } 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; - } - String seinfo = readSeinfoTag(parser); - if (seinfo != null) { - if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "<package> tag: (" + pkgName + - ") assigned seinfo=" + seinfo); + // Value is null if default tag is absent or seinfo tag is malformed. + defaultSeinfo = readSeinfoTag(parser); + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "<default> tag assigned seinfo=" + defaultSeinfo); - sPackageSeinfo.put(pkgName, seinfo); - } } 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(); + // An error outside of a stanza means a structural problem + // with the xml file. So ignore it. + Slog.w(TAG, "Got exception parsing ", e); + return false; } catch (IOException e) { - //omit + Slog.w(TAG, "Got exception parsing ", e); + return false; + } finally { + try { + policyFile.close(); + } catch (IOException e) { + //omit + } } + + flushInstallPolicy(); + sSigSeinfo = sigSeinfo; + sDefaultSeinfo = defaultSeinfo; + return true; } - private static String readSeinfoTag(XmlPullParser parser) throws + private static Policy readPolicyTags(XmlPullParser parser) throws IOException, XmlPullParserException { int type; int outerDepth = parser.getDepth(); - String seinfo = null; + Policy policy = new Policy(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -205,19 +228,98 @@ public final class SELinuxMMAC { String tagName = parser.getName(); if ("seinfo".equals(tagName)) { - String seinfoValue = parser.getAttributeValue(null, "value"); - if (validateValue(seinfoValue)) { - seinfo = seinfoValue; - } else { - Slog.w(TAG, "<seinfo> without valid value at " + String seinfo = parseSeinfo(parser); + if (seinfo != null) { + policy.putSeinfo(seinfo); + } + XmlUtils.skipCurrentTag(parser); + } else if ("package".equals(tagName)) { + String pkg = parser.getAttributeValue(null, "name"); + if (!validatePackageName(pkg)) { + Slog.w(TAG, "<package> without valid name at " + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + + String seinfo = readSeinfoTag(parser); + if (seinfo != null) { + policy.putPkg(pkg, seinfo); } + } else { + XmlUtils.skipCurrentTag(parser); + } + } + return policy; + } + + private static String readSeinfoTag(XmlPullParser parser) throws + IOException, XmlPullParserException { + + int type; + int outerDepth = parser.getDepth(); + 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)) { + seinfo = parseSeinfo(parser); } XmlUtils.skipCurrentTag(parser); } return seinfo; } + private static String parseSeinfo(XmlPullParser parser) { + + String seinfoValue = parser.getAttributeValue(null, "value"); + if (!validateValue(seinfoValue)) { + Slog.w(TAG, "<seinfo> without valid value at " + + parser.getPositionDescription()); + seinfoValue = null; + } + return seinfoValue; + } + + /** + * General validation routine for package names. + * Returns a boolean indicating if the passed string + * is a valid android package name. + */ + private static boolean validatePackageName(String name) { + if (name == null) + return false; + + final int N = name.length(); + boolean hasSep = false; + boolean front = true; + for (int i=0; i<N; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + } + if (c == '.') { + hasSep = true; + front = true; + continue; + } + return false; + } + return hasSep; + } + /** * General validation routine for tag values. * Returns a boolean indicating if the passed string @@ -245,10 +347,11 @@ public final class SELinuxMMAC { * The label is attached to the ApplicationInfo instance of the package. * @param PackageParser.Package object representing the package * to labeled. - * @return String holding the value of the seinfo label that was assigned. - * Value may be null which indicates no seinfo label was assigned. + * @return boolean which determines whether a non null seinfo label + * was assigned to the package. A null value simply meaning that + * no policy matched. */ - public static void assignSeinfoValue(PackageParser.Package pkg) { + public static boolean assignSeinfoValue(PackageParser.Package pkg) { /* * Non system installed apps should be treated the same. This @@ -264,31 +367,28 @@ public final class SELinuxMMAC { if (s == null) continue; - if (sSigSeinfo.containsKey(s)) { - String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s); - if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "package (" + pkg.packageName + - ") labeled with seinfo=" + seinfo); + Policy policy = sSigSeinfo.get(s); + if (policy != null) { + String seinfo = policy.checkPolicy(pkg.packageName); + if (seinfo != null) { + pkg.applicationInfo.seinfo = seinfo; + if (DEBUG_POLICY_INSTALL) + Slog.i(TAG, "package (" + pkg.packageName + + ") labeled with seinfo=" + seinfo); - return; + return true; + } } } - - // Check for seinfo labeled by package. - if (sPackageSeinfo.containsKey(pkg.packageName)) { - String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName); - if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "package (" + pkg.packageName + - ") labeled with seinfo=" + seinfo); - return; - } } // If we have a default seinfo value then great, otherwise // we set a null object and that is what we started with. - String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null); + pkg.applicationInfo.seinfo = sDefaultSeinfo; if (DEBUG_POLICY_INSTALL) - Slog.i(TAG, "package (" + pkg.packageName + - ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo)); + Slog.i(TAG, "package (" + pkg.packageName + ") labeled with seinfo=" + + (sDefaultSeinfo == null ? "null" : sDefaultSeinfo)); + + return (sDefaultSeinfo != null); } } |