CHROMIUM: ensure we mount /sys and /proc properly when using SELinux
As part of initial policy loading process, libselinux would mount /proc
and /sys, if they were not already mounted, and it would not use the
standar mount flags, such as nosuid, noexec, nodev, that upstart would
normally use. To make sure the behavior is consistent in all the cases
let's mount /sys, /proc, and even /sys/fs/selinux ourselves, before
trying to load SELinux policy and re-executing init.
This rearranges SELinux support patch a bit by moving selinuxfs mounting
and policy loading code slightly later in the startup process so that
the mounting code is kept in the single place. It also allows us to use
standard nih library logging methods.
Note that we mount selinuxfs as nosuid, noexec, but leave out the nodev
option as there is null device on selinufs and things break if we try to
mount it as nodev.
BUG=b:29003204
TEST=Booted minnie, validated filesystems are mounted with correct
permissions.
Reviewed-on: https://chromium-review.googlesource.com/351002
Change-Id: Id182027516281144e9790ef82351344974605dcd
diff --git a/init/errors.h b/init/errors.h
index 2bf6c7c..c5ed1e3 100644
--- a/init/errors.h
+++ b/init/errors.h
@@ -52,6 +52,9 @@
/* Errors while handling control requests */
CONTROL_NAME_TAKEN,
+
+ /* SELinux handling errors */
+ SELINUX_POLICY_LOAD_FAIL,
};
/* Error strings for defined messages */
@@ -72,5 +75,6 @@
#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 */
diff --git a/init/main.c b/init/main.c
index d405dcc..94ce408 100644
--- a/init/main.c
+++ b/init/main.c
@@ -28,6 +28,7 @@
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <sys/resource.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -64,6 +65,7 @@
#include <nih/logging.h>
#include "paths.h"
+#include "errors.h"
#include "events.h"
#include "system.h"
#include "job_class.h"
@@ -82,16 +84,12 @@
static void pwr_handler (void *data, NihSignal *signal);
static void hup_handler (void *data, NihSignal *signal);
static void usr1_handler (void *data, NihSignal *signal);
-#else
-static int logger_kmsg (NihLogLevel priority, const char *message) {}
#endif /* DEBUG */
#ifdef HAVE_SELINUX
-#define CHECKREQPROT_PATH "/sys/fs/selinux/checkreqprot"
-static void initialize_selinux (char **argv);
+static int initialize_selinux (void);
#endif
-
/**
* argv0:
*
@@ -129,6 +127,7 @@
char *argv[])
{
char **args;
+ char *arg_end = NULL;
int ret;
#ifdef ADD_DIRCRYPTO_RING
int root_fd;
@@ -136,12 +135,6 @@
key_serial_t keyring_id;
#endif
-#ifdef HAVE_SELINUX
- if (getpid () == 1 && getenv ("SELINUX_INIT") == NULL) {
- initialize_selinux (argv);
- }
-#endif
-
argv0 = argv[0];
nih_main_init (argv0);
@@ -187,8 +180,6 @@
* will show whitespace in their place.
*/
if (argc > 1) {
- char *arg_end;
-
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
@@ -237,7 +228,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 ();
@@ -246,7 +238,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 ();
@@ -254,6 +247,56 @@
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);
+ }
+
+ 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)",
@@ -700,58 +743,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 and reexecs init to enter the the proper SELinux
- * context.
+ * Loads an SELinux policy.
**/
-void initialize_selinux (char **argv)
+static int initialize_selinux (void)
{
int enforce = 0;
- FILE *checkreqprot_file;
- const char *errstr;
- program_name = argv[0]; /* for logger_kmsg before NIH init */
- putenv ("SELINUX_INIT=YES");
if (selinux_init_load_policy (&enforce) != 0) {
- logger_kmsg (NIH_LOG_WARN, "SELinux policy failed to load");
+ nih_warn (_("SELinux policy failed to load"));
if (enforce > 0) {
/* Enforcing mode, must quit. */
- logger_kmsg (NIH_LOG_FATAL,
- "no SELinux policy in enforcing mode: quit");
- exit (1);
+ nih_return_error (-1, SELINUX_POLICY_LOAD_FAIL,
+ _(SELINUX_POLICY_LOAD_FAIL_STR));
}
}
- checkreqprot_file = fopen (CHECKREQPROT_PATH, "w");
- if (checkreqprot_file == NULL) {
- errstr = strerror(errno);
- logger_kmsg (NIH_LOG_FATAL,
- "Failed to open " CHECKREQPROT_PATH);
- logger_kmsg (NIH_LOG_FATAL, errstr);
- exit (1);
- }
- if (fputc ('0', checkreqprot_file) == EOF) {
- errstr = strerror(errno);
- logger_kmsg (NIH_LOG_FATAL,
- "Failed to write " CHECKREQPROT_PATH);
- logger_kmsg (NIH_LOG_FATAL, errstr);
- exit (1);
- }
- if (fclose (checkreqprot_file) != 0) {
- errstr = strerror(errno);
- logger_kmsg (NIH_LOG_FATAL,
- "Failed to close " CHECKREQPROT_PATH);
- logger_kmsg (NIH_LOG_FATAL, errstr);
- exit (1);
- }
-
- logger_kmsg (NIH_LOG_MESSAGE, "SELinux policy loaded, doing self-exec");
- execv (argv[0], argv);
- errstr = strerror(errno);
-
- logger_kmsg (NIH_LOG_FATAL, "Failed to re-exec init.");
- logger_kmsg (NIH_LOG_FATAL, errstr);
- exit (1);
+ return selinux_set_checkreqprot ();
}
#endif /* HAVE_SELINUX */
diff --git a/init/system.c b/init/system.c
index 98ceec7..c123eee 100644
--- a/init/system.c
+++ b/init/system.c
@@ -175,7 +175,8 @@
**/
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 @@
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;
diff --git a/init/system.h b/init/system.h
index 0b21dcc..a1a9238 100644
--- a/init/system.h
+++ b/init/system.h
@@ -35,7 +35,7 @@
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