am ee79b455: am 93553073: Merge "Extend init and ueventd for SE Android."

* commit 'ee79b455a904558f74d63a99ade8f3f1197a5de5':
  Extend init and ueventd for SE Android.
diff --git a/init/Android.mk b/init/Android.mk
index 162c226..e9fd884 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -30,6 +30,12 @@
 
 LOCAL_STATIC_LIBRARIES := libcutils libc
 
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_C_INCLUDES := external/libselinux/include
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
 include $(BUILD_EXECUTABLE)
 
 # Make a symlink from /sbin/ueventd to /init
diff --git a/init/builtins.c b/init/builtins.c
index 3781dcd..9aa2345 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -33,6 +33,11 @@
 #include <cutils/partition_utils.h>
 #include <sys/system_properties.h>
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
 #include "init.h"
 #include "keywords.h"
 #include "property_service.h"
@@ -436,6 +441,28 @@
 
 }
 
+int do_setcon(int nargs, char **args) {
+#ifdef HAVE_SELINUX
+    if (is_selinux_enabled() <= 0)
+        return 0;
+    if (setcon(args[1]) < 0) {
+        return -errno;
+    }
+#endif
+    return 0;
+}
+
+int do_setenforce(int nargs, char **args) {
+#ifdef HAVE_SELINUX
+    if (is_selinux_enabled() <= 0)
+        return 0;
+    if (security_setenforce(atoi(args[1])) < 0) {
+        return -errno;
+    }
+#endif
+    return 0;
+}
+
 int do_setkey(int nargs, char **args)
 {
     struct kbentry kbe;
@@ -649,6 +676,64 @@
     return 0;
 }
 
+int do_restorecon(int nargs, char **args) {
+#ifdef HAVE_SELINUX
+    char *secontext = NULL;
+    struct stat sb;
+    int i;
+
+    if (is_selinux_enabled() <= 0 || !sehandle)
+        return 0;
+
+    for (i = 1; i < nargs; i++) {
+        if (lstat(args[i], &sb) < 0)
+            return -errno;
+        if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0)
+            return -errno;
+        if (lsetfilecon(args[i], secontext) < 0) {
+            freecon(secontext);
+            return -errno;
+        }
+        freecon(secontext);
+    }
+#endif
+    return 0;
+}
+
+int do_setsebool(int nargs, char **args) {
+#ifdef HAVE_SELINUX
+    SELboolean *b = alloca(nargs * sizeof(SELboolean));
+    char *v;
+    int i;
+
+    if (is_selinux_enabled() <= 0)
+        return 0;
+
+    for (i = 1; i < nargs; i++) {
+        char *name = args[i];
+        v = strchr(name, '=');
+        if (!v) {
+            ERROR("setsebool: argument %s had no =\n", name);
+            return -EINVAL;
+        }
+        *v++ = 0;
+        b[i-1].name = name;
+        if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
+            b[i-1].value = 1;
+        else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
+            b[i-1].value = 0;
+        else {
+            ERROR("setsebool: invalid value %s\n", v);
+            return -EINVAL;
+        }
+    }
+
+    if (security_set_boolean_list(nargs - 1, b, 0) < 0)
+        return -errno;
+#endif
+    return 0;
+}
+
 int do_loglevel(int nargs, char **args) {
     if (nargs == 2) {
         klog_set_level(atoi(args[1]));
diff --git a/init/devices.c b/init/devices.c
index a2f84aa..3b4d369 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -29,6 +29,12 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <linux/netlink.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
 #include <private/android_filesystem_config.h>
 #include <sys/time.h>
 #include <asm/page.h>
@@ -45,6 +51,10 @@
 #define FIRMWARE_DIR1   "/etc/firmware"
 #define FIRMWARE_DIR2   "/vendor/firmware"
 
+#ifdef HAVE_SELINUX
+static struct selabel_handle *sehandle;
+#endif
+
 static int device_fd = -1;
 
 struct uevent {
@@ -180,8 +190,17 @@
     unsigned gid;
     mode_t mode;
     dev_t dev;
+#ifdef HAVE_SELINUX
+    char *secontext = NULL;
+#endif
 
     mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+#ifdef HAVE_SELINUX
+    if (sehandle) {
+        selabel_lookup(sehandle, &secontext, path, mode);
+        setfscreatecon(secontext);
+    }
+#endif
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
      * device node. Unforunately changing the euid would prevent creation of
@@ -192,8 +211,40 @@
     mknod(path, mode, dev);
     chown(path, uid, -1);
     setegid(AID_ROOT);
+#ifdef HAVE_SELINUX
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(NULL);
+    }
+#endif
 }
 
+
+static int make_dir(const char *path, mode_t mode)
+{
+    int rc;
+
+#ifdef HAVE_SELINUX
+    char *secontext = NULL;
+
+    if (sehandle) {
+        selabel_lookup(sehandle, &secontext, path, mode);
+        setfscreatecon(secontext);
+    }
+#endif
+
+    rc = mkdir(path, mode);
+
+#ifdef HAVE_SELINUX
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(NULL);
+    }
+#endif
+    return rc;
+}
+
+
 static void add_platform_device(const char *name)
 {
     int name_len = strlen(name);
@@ -506,7 +557,7 @@
         return;
 
     snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-    mkdir(base, 0755);
+    make_dir(base, 0755);
 
     if (!strncmp(uevent->path, "/devices/platform/", 18))
         links = parse_platform_block_device(uevent);
@@ -535,10 +586,10 @@
              int bus_id = uevent->minor / 128 + 1;
              int device_id = uevent->minor % 128 + 1;
              /* build directories */
-             mkdir("/dev/bus", 0755);
-             mkdir("/dev/bus/usb", 0755);
+             make_dir("/dev/bus", 0755);
+             make_dir("/dev/bus/usb", 0755);
              snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
-             mkdir(devpath, 0755);
+             make_dir(devpath, 0755);
              snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
          } else {
              /* ignore other USB events */
@@ -546,29 +597,29 @@
          }
      } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
          base = "/dev/graphics/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
          base = "/dev/oncrpc/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
          base = "/dev/adsp/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
          base = "/dev/msm_camera/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if(!strncmp(uevent->subsystem, "input", 5)) {
          base = "/dev/input/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
          base = "/dev/mtd/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if(!strncmp(uevent->subsystem, "sound", 5)) {
          base = "/dev/snd/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
      } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                  !strncmp(name, "log_", 4)) {
          base = "/dev/log/";
-         mkdir(base, 0755);
+         make_dir(base, 0755);
          name += 4;
      } else
          base = "/dev/";
@@ -819,7 +870,14 @@
     suseconds_t t0, t1;
     struct stat info;
     int fd;
+#ifdef HAVE_SELINUX
+    struct selinux_opt seopts[] = {
+        { SELABEL_OPT_PATH, "/file_contexts" }
+    };
 
+    if (is_selinux_enabled() > 0)
+        sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+#endif
     /* is 64K enough? udev uses 16MB! */
     device_fd = uevent_open_socket(64*1024, true);
     if(device_fd < 0)
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");
diff --git a/init/init.h b/init/init.h
index a91d9d4..58bbbfe 100644
--- a/init/init.h
+++ b/init/init.h
@@ -95,6 +95,10 @@
     gid_t supp_gids[NR_SVC_SUPP_GIDS];
     size_t nr_supp_gids;
 
+#ifdef HAVE_SELINUX
+    char *seclabel;
+#endif
+
     struct socketinfo *sockets;
     struct svcenvinfo *envvars;
 
@@ -132,4 +136,8 @@
 
 int load_565rle_image( char *file_name );
 
+#ifdef HAVE_SELINUX
+extern struct selabel_handle *sehandle;
+#endif
+
 #endif	/* _INIT_INIT_H */
diff --git a/init/init_parser.c b/init/init_parser.c
index d255db9..f538450 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -131,15 +131,20 @@
         break;
     case 'r':
         if (!strcmp(s, "estart")) return K_restart;
+        if (!strcmp(s, "estorecon")) return K_restorecon;
         if (!strcmp(s, "mdir")) return K_rmdir;
         if (!strcmp(s, "m")) return K_rm;
         break;
     case 's':
+        if (!strcmp(s, "eclabel")) return K_seclabel;
         if (!strcmp(s, "ervice")) return K_service;
+        if (!strcmp(s, "etcon")) return K_setcon;
+        if (!strcmp(s, "etenforce")) return K_setenforce;
         if (!strcmp(s, "etenv")) return K_setenv;
         if (!strcmp(s, "etkey")) return K_setkey;
         if (!strcmp(s, "etprop")) return K_setprop;
         if (!strcmp(s, "etrlimit")) return K_setrlimit;
+        if (!strcmp(s, "etsebool")) return K_setsebool;
         if (!strcmp(s, "ocket")) return K_socket;
         if (!strcmp(s, "tart")) return K_start;
         if (!strcmp(s, "top")) return K_stop;
@@ -792,6 +797,16 @@
             svc->uid = decode_uid(args[1]);
         }
         break;
+    case K_seclabel:
+#ifdef HAVE_SELINUX
+        if (nargs != 2) {
+            parse_error(state, "seclabel option requires a label string\n");
+        } else {
+            svc->seclabel = args[1];
+        }
+#endif
+        break;
+
     default:
         parse_error(state, "invalid option '%s'\n", args[0]);
     }
diff --git a/init/keywords.h b/init/keywords.h
index 3e3733f..307c084 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -14,11 +14,15 @@
 int do_mkdir(int nargs, char **args);
 int do_mount(int nargs, char **args);
 int do_restart(int nargs, char **args);
+int do_restorecon(int nargs, char **args);
 int do_rm(int nargs, char **args);
 int do_rmdir(int nargs, char **args);
+int do_setcon(int nargs, char **args);
+int do_setenforce(int nargs, char **args);
 int do_setkey(int nargs, char **args);
 int do_setprop(int nargs, char **args);
 int do_setrlimit(int nargs, char **args);
+int do_setsebool(int nargs, char **args);
 int do_start(int nargs, char **args);
 int do_stop(int nargs, char **args);
 int do_trigger(int nargs, char **args);
@@ -61,13 +65,18 @@
     KEYWORD(oneshot,     OPTION,  0, 0)
     KEYWORD(onrestart,   OPTION,  0, 0)
     KEYWORD(restart,     COMMAND, 1, do_restart)
+    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
     KEYWORD(rm,          COMMAND, 1, do_rm)
     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
+    KEYWORD(seclabel,    OPTION,  0, 0)
     KEYWORD(service,     SECTION, 0, 0)
+    KEYWORD(setcon,      COMMAND, 1, do_setcon)
+    KEYWORD(setenforce,  COMMAND, 1, do_setenforce)
     KEYWORD(setenv,      OPTION,  2, 0)
     KEYWORD(setkey,      COMMAND, 0, do_setkey)
     KEYWORD(setprop,     COMMAND, 2, do_setprop)
     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
+    KEYWORD(setsebool,   COMMAND, 1, do_setsebool)
     KEYWORD(socket,      OPTION,  0, 0)
     KEYWORD(start,       COMMAND, 1, do_start)
     KEYWORD(stop,        COMMAND, 1, do_stop)
diff --git a/init/util.c b/init/util.c
index 13c9ca2..3a4b10b 100755
--- a/init/util.c
+++ b/init/util.c
@@ -23,6 +23,10 @@
 #include <errno.h>
 #include <time.h>
 
+#ifdef HAVE_SELINUX
+#include <selinux/label.h>
+#endif
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -33,6 +37,7 @@
 
 #include <private/android_filesystem_config.h>
 
+#include "init.h"
 #include "log.h"
 #include "util.h"
 
@@ -84,6 +89,9 @@
 {
     struct sockaddr_un addr;
     int fd, ret;
+#ifdef HAVE_SELINUX
+    char *secon;
+#endif
 
     fd = socket(PF_UNIX, type, 0);
     if (fd < 0) {
@@ -102,12 +110,26 @@
         goto out_close;
     }
 
+#ifdef HAVE_SELINUX
+    secon = NULL;
+    if (sehandle) {
+        ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK);
+        if (ret == 0)
+            setfscreatecon(secon);
+    }
+#endif
+
     ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     if (ret) {
         ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
         goto out_unlink;
     }
 
+#ifdef HAVE_SELINUX
+    setfscreatecon(NULL);
+    freecon(secon);
+#endif
+
     chown(addr.sun_path, uid, gid);
     chmod(addr.sun_path, perm);