blob: d8f00a765debc9ba435e3e6d86e1de657cb9d92e [file] [log] [blame]
This patch adds SELinux functionality to upstart, by making it load the SELinux
policy and then re-exec itself. It also relabels devtmpfs created by kernel
before init scripts execute. This appears to be all that is required to
support SELinux in the init process.
This mimics an existing upstart patch that is floating around but was never
mainlined in any major distribution (afaik), likely because upstart is no
longer the default init for any of them (also afaik). It also mimics the
behavior of other init programs, for example Android's init.
The patch was developed specifically for Chromium OS, which at this time is
using a patched upstart version 1.2. It is meant to be applied after a number
of other patches as described in the Chromium OS upstart ebuild in
src/third_party/chromiumos-overlay/sys-apps/upstart.
Patch by Luigi Semenzato <semenzato@chromium.org>
Modified by Qijiang Fan <fqj@chromium.org> to relabel some fs.
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,16 @@ PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16])
# Checks for header files.
AC_CHECK_HEADERS([valgrind/valgrind.h])
+AC_ARG_ENABLE(selinux,
+ AS_HELP_STRING([--enable-selinux], [enable SELinux support]),
+ [], [enable_selinux=no])
+
+if test "x$enable_selinux" = "xyes" ; then
+ PKG_CHECK_MODULES(SELINUX, [libselinux])
+ AC_DEFINE(HAVE_SELINUX, 1, [Define if we have SELinux])
+ AC_DEFINE(RESTORE_PATHS, [{"/dev"}], [Define restorecon paths])
+fi
+
# Checks for typedefs, structures, and compiler characteristics.
AC_PROG_CC_C99
AM_PROG_CC_C_O
--- a/init/Makefile.am
+++ b/init/Makefile.am
@@ -5,7 +5,8 @@ initconfdir = $(sysconfdir)/init
AM_CFLAGS = \
$(NIH_CFLAGS) \
$(NIH_DBUS_CFLAGS) \
- $(DBUS_CFLAGS)
+ $(DBUS_CFLAGS) \
+ $(SELINUX_CFLAGS)
AM_CPPFLAGS = \
-DLOCALEDIR="\"$(localedir)\"" \
@@ -60,6 +61,7 @@ init_LDADD = \
$(NIH_LIBS) \
$(NIH_DBUS_LIBS) \
$(DBUS_LIBS) \
+ $(SELINUX_LIBS) \
-lrt
--- a/init/errors.h
+++ b/init/errors.h
@@ -52,6 +52,9 @@ enum {
/* Errors while handling control requests */
CONTROL_NAME_TAKEN,
+
+ /* SELinux handling errors */
+ SELINUX_POLICY_LOAD_FAIL,
};
/* Error strings for defined messages */
@@ -72,5 +75,6 @@ enum {
#define PARSE_EXPECTED_VARIABLE_STR N_("Expected variable name before value")
#define PARSE_MISMATCHED_PARENS_STR N_("Mismatched parentheses")
#define CONTROL_NAME_TAKEN_STR N_("Name already taken")
+#define SELINUX_POLICY_LOAD_FAIL_STR N_("Failed to load SELinux policy while in enforcing mode")
#endif /* INIT_ERRORS_H */
--- a/init/main.c
+++ b/init/main.c
@@ -29,6 +29,10 @@
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <sys/resource.h>
+#include <sys/mount.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
@@ -39,6 +43,11 @@
#include <syslog.h>
#include <unistd.h>
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/restorecon.h>
+#endif
+
#include <linux/kd.h>
#include <nih/macros.h>
@@ -53,6 +61,7 @@
#include <nih/logging.h>
#include "paths.h"
+#include "errors.h"
#include "events.h"
#include "system.h"
#include "job_process.h"
@@ -73,6 +82,9 @@
static void usr1_handler (void *data, NihSignal *signal);
#endif /* DEBUG */
+#ifdef HAVE_SELINUX
+static int initialize_selinux (void);
+#endif
/**
* argv0:
@@ -111,6 +123,7 @@
char *argv[])
{
char **args;
+ char *arg_end = NULL;
int ret;
argv0 = argv[0];
@@ -158,8 +171,6 @@
* will show whitespace in their place.
*/
if (argc > 1) {
- char *arg_end;
-
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
@@ -208,7 +219,8 @@
* essential for any Linux system; not to mention used by
* ourselves.
*/
- if (system_mount ("proc", "/proc") < 0) {
+ if (system_mount ("proc", "/proc",
+ MS_NODEV | MS_NOEXEC | MS_NOSUID) < 0) {
NihError *err;
err = nih_error_get ();
@@ -217,7 +229,8 @@
nih_free (err);
}
- if (system_mount ("sysfs", "/sys") < 0) {
+ if (system_mount ("sysfs", "/sys",
+ MS_NODEV | MS_NOEXEC | MS_NOSUID) < 0) {
NihError *err;
err = nih_error_get ();
@@ -225,6 +238,70 @@
err->message);
nih_free (err);
}
+
+#ifdef HAVE_SELINUX
+ if (!getenv ("SELINUX_INIT")) {
+ /*
+ * We mount selinuxfs ourselves instead of letting
+ * libselinux do it so that our standard mount options
+ * (nosuid and noexec) will be applied. Note that
+ * we leave devices on since there is null device in
+ * selinuxfs.
+ */
+ if (system_mount ("selinuxfs", "/sys/fs/selinux",
+ MS_NOEXEC | MS_NOSUID) < 0) {
+ NihError *err;
+
+ err = nih_error_get ();
+ nih_fatal ("%s: %s",
+ _("Unable to mount /sys/fs/selinux filesystem"),
+ err->message);
+ nih_free (err);
+
+ exit (1);
+ }
+
+ if (initialize_selinux () < 0) {
+ NihError *err;
+
+ err = nih_error_get ();
+ nih_fatal ("%s: %s",
+ _("Failed to initialize SELinux"),
+ err->message);
+ nih_free (err);
+
+ exit (1);
+ }
+
+ const char *restore_paths[] = RESTORE_PATHS;
+ for (size_t i = 0;
+ i < sizeof(restore_paths) / sizeof(const char *);
+ i++) {
+ const int restorecon_args = SELINUX_RESTORECON_RECURSE |
+ SELINUX_RESTORECON_REALPATH;
+ if (selinux_restorecon(restore_paths[i],
+ restorecon_args) != 0) {
+ nih_warn ("%s: %d",
+ _("Failed to restorecon"), errno);
+ // ignore error for now until policy are combined. exit(1);
+ }
+ }
+
+ putenv ("SELINUX_INIT=YES");
+ nih_info (_("SELinux policy loaded, doing self-exec"));
+
+ /* Unmangle argv and re-execute */
+ if (arg_end)
+ *arg_end = '\0';
+ execv (argv0, argv);
+
+ nih_fatal ("%s: %s",
+ _("Failed to re-exec init"),
+ strerror (errno));
+ exit (1);
+ }
+#endif
+
#else /* DEBUG */
nih_log_set_priority (NIH_LOG_DEBUG);
nih_debug ("Running as PID %d (PPID %d)",
@@ -632,0 +696,49 @@
+
+#ifdef HAVE_SELINUX
+/**
+ * selinux_set_checkreqprot:
+ *
+ * Forces /sys/fs/selinux/checkreqprot to 0 to ensure that
+ * SELinux will check the protection for mmap and mprotect
+ * calls that will be applied by the kernel and not the
+ * one requested by the application.
+ **/
+static int selinux_set_checkreqprot (void)
+{
+ static const char path[] = "/sys/fs/selinux/checkreqprot";
+ FILE *checkreqprot_file;
+
+ checkreqprot_file = fopen (path, "w");
+ if (!checkreqprot_file)
+ nih_return_system_error (-1);
+
+ if (fputc ('0', checkreqprot_file) == EOF)
+ nih_return_system_error (-1);
+
+ if (fclose (checkreqprot_file) != 0)
+ nih_return_system_error (-1);
+
+ return 0;
+}
+
+/**
+ * initialize_selinux:
+ *
+ * Loads an SELinux policy.
+ **/
+static int initialize_selinux (void)
+{
+ int enforce = 0;
+
+ if (selinux_init_load_policy (&enforce) != 0) {
+ nih_warn (_("SELinux policy failed to load"));
+ if (enforce > 0) {
+ /* Enforcing mode, must quit. */
+ nih_return_error (-1, SELINUX_POLICY_LOAD_FAIL,
+ _(SELINUX_POLICY_LOAD_FAIL_STR));
+ }
+ }
+
+ return selinux_set_checkreqprot ();
+}
+#endif /* HAVE_SELINUX */
--- a/init/system.c
+++ b/init/system.c
@@ -175,7 +175,8 @@ system_setup_console (ConsoleType type,
**/
int
system_mount (const char *type,
- const char *dir)
+ const char *dir,
+ unsigned int opts)
{
nih_local char *parent = NULL;
char * ptr;
@@ -204,8 +205,7 @@ system_mount (const char *type,
return 0;
/* Mount the filesystem */
- if (mount ("none", dir, type,
- MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
+ if (mount ("none", dir, type, opts, NULL) < 0)
nih_return_system_error (-1);
return 0;
--- a/init/system.h
+++ b/init/system.h
@@ -35,7 +35,7 @@ int system_kill (pid_t pid, int
int system_setup_console (ConsoleType type, int reset)
__attribute__ ((warn_unused_result));
-int system_mount (const char *type, const char *dir)
+int system_mount (const char *type, const char *dir, unsigned int opts)
__attribute__ ((warn_unused_result));
NIH_END_EXTERN