Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4385343f authored by Robert Craig's avatar Robert Craig
Browse files

Allow PMS to restorecon directories under /data.



This change applies a relabel to both /data/data and
/data/user directories on boot. Not every boot will
apply this relabeling however. The appropriate
seapp_contexts is hashed and compared to
/data/system/seapp_hash to decide if the relabel
should occur.

Change-Id: I05e8b438950ddb908e46c9168ea6ee601e6d674f
Signed-off-by: default avatarrpcraig <rpcraig@tycho.ncsc.mil>
parent 7cb1dfe6
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -396,4 +396,8 @@ public final class Installer {


        return execute(builder.toString());
        return execute(builder.toString());
    }
    }

    public boolean restoreconData() {
        return (execute("restorecondata") == 0);
    }
}
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -1486,6 +1486,13 @@ public class PackageManagerService extends IPackageManager.Stub {
            // can downgrade to reader
            // can downgrade to reader
            mSettings.writeLPr();
            mSettings.writeLPr();


            if (SELinuxMMAC.shouldRestorecon()) {
                Slog.i(TAG, "Relabeling of /data/data and /data/user issued.");
                if (mInstaller.restoreconData()) {
                    SELinuxMMAC.setRestoreconDone();
                }
            }

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
                    SystemClock.uptimeMillis());


+97 −0
Original line number Original line Diff line number Diff line
@@ -25,11 +25,16 @@ import android.util.Xml;


import com.android.internal.util.XmlUtils;
import com.android.internal.util.XmlUtils;


import libcore.io.IoUtils;

import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


import java.util.HashMap;
import java.util.HashMap;


@@ -60,6 +65,13 @@ public final class SELinuxMMAC {
        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
        null};
        null};


    // Location of seapp_contexts policy file.
    private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";

    // Stores the hash of the last used seapp_contexts file.
    private static final String SEAPP_HASH_FILE =
            Environment.getDataDirectory().toString() + "/system/seapp_hash";

    // Signature policy stanzas
    // Signature policy stanzas
    static class Policy {
    static class Policy {
        private String seinfo;
        private String seinfo;
@@ -391,4 +403,89 @@ public final class SELinuxMMAC {


        return (sDefaultSeinfo != null);
        return (sDefaultSeinfo != null);
    }
    }

    /**
     * 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.
     */
    public static boolean shouldRestorecon() {
        // Any error with the seapp_contexts file should be fatal
        byte[] currentHash = null;
        try {
            currentHash = returnHash(SEAPP_CONTEXTS_FILE);
        } 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.e(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.", ioe);
        }

        return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
    }

    /**
     * Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.
     */
    public static void setRestoreconDone() {
        try {
            final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
            dumpHash(new File(SEAPP_HASH_FILE), currentHash);
        } catch (IOException ioe) {
            Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
        }
    }

    /**
     * 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.
     */
    private static void dumpHash(File file, byte[] content) throws IOException {
        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);
        }
    }

    /**
     * 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.
     */
    private static byte[] returnHash(String file) throws IOException {
        try {
            final byte[] contents = IoUtils.readFileAsByteArray(file);
            return MessageDigest.getInstance("SHA-1").digest(contents);
        } catch (NoSuchAlgorithmException nsae) {
            throw new RuntimeException(nsae);  // impossible
        }
    }
}
}