am f7422885: Merge "Augment SELinuxMMAC functionality."

* commit 'f7422885a99c5d240f70c2f8227ae44abeea3e5c':
  Augment SELinuxMMAC functionality.
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 @@
     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 @@
         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 @@
     }
 
     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 @@
 
         Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
 
-        flushInstallPolicy();
-
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(policyFile);
@@ -138,57 +175,84 @@
                         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);
+                    // 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);
 
-                        // 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);
-
-                        sPackageSeinfo.put(pkgName, seinfo);
-                    }
                 } else {
                     XmlUtils.skipCurrentTag(parser);
-                    continue;
                 }
             }
         } catch (XmlPullParserException e) {
-            Slog.w(TAG, "Got execption parsing ", e);
+            // 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) {
-            Slog.w(TAG, "Got execption parsing ", e);
+            Slog.w(TAG, "Got exception parsing ", e);
+            return false;
+        } finally {
+            try {
+                policyFile.close();
+            } catch (IOException e) {
+                //omit
+            }
         }
-        try {
-            policyFile.close();
-        } catch (IOException e) {
-            //omit
-        }
+
+        flushInstallPolicy();
+        sSigSeinfo = sigSeinfo;
+        sDefaultSeinfo = defaultSeinfo;
+
         return true;
     }
 
+    private static Policy readPolicyTags(XmlPullParser parser) throws
+            IOException, XmlPullParserException {
+
+        int type;
+        int outerDepth = parser.getDepth();
+        Policy policy = new Policy();
+        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 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 {
 
@@ -205,19 +269,57 @@
 
             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 "
-                           + parser.getPositionDescription());
-                }
+                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 @@
      * 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 @@
                 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);
     }
 }