FileDocCategorySizeDatePackage
SELinuxMMAC.javaAPI DocAndroid 5.1 API17150Thu Mar 12 22:22:42 GMT 2015com.android.server.pm

SELinuxMMAC

public final class SELinuxMMAC extends Object
Centralized access to SELinux MMAC (middleware MAC) implementation. {@hide}

Fields Summary
private static final String
TAG
private static final boolean
DEBUG_POLICY
private static final boolean
DEBUG_POLICY_INSTALL
private static HashMap
sSigSeinfo
private static String
sDefaultSeinfo
private static final String
DATA_VERSION_FILE
private static final String
BASE_VERSION_FILE
private static final boolean
USE_OVERRIDE_POLICY
private static final String
DATA_MAC_PERMISSIONS
private static final String
BASE_MAC_PERMISSIONS
private static final String
MAC_PERMISSIONS
private static final String
DATA_SEAPP_CONTEXTS
private static final String
BASE_SEAPP_CONTEXTS
private static final String
SEAPP_CONTEXTS
private static final String
SEAPP_HASH_FILE
Constructors Summary
Methods Summary
public static booleanassignSeinfoValue(PackageParser.Package pkg)
Labels a package based on an seinfo tag from install policy. The label is attached to the ApplicationInfo instance of the package.

param
pkg object representing the package to be labeled.
return
boolean which determines whether a non null seinfo label was assigned to the package. A null value simply meaning that no policy matched.


        // We just want one of the signatures to match.
        for (Signature s : pkg.mSignatures) {
            if (s == null)
                continue;

            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 true;
                }
            }
        }

        // If we have a default seinfo value then great, otherwise
        // we set a null object and that is what we started with.
        pkg.applicationInfo.seinfo = sDefaultSeinfo;
        if (DEBUG_POLICY_INSTALL)
            Slog.i(TAG, "package (" + pkg.packageName + ") labeled with seinfo="
                   + (sDefaultSeinfo == null ? "null" : sDefaultSeinfo));

        return (sDefaultSeinfo != null);
    
private static voiddumpHash(java.io.File file, byte[] content)
Dump the contents of a byte array to a specified file.

param
file The file that receives the byte array content.
param
content A byte array that will be written to the specified file.
throws
IOException if any failed I/O operation occured. Included is the failure to atomically rename the tmp file used in the process.

        FileOutputStream fos = null;
        File tmp = null;
        try {
            tmp = File.createTempFile("seapp_hash", ".journal", file.getParentFile());
            tmp.setReadable(true);
            fos = new FileOutputStream(tmp);
            fos.write(content);
            fos.getFD().sync();
            if (!tmp.renameTo(file)) {
                throw new IOException("Failure renaming " + file.getCanonicalPath());
            }
        } finally {
            if (tmp != null) {
                tmp.delete();
            }
            IoUtils.closeQuietly(fos);
        }
    
private static voidflushInstallPolicy()

        sSigSeinfo.clear();
        sDefaultSeinfo = null;
    
private static java.lang.StringparseSeinfo(org.xmlpull.v1.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;
    
public static booleanreadInstallPolicy()

        // 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;
        try {
            policyFile = new FileReader(MAC_PERMISSIONS);
            Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);

            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;
                    }
                    Policy policy = readPolicyTags(parser);
                    if (policy.isValid()) {
                        sigSeinfo.put(signature, policy);
                    }
                } else if ("default".equals(tagName)) {
                    // 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);

                } else {
                    XmlUtils.skipCurrentTag(parser);
                }
            }
        } catch (XmlPullParserException xpe) {
            Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe);
            return false;
        } catch (IOException ioe) {
            Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe);
            return false;
        } finally {
            IoUtils.closeQuietly(policyFile);
        }

        flushInstallPolicy();
        sSigSeinfo = sigSeinfo;
        sDefaultSeinfo = defaultSeinfo;

        return true;
    
private static com.android.server.pm.SELinuxMMAC$PolicyreadPolicyTags(org.xmlpull.v1.XmlPullParser parser)


        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 java.lang.StringreadSeinfoTag(org.xmlpull.v1.XmlPullParser parser)


        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 byte[]returnHash(java.lang.String file)
Return the SHA-1 of a file.

param
file The path to the file given as a string.
return
Returns the SHA-1 of the file as a byte array.
throws
IOException if any failed I/O operations occured.

        try {
            final byte[] contents = IoUtils.readFileAsByteArray(file);
            return MessageDigest.getInstance("SHA-1").digest(contents);
        } catch (NoSuchAlgorithmException nsae) {
            throw new RuntimeException(nsae);  // impossible
        }
    
public static voidsetRestoreconDone()
Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.

        try {
            final byte[] currentHash = returnHash(SEAPP_CONTEXTS);
            dumpHash(new File(SEAPP_HASH_FILE), currentHash);
        } catch (IOException ioe) {
            Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
        }
    
public static booleanshouldRestorecon()
Determines if a recursive restorecon on /data/data and /data/user is needed. It does this by comparing the SHA-1 of the seapp_contexts file against the stored hash at /data/system/seapp_hash.

return
Returns true if the restorecon should occur or false otherwise.

        // Any error with the seapp_contexts file should be fatal
        byte[] currentHash = null;
        try {
            currentHash = returnHash(SEAPP_CONTEXTS);
        } catch (IOException ioe) {
            Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
            return false;
        }

        // Push past any error with the stored hash file
        byte[] storedHash = null;
        try {
            storedHash = IoUtils.readFileAsByteArray(SEAPP_HASH_FILE);
        } catch (IOException ioe) {
            Slog.w(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.");
        }

        return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
    
private static booleanuseOverridePolicy()

        try {
            final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE);
            final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE);
            if (overrideVersion.equals(baseVersion)) {
                return true;
            }
            Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " +
                   "base version '" + baseVersion + "'. Skipping override policy files.");
        } catch (FileNotFoundException fnfe) {
            // Override version file doesn't have to exist so silently ignore.
        } catch (IOException ioe) {
            Slog.w(TAG, "Skipping override policy files.", ioe);
        }
        return false;
    
private static booleanvalidatePackageName(java.lang.String name)
General validation routine for package names. Returns a boolean indicating if the passed string is a valid android package 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;
    
private static booleanvalidateValue(java.lang.String name)
General validation routine for tag values. Returns a boolean indicating if the passed string contains only letters or underscores.

        if (name == null)
            return false;

        final int N = name.length();
        if (N == 0)
            return false;

        for (int i = 0; i < N; i++) {
            final char c = name.charAt(i);
            if ((c < 'a" || c > 'z") && (c < 'A" || c > 'Z") && (c != '_")) {
                return false;
            }
        }
        return true;