| 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 |