chiark / gitweb /
selinux: don't relabel /run/initramfs
[elogind.git] / src / mount-setup.c
index 49eab0bfa72ef5b148d53f70ae9582415207f351..f70c4d46f3b9a78f977e6086917eed63a9a20c0d 100644 (file)
@@ -34,6 +34,8 @@
 #include "macro.h"
 #include "util.h"
 #include "label.h"
+#include "set.h"
+#include "strv.h"
 
 #ifndef TTY_GID
 #define TTY_GID 5
@@ -48,25 +50,28 @@ typedef struct MountPoint {
         bool fatal;
 } MountPoint;
 
+/* The first three entries we might need before SELinux is up. The
+ * other ones we can delay until SELinux is loaded. */
+#define N_EARLY_MOUNT 3
+
 static const MountPoint mount_table[] = {
         { "proc",     "/proc",                  "proc",     NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
         { "sysfs",    "/sys",                   "sysfs",    NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
         { "devtmpfs", "/dev",                   "devtmpfs", "mode=755",          MS_NOSUID,                    true },
         { "tmpfs",    "/dev/shm",               "tmpfs",    "mode=1777",         MS_NOSUID|MS_NODEV,           true },
         { "devpts",   "/dev/pts",               "devpts",   "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
-        { "tmpfs",    "/run",                   "tmpfs",    "mode=755",          MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
-        { "tmpfs",    "/sys/fs/cgroup",         "tmpfs",    "mode=755",          MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
-        { "cgroup",   "/sys/fs/cgroup/systemd", "cgroup",   "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
+        { "tmpfs",    "/run",                   "tmpfs",    "mode=755",          MS_NOSUID|MS_NODEV, true },
+        { "tmpfs",    "/sys/fs/cgroup",         "tmpfs",    "mode=755",          MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
+        { "cgroup",   "/sys/fs/cgroup/systemd", "cgroup",   "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
 };
 
 /* These are API file systems that might be mounted by other software,
  * we just list them here so that we know that we should ignore them */
 
 static const char * const ignore_paths[] = {
+        "/sys/fs/selinux",
         "/selinux",
-        "/proc/bus/usb",
-        "/var/lib/nfs/rpc_pipefs",
-        "/proc/fs/nfsd"
+        "/proc/bus/usb"
 };
 
 bool mount_point_is_api(const char *path) {
@@ -92,16 +97,20 @@ bool mount_point_ignore(const char *path) {
         return false;
 }
 
-static int mount_one(const MountPoint *p) {
+static int mount_one(const MountPoint *p, bool relabel) {
         int r;
 
         assert(p);
 
-        if ((r = path_is_mount_point(p->where)) < 0)
+        /* Relabel first, just in case */
+        if (relabel)
+                label_fix(p->where, true);
+
+        if ((r = path_is_mount_point(p->where, true)) < 0)
                 return r;
 
         if (r > 0)
-                goto finish;
+                return 0;
 
         /* The access mode here doesn't really matter too much, since
          * the mounted file system will take precedence anyway. */
@@ -122,29 +131,59 @@ static int mount_one(const MountPoint *p) {
                 return p->fatal ? -errno : 0;
         }
 
-finish:
-        label_fix(p->where, false);
+        /* Relabel again, since we now mounted something fresh here */
+        if (relabel)
+                label_fix(p->where, false);
 
-        return 0;
+        return 1;
 }
 
-static int mount_cgroup_controllers(void) {
+int mount_setup_early(void) {
+        unsigned i;
+        int r = 0;
+
+        assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
+
+        /* Do a minimal mount of /proc and friends to enable the most
+         * basic stuff, such as SELinux */
+        for (i = 0; i < N_EARLY_MOUNT; i ++)  {
+                int j;
+
+                j = mount_one(mount_table + i, false);
+                if (r == 0)
+                        r = j;
+        }
+
+        return r;
+}
+
+int mount_cgroup_controllers(char ***join_controllers) {
         int r;
         FILE *f;
-        char buf [256];
+        char buf[LINE_MAX];
+        Set *controllers;
 
         /* Mount all available cgroup controllers that are built into the kernel. */
 
-        if (!(f = fopen("/proc/cgroups", "re")))
-                return -ENOENT;
+        f = fopen("/proc/cgroups", "re");
+        if (!f) {
+                log_error("Failed to enumerate cgroup controllers: %m");
+                return 0;
+        }
+
+        controllers = set_new(string_hash_func, string_compare_func);
+        if (!controllers) {
+                r = -ENOMEM;
+                log_error("Failed to allocate controller set.");
+                goto finish;
+        }
 
         /* Ignore the header line */
         (void) fgets(buf, sizeof(buf), f);
 
         for (;;) {
-                MountPoint p;
-                char *controller, *where;
-                int enabled = false;
+                char *controller;
+                int enabled = 0;
 
                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
 
@@ -161,8 +200,66 @@ static int mount_cgroup_controllers(void) {
                         continue;
                 }
 
-                if (asprintf(&where, "/sys/fs/cgroup/%s", controller) < 0) {
+                r = set_put(controllers, controller);
+                if (r < 0) {
+                        log_error("Failed to add controller to set.");
                         free(controller);
+                        goto finish;
+                }
+        }
+
+        for (;;) {
+                MountPoint p;
+                char *controller, *where, *options;
+                char ***k = NULL;
+
+                controller = set_steal_first(controllers);
+                if (!controller)
+                        break;
+
+                if (join_controllers)
+                        for (k = join_controllers; *k; k++)
+                                if (strv_find(*k, controller))
+                                        break;
+
+                if (k && *k) {
+                        char **i, **j;
+
+                        for (i = *k, j = *k; *i; i++) {
+
+                                if (!streq(*i, controller)) {
+                                        char *t;
+
+                                        t = set_remove(controllers, *i);
+                                        if (!t) {
+                                                free(*i);
+                                                continue;
+                                        }
+                                        free(t);
+                                }
+
+                                *(j++) = *i;
+                        }
+
+                        *j = NULL;
+
+                        options = strv_join(*k, ",");
+                        if (!options) {
+                                log_error("Failed to join options");
+                                free(controller);
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                } else {
+                        options = controller;
+                        controller = NULL;
+                }
+
+                where = strappend("/sys/fs/cgroup/", options);
+                if (!where) {
+                        log_error("Failed to build path");
+                        free(options);
                         r = -ENOMEM;
                         goto finish;
                 }
@@ -171,21 +268,53 @@ static int mount_cgroup_controllers(void) {
                 p.what = "cgroup";
                 p.where = where;
                 p.type = "cgroup";
-                p.options = controller;
+                p.options = options;
                 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
                 p.fatal = false;
 
-                r = mount_one(&p);
+                r = mount_one(&p, true);
                 free(controller);
                 free(where);
 
-                if (r < 0)
+                if (r < 0) {
+                        free(options);
                         goto finish;
+                }
+
+                if (r > 0 && k && *k) {
+                        char **i;
+
+                        for (i = *k; *i; i++) {
+                                char *t;
+
+                                t = strappend("/sys/fs/cgroup/", *i);
+                                if (!t) {
+                                        log_error("Failed to build path");
+                                        r = -ENOMEM;
+                                        free(options);
+                                        goto finish;
+                                }
+
+                                r = symlink(options, t);
+                                free(t);
+
+                                if (r < 0 && errno != EEXIST) {
+                                        log_error("Failed to create symlink: %m");
+                                        r = -errno;
+                                        free(options);
+                                        goto finish;
+                                }
+                        }
+                }
+
+                free(options);
         }
 
         r = 0;
 
 finish:
+        set_free_free(controllers);
+
         fclose(f);
 
         return r;
@@ -215,14 +344,21 @@ static int nftw_cb(
                 struct FTW *ftwbuf) {
 
         /* No need to label /dev twice in a row... */
-        if (ftwbuf->level == 0)
-                return 0;
+        if (_unlikely_(ftwbuf->level == 0))
+                return FTW_CONTINUE;
+
+        /* /run/initramfs is static data and big, no need to
+         * dynamically relabel it at boot... */
+        if (_unlikely_(ftwbuf->level == 1 &&
+                      tflag == FTW_D &&
+                      streq(fpath, "/run/initramfs")))
+                return FTW_SKIP_SUBTREE;
 
         label_fix(fpath, true);
-        return 0;
+        return FTW_CONTINUE;
 };
 
-int mount_setup(void) {
+int mount_setup(bool loaded_policy) {
 
         const char symlinks[] =
                 "/proc/kcore\0"      "/dev/core\0"
@@ -235,27 +371,42 @@ int mount_setup(void) {
         unsigned i;
         const char *j, *k;
 
-        for (i = 0; i < ELEMENTSOF(mount_table); i ++)
-                if ((r = mount_one(mount_table+i)) < 0)
+        for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
+                r = mount_one(mount_table + i, true);
+
+                if (r < 0)
                         return r;
+        }
+
+        /* Nodes in devtmpfs and /run need to be manually updated for
+         * the appropriate labels, after mounting. The other virtual
+         * API file systems like /sys and /proc do not need that, they
+         * use the same label for all their files. */
+        if (loaded_policy) {
+                usec_t before_relabel, after_relabel;
+                char timespan[FORMAT_TIMESPAN_MAX];
 
-        /* Nodes in devtmpfs need to be manually updated for the
-         * appropriate labels, after mounting. The other virtual API
-         * file systems do not need. */
+                before_relabel = now(CLOCK_MONOTONIC);
 
-        if (unlink("/dev/.systemd-relabel-devtmpfs") >= 0)
-                nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS);
+                nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+                nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+
+                after_relabel = now(CLOCK_MONOTONIC);
+
+                log_info("Relabelled /dev and /run in %s.",
+                         format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
+
+        }
 
         /* Create a few default symlinks, which are normally created
-         * bei udevd, but some scripts might need them before we start
+         * by udevd, but some scripts might need them before we start
          * udevd. */
-
         NULSTR_FOREACH_PAIR(j, k, symlinks)
                 symlink_and_label(j, k);
 
         /* Create a few directories we always want around */
         mkdir("/run/systemd", 0755);
-        mkdir("/run/systemd/ask-password", 0755);
+        mkdir("/run/systemd/system", 0755);
 
-        return mount_cgroup_controllers();
+        return 0;
 }