Revert "Revert "Make init re-exec itself for its SELinux domain transition.""

This reverts commit 4217374611ada50f33aee544f015f6f9dfbf7ced.

It turns out that the kernel passes any unrecognized arguments on to init,
and (at least) N6 and N9 have such arguments. My lazy check of argc was
thus insufficient to recognize what stage of init we were in, so we'd
skip to stage 2 and not set up SELinux. And apparently you can get a
very long way with SELinux off... We'll fix that in a later change.

Bug: 19702273
Change-Id: I43b3fb722fed35dd217cb529cbcac9a29aff4e4b
diff --git a/init/init.cpp b/init/init.cpp
index b1d65db..661ee2f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -940,7 +940,13 @@
     return 0;
 }
 
-static void selinux_initialize() {
+static void security_failure() {
+    ERROR("Security failure; rebooting into recovery mode...\n");
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (true) { pause(); }  // never reached
+}
+
+static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
     selinux_callback cb;
@@ -953,19 +959,27 @@
         return;
     }
 
-    INFO("Loading SELinux policy...\n");
-    if (selinux_android_load_policy() < 0) {
-        ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
-        android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-        while (1) { pause(); }  // never reached
+    if (in_kernel_domain) {
+        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+            ERROR("couldn't write to /sys/fs/selinux/checkreqprot: %s\n",
+                  strerror(errno));
+            security_failure();
+        }
+
+        INFO("Loading SELinux policy...\n");
+        if (selinux_android_load_policy() < 0) {
+            ERROR("failed to load policy: %s\n", strerror(errno));
+            security_failure();
+        }
+
+        bool is_enforcing = selinux_is_enforcing();
+        security_setenforce(is_enforcing);
+
+        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+    } else {
+        selinux_init_all_handles();
     }
-
-    selinux_init_all_handles();
-    bool is_enforcing = selinux_is_enforcing();
-    INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
-    security_setenforce(is_enforcing);
-
-    NOTICE("(Initializing SELinux took %.2fs.)\n", t.duration());
 }
 
 int main(int argc, char** argv) {
@@ -1006,7 +1020,8 @@
     klog_init();
     klog_set_level(KLOG_NOTICE_LEVEL);
 
-    NOTICE("init started!\n");
+    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
 
     property_init();
 
@@ -1019,7 +1034,23 @@
     // used by init as well as the current required properties.
     export_kernel_boot_props();
 
-    selinux_initialize();
+    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+    selinux_initialize(is_first_stage);
+
+    // If we're in the kernel domain, re-exec init to transition to the init domain now
+    // that the SELinux policy has been loaded.
+    if (is_first_stage) {
+        if (restorecon("/init") == -1) {
+            ERROR("restorecon failed: %s\n", strerror(errno));
+            security_failure();
+        }
+        char* path = argv[0];
+        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+        if (execv(path, args) == -1) {
+            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+            security_failure();
+        }
+    }
 
     // These directories were necessarily created before initial policy load
     // and therefore need their security context restored to the proper value.