Extend init and ueventd for SE Android.

Add SE Android support for init and ueventd.

init:
- Load policy at boot.
- Set the security context for service daemons and their sockets.
- New built-in commands: setcon, setenforce, restorecon, setsebool.
- New option for services: seclabel.

ueventd:
- Set the security context for device directories and nodes.

Change-Id: I98ed752cde503c94d99dfa5b5a47e3c33db16aac
diff --git a/init/init.c b/init/init.c
index c3be93d..71c28b5 100755
--- a/init/init.c
+++ b/init/init.c
@@ -31,6 +31,13 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+
+#ifdef HAVE_SELINUX
+#include <sys/mman.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
 #include <libgen.h>
 
 #include <cutils/list.h>
@@ -52,6 +59,10 @@
 #include "util.h"
 #include "ueventd.h"
 
+#ifdef HAVE_SELINUX
+struct selabel_handle *sehandle;
+#endif
+
 static int property_triggers_enabled = 0;
 
 #if BOOTCHART
@@ -64,6 +75,11 @@
 static unsigned revision = 0;
 static char qemu[32];
 
+#ifdef HAVE_SELINUX
+static int selinux_enabled = 1;
+static int selinux_enforcing = 0;
+#endif
+
 static struct action *cur_action = NULL;
 static struct command *cur_command = NULL;
 static struct listnode *command_queue = NULL;
@@ -145,7 +161,10 @@
     pid_t pid;
     int needs_console;
     int n;
-
+#ifdef HAVE_SELINUX
+    char *scon = NULL;
+    int rc;
+#endif
         /* starting a service removes it from the disabled or reset
          * state and immediately takes it out of the restarting
          * state if it was in there
@@ -182,6 +201,34 @@
         return;
     }
 
+#ifdef HAVE_SELINUX
+    if (is_selinux_enabled() > 0) {
+        char *mycon = NULL, *fcon = NULL;
+
+        INFO("computing context for service '%s'\n", svc->args[0]);
+        rc = getcon(&mycon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
+        }
+
+        rc = getfilecon(svc->args[0], &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            freecon(mycon);
+            return;
+        }
+
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+        freecon(mycon);
+        freecon(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
+        }
+    }
+#endif
+
     NOTICE("starting '%s'\n", svc->name);
 
     pid = fork();
@@ -201,6 +248,10 @@
         for (ei = svc->envvars; ei; ei = ei->next)
             add_environment(ei->name, ei->value);
 
+#ifdef HAVE_SELINUX
+        setsockcreatecon(scon);
+#endif
+
         for (si = svc->sockets; si; si = si->next) {
             int socket_type = (
                     !strcmp(si->type, "stream") ? SOCK_STREAM :
@@ -212,6 +263,12 @@
             }
         }
 
+#ifdef HAVE_SELINUX
+        freecon(scon);
+        scon = NULL;
+        setsockcreatecon(NULL);
+#endif
+
         if (svc->ioprio_class != IoSchedClass_NONE) {
             if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                 ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
@@ -257,6 +314,15 @@
             }
         }
 
+#ifdef HAVE_SELINUX
+        if (svc->seclabel) {
+            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
+                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
+                _exit(127);
+            }
+        }
+#endif
+
         if (!dynamic_args) {
             if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                 ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
@@ -282,6 +348,10 @@
         _exit(127);
     }
 
+#ifdef HAVE_SELINUX
+    freecon(scon);
+#endif
+
     if (pid < 0) {
         ERROR("failed to start '%s'\n", svc->name);
         svc->pid = 0;
@@ -531,6 +601,14 @@
     *value++ = 0;
     if (name_len == 0) return;
 
+#ifdef HAVE_SELINUX
+    if (!strcmp(name,"enforcing")) {
+        selinux_enforcing = atoi(value);
+    } else if (!strcmp(name,"selinux")) {
+        selinux_enabled = atoi(value);
+    }
+#endif
+
     if (for_emulator) {
         /* in the emulator, export any kernel option with the
          * ro.kernel. prefix */
@@ -678,6 +756,97 @@
 }
 #endif
 
+#ifdef HAVE_SELINUX
+void selinux_load_policy(void)
+{
+    const char path_prefix[] = "/sepolicy";
+    struct selinux_opt seopts[] = {
+        { SELABEL_OPT_PATH, "/file_contexts" }
+    };
+    char path[PATH_MAX];
+    int fd, rc, vers;
+    struct stat sb;
+    void *map;
+
+    sehandle = NULL;
+    if (!selinux_enabled) {
+        INFO("SELinux:  Disabled by command line option\n");
+        return;
+    }
+
+    mkdir(SELINUXMNT, 0755);
+    if (mount("selinuxfs", SELINUXMNT, "selinuxfs", 0, NULL)) {
+        if (errno == ENODEV) {
+            /* SELinux not enabled in kernel */
+            return;
+        }
+        ERROR("SELinux:  Could not mount selinuxfs:  %s\n",
+              strerror(errno));
+        return;
+    }
+    set_selinuxmnt(SELINUXMNT);
+
+    vers = security_policyvers();
+    if (vers <= 0) {
+        ERROR("SELinux:  Unable to read policy version\n");
+        return;
+    }
+    INFO("SELinux:  Maximum supported policy version:  %d\n", vers);
+
+    snprintf(path, sizeof(path), "%s.%d",
+             path_prefix, vers);
+    fd = open(path, O_RDONLY);
+    while (fd < 0 && errno == ENOENT && --vers) {
+        snprintf(path, sizeof(path), "%s.%d",
+                 path_prefix, vers);
+        fd = open(path, O_RDONLY);
+    }
+    if (fd < 0) {
+        ERROR("SELinux:  Could not open %s:  %s\n",
+              path, strerror(errno));
+        return;
+    }
+    if (fstat(fd, &sb) < 0) {
+        ERROR("SELinux:  Could not stat %s:  %s\n",
+              path, strerror(errno));
+        return;
+    }
+    map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (map == MAP_FAILED) {
+        ERROR("SELinux:  Could not map %s:  %s\n",
+              path, strerror(errno));
+        return;
+    }
+
+    rc = security_load_policy(map, sb.st_size);
+    if (rc < 0) {
+        ERROR("SELinux:  Could not load policy:  %s\n",
+              strerror(errno));
+        return;
+    }
+
+    rc = security_setenforce(selinux_enforcing);
+    if (rc < 0) {
+        ERROR("SELinux:  Could not set enforcing mode to %s:  %s\n",
+              selinux_enforcing ? "enforcing" : "permissive", strerror(errno));
+        return;
+    }
+
+    munmap(map, sb.st_size);
+    close(fd);
+    INFO("SELinux: Loaded policy from %s\n", path);
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+    if (!sehandle) {
+        ERROR("SELinux:  Could not load file_contexts:  %s\n",
+              strerror(errno));
+        return;
+    }
+    INFO("SELinux: Loaded file contexts from %s\n", seopts[0].value);
+    return;
+}
+#endif
+
 int main(int argc, char **argv)
 {
     int fd_count = 0;
@@ -728,6 +897,11 @@
 
     process_kernel_cmdline();
 
+#ifdef HAVE_SELINUX
+    INFO("loading selinux policy\n");
+    selinux_load_policy();
+#endif
+
     is_charger = !strcmp(bootmode, "charger");
 
     INFO("property init\n");