(void) mkdir_label("/run/systemd", 0755);
(void) mkdir_label("/run/systemd/system", 0755);
- /* Set up inaccessible (and empty) file nodes of all types */
- (void) mkdir_label("/run/systemd/inaccessible", 0000);
- (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
- (void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
- (void) mkfifo("/run/systemd/inaccessible/fifo", 0000);
- (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0);
-
- /* The following two are likely to fail if we lack the privs for it (for example in an userns environment, if
- * CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 device nodes to be
- * created). But that's entirely fine. Consumers of these files should carry fallback to use a different node
- * then, for example /run/systemd/inaccessible/sock, which is close enough in behaviour and semantics for most
- * uses. */
- (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));
- (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0));
+ /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount inaccessible nodes
+ * from. */
+ (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
return 0;
}
#include "label.h"
#include "log.h"
#include "path-util.h"
+#include "umask-util.h"
#include "user-util.h"
#include "util.h"
return 0;
}
+
+int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
+ static const struct {
+ const char *name;
+ mode_t mode;
+ } table[] = {
+ { "/run/systemd", S_IFDIR | 0755 },
+ { "/run/systemd/inaccessible", S_IFDIR | 0000 },
+ { "/run/systemd/inaccessible/reg", S_IFREG | 0000 },
+ { "/run/systemd/inaccessible/dir", S_IFDIR | 0000 },
+ { "/run/systemd/inaccessible/fifo", S_IFIFO | 0000 },
+ { "/run/systemd/inaccessible/sock", S_IFSOCK | 0000 },
+
+ /* The following two are likely to fail if we lack the privs for it (for example in an userns
+ * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
+ * device nodes to be created). But that's entirely fine. Consumers of these files should carry
+ * fallback to use a different node then, for example /run/systemd/inaccessible/sock, which is close
+ * enough in behaviour and semantics for most uses. */
+ { "/run/systemd/inaccessible/chr", S_IFCHR | 0000 },
+ { "/run/systemd/inaccessible/blk", S_IFBLK | 0000 },
+ };
+
+ _cleanup_umask_ mode_t u;
+ size_t i;
+ int r;
+
+ u = umask(0000);
+
+ /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
+ * ("masking") file nodes that shall become inaccessible and empty for specific containers or services. We try
+ * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
+ * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ _cleanup_free_ char *path = NULL;
+
+ path = prefix_root(root, table[i].name);
+ if (!path)
+ return log_oom();
+
+ if (S_ISDIR(table[i].mode))
+ r = mkdir(path, table[i].mode & 07777);
+ else
+ r = mknod(path, table[i].mode, makedev(0, 0));
+ if (r < 0) {
+ if (errno != EEXIST)
+ log_debug_errno(errno, "Failed to create '%s', ignoring: %m", path);
+ continue;
+ }
+
+ if (uid != UID_INVALID || gid != GID_INVALID) {
+ if (lchown(path, uid, gid) < 0)
+ log_debug_errno(errno, "Failed to chown '%s': %m", path);
+ }
+ }
+
+ return 0;
+}
#include <sys/types.h>
int dev_setup(const char *prefix, uid_t uid, gid_t gid);
+
+int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid);
[],
[]],
+ [['src/test/test-dev-setup.c'],
+ [],
+ []],
+
[['src/test/test-capability.c'],
[],
[libcap]],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "capability-util.h"
+#include "dev-setup.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+ const char *f;
+ struct stat st;
+
+ if (have_effective_cap(CAP_DAC_OVERRIDE) <= 0)
+ return EXIT_TEST_SKIP;
+
+ assert_se(mkdtemp_malloc("/tmp/test-dev-setupXXXXXX", &p) >= 0);
+
+ f = prefix_roota(p, "/run");
+ assert_se(mkdir(f, 0755) >= 0);
+
+ assert_se(make_inaccessible_nodes(p, 1, 1) >= 0);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/reg");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/dir");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/fifo");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISFIFO(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/sock");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/chr");
+ if (stat(f, &st) < 0)
+ assert_se(errno == ENOENT);
+ else {
+ assert_se(S_ISCHR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+ }
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/blk");
+ if (stat(f, &st) < 0)
+ assert_se(errno == ENOENT);
+ else {
+ assert_se(S_ISBLK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+ }
+
+ return EXIT_SUCCESS;
+}