1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6
7 #include "alloc-util.h"
8 #include "dev-setup.h"
9 #include "label.h"
10 #include "log.h"
11 #include "mkdir-label.h"
12 #include "nulstr-util.h"
13 #include "path-util.h"
14 #include "umask-util.h"
15 #include "user-util.h"
16
dev_setup(const char * prefix,uid_t uid,gid_t gid)17 int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
18 static const char symlinks[] =
19 "-/proc/kcore\0" "/dev/core\0"
20 "/proc/self/fd\0" "/dev/fd\0"
21 "/proc/self/fd/0\0" "/dev/stdin\0"
22 "/proc/self/fd/1\0" "/dev/stdout\0"
23 "/proc/self/fd/2\0" "/dev/stderr\0";
24
25 const char *j, *k;
26 int r;
27
28 NULSTR_FOREACH_PAIR(j, k, symlinks) {
29 _cleanup_free_ char *link_name = NULL;
30 const char *n;
31
32 if (j[0] == '-') {
33 j++;
34
35 if (access(j, F_OK) < 0)
36 continue;
37 }
38
39 if (prefix) {
40 link_name = path_join(prefix, k);
41 if (!link_name)
42 return -ENOMEM;
43
44 n = link_name;
45 } else
46 n = k;
47
48 r = symlink_label(j, n);
49 if (r < 0)
50 log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n);
51
52 if (uid != UID_INVALID || gid != GID_INVALID)
53 if (lchown(n, uid, gid) < 0)
54 log_debug_errno(errno, "Failed to chown %s: %m", n);
55 }
56
57 return 0;
58 }
59
make_inaccessible_nodes(const char * parent_dir,uid_t uid,gid_t gid)60 int make_inaccessible_nodes(
61 const char *parent_dir,
62 uid_t uid,
63 gid_t gid) {
64
65 static const struct {
66 const char *name;
67 mode_t mode;
68 } table[] = {
69 { "inaccessible", S_IFDIR | 0755 },
70 { "inaccessible/reg", S_IFREG | 0000 },
71 { "inaccessible/dir", S_IFDIR | 0000 },
72 { "inaccessible/fifo", S_IFIFO | 0000 },
73 { "inaccessible/sock", S_IFSOCK | 0000 },
74
75 /* The following two are likely to fail if we lack the privs for it (for example in an userns
76 * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibits creation of
77 * device nodes with a major/minor of 0). But that's entirely fine. Consumers of these files
78 * should implement falling back to use a different node then, for example
79 * <root>/inaccessible/sock, which is close enough in behaviour and semantics for most uses.
80 */
81 { "inaccessible/chr", S_IFCHR | 0000 },
82 { "inaccessible/blk", S_IFBLK | 0000 },
83 };
84
85 int r;
86
87 if (!parent_dir)
88 parent_dir = "/run/systemd";
89
90 BLOCK_WITH_UMASK(0000);
91
92 /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
93 * ("masking") file nodes that shall become inaccessible and empty for specific containers or services. We try
94 * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
95 * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
96
97 for (size_t i = 0; i < ELEMENTSOF(table); i++) {
98 _cleanup_free_ char *path = NULL;
99
100 path = path_join(parent_dir, table[i].name);
101 if (!path)
102 return log_oom();
103
104 if (S_ISDIR(table[i].mode))
105 r = mkdir_label(path, table[i].mode & 07777);
106 else
107 r = mknod_label(path, table[i].mode, makedev(0, 0));
108 if (r < 0) {
109 log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
110 continue;
111 }
112
113 if (uid != UID_INVALID || gid != GID_INVALID) {
114 if (lchown(path, uid, gid) < 0)
115 log_debug_errno(errno, "Failed to chown '%s': %m", path);
116 }
117 }
118
119 return 0;
120 }
121