logd: enforce policy integrity

If a SELinux policy change or a switch to permissive mode is detected
on a user build, restart the device into safe mode, and keep it there
until an OTA is applied or user data is wiped.

This change deprecates the ro.logd.auditd property.

Needs matching changes from
  I781c3059ea8d4fb2f0c923e4488b1932d69678d3
  Ica825cf2af74f5624cf4091544bd24bb5482dbe7
  Id3ca7889ede30b54b7af73dd50653ca1a20d59aa

Bug: 26902605
Change-Id: Idcdc5bff133f13c1267f0ec0a75cc8cf1ddbda0d
(cherry picked from commit d122ee65b66b5b33d51302dabbaa0d6c84597549)
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 143fb04..fffc9ba 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -24,6 +24,7 @@
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <cutils/properties.h>
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -32,6 +33,10 @@
 #include "LogAudit.h"
 #include "LogKlog.h"
 
+#ifndef AUDITD_ENFORCE_INTEGRITY
+#define AUDITD_ENFORCE_INTEGRITY false
+#endif
+
 #define KMSG_PRIORITY(PRI)                          \
     '<',                                            \
     '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
@@ -43,11 +48,10 @@
         logbuf(buf),
         reader(reader),
         fdDmesg(fdDmesg),
+        policyLoaded(false),
+        rebootToSafeMode(false),
         initialized(false) {
-    static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
-        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
-        ' ', 's', 't', 'a', 'r', 't', '\n' };
-    write(fdDmesg, auditd_message, sizeof(auditd_message));
+    logToDmesg("start");
 }
 
 bool LogAudit::onDataAvailable(SocketClient *cli) {
@@ -73,6 +77,46 @@
     return true;
 }
 
+void LogAudit::logToDmesg(const std::string& str)
+{
+    static const char prefix[] = { KMSG_PRIORITY(LOG_INFO),
+        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
+        ' ', '\0' };
+    std::string message = prefix + str + "\n";
+    write(fdDmesg, message.c_str(), message.length());
+}
+
+std::string LogAudit::getProperty(const std::string& name)
+{
+    char value[PROP_VALUE_MAX] = {0};
+    property_get(name.c_str(), value, "");
+    return value;
+}
+
+void LogAudit::enforceIntegrity() {
+    if (!AUDITD_ENFORCE_INTEGRITY) {
+        logToDmesg("integrity enforcement suppressed; not rebooting");
+    } else if (rebootToSafeMode) {
+        if (getProperty("persist.sys.safemode") == "1") {
+            logToDmesg("integrity enforcement suppressed; in safe mode");
+            return;
+        }
+
+        logToDmesg("enforcing integrity; rebooting to safe mode");
+        property_set("persist.sys.safemode", "1");
+
+        std::string buildDate = getProperty("ro.build.date.utc");
+        if (!buildDate.empty()) {
+            property_set("persist.sys.audit_safemode", buildDate.c_str());
+        }
+
+        property_set("sys.powerctl", "reboot");
+    } else {
+        logToDmesg("enforcing integrity: rebooting to recovery");
+        property_set("sys.powerctl", "reboot,recovery");
+    }
+}
+
 int LogAudit::logPrint(const char *fmt, ...) {
     if (fmt == NULL) {
         return -EINVAL;
@@ -94,7 +138,27 @@
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
 
-    bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+    bool loaded = strstr(str, " policy loaded ");
+
+    if (loaded) {
+        if (policyLoaded) {
+            // SELinux policy changes are not allowed
+            enforceIntegrity();
+        } else {
+            logToDmesg("policy loaded");
+            policyLoaded = true;
+        }
+    }
+
+    bool permissive = strstr(str, " enforcing=0") ||
+                      strstr(str, " permissive=1");
+
+    if (permissive) {
+        // SELinux in permissive mode is not allowed
+        enforceIntegrity();
+    }
+
+    bool info = loaded || permissive;
     if ((fdDmesg >= 0) && initialized) {
         struct iovec iov[3];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };