Merge "Allow PMS to restorecon directories under /data."
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 54acda2..4b087cc 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -396,4 +396,8 @@
return execute(builder.toString());
}
+
+ public boolean restoreconData() {
+ return (execute("restorecondata") == 0);
+ }
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 1bac1db..5dded57 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1486,6 +1486,13 @@
// can downgrade to reader
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,
SystemClock.uptimeMillis());
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java
index 1d68afa..91de6d8 100644
--- a/services/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/java/com/android/server/pm/SELinuxMMAC.java
@@ -25,11 +25,16 @@
import com.android.internal.util.XmlUtils;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@@ -60,6 +65,13 @@
new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
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
static class Policy {
private String seinfo;
@@ -391,4 +403,89 @@
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
+ }
+ }
}