]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/mount-setup.c
selinux: unify systemd and udev code
[thirdparty/systemd.git] / src / core / mount-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/mount.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <libgen.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <ftw.h>
31
32 #include "mount-setup.h"
33 #include "log.h"
34 #include "macro.h"
35 #include "util.h"
36 #include "label.h"
37 #include "set.h"
38 #include "strv.h"
39 #include "mkdir.h"
40
41 #ifndef TTY_GID
42 #define TTY_GID 5
43 #endif
44
45 typedef struct MountPoint {
46 const char *what;
47 const char *where;
48 const char *type;
49 const char *options;
50 unsigned long flags;
51 bool fatal;
52 } MountPoint;
53
54 /* The first three entries we might need before SELinux is up. The
55 * fourth (securityfs) is needed by IMA to load a custom policy. The
56 * other ones we can delay until SELinux and IMA are loaded. */
57 #define N_EARLY_MOUNT 4
58
59 static const MountPoint mount_table[] = {
60 { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
61 { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
62 { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
63 { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
64 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
65 { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
66 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
67 { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false },
68 { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
69 };
70
71 /* These are API file systems that might be mounted by other software,
72 * we just list them here so that we know that we should ignore them */
73
74 static const char * const ignore_paths[] = {
75 "/sys/fs/selinux",
76 "/selinux",
77 "/proc/bus/usb"
78 };
79
80 bool mount_point_is_api(const char *path) {
81 unsigned i;
82
83 /* Checks if this mount point is considered "API", and hence
84 * should be ignored */
85
86 for (i = 0; i < ELEMENTSOF(mount_table); i ++)
87 if (path_equal(path, mount_table[i].where))
88 return true;
89
90 return path_startswith(path, "/sys/fs/cgroup/");
91 }
92
93 bool mount_point_ignore(const char *path) {
94 unsigned i;
95
96 for (i = 0; i < ELEMENTSOF(ignore_paths); i++)
97 if (path_equal(path, ignore_paths[i]))
98 return true;
99
100 return false;
101 }
102
103 static int mount_one(const MountPoint *p, bool relabel) {
104 int r;
105
106 assert(p);
107
108 /* Relabel first, just in case */
109 if (relabel)
110 label_fix(p->where, true);
111
112 if ((r = path_is_mount_point(p->where, true)) < 0)
113 return r;
114
115 if (r > 0)
116 return 0;
117
118 /* The access mode here doesn't really matter too much, since
119 * the mounted file system will take precedence anyway. */
120 mkdir_p(p->where, 0755);
121
122 log_debug("Mounting %s to %s of type %s with options %s.",
123 p->what,
124 p->where,
125 p->type,
126 strna(p->options));
127
128 if (mount(p->what,
129 p->where,
130 p->type,
131 p->flags,
132 p->options) < 0) {
133 log_error("Failed to mount %s: %s", p->where, strerror(errno));
134 return p->fatal ? -errno : 0;
135 }
136
137 /* Relabel again, since we now mounted something fresh here */
138 if (relabel)
139 label_fix(p->where, false);
140
141 return 1;
142 }
143
144 int mount_setup_early(void) {
145 unsigned i;
146 int r = 0;
147
148 assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
149
150 /* Do a minimal mount of /proc and friends to enable the most
151 * basic stuff, such as SELinux */
152 for (i = 0; i < N_EARLY_MOUNT; i ++) {
153 int j;
154
155 j = mount_one(mount_table + i, false);
156 if (r == 0)
157 r = j;
158 }
159
160 return r;
161 }
162
163 int mount_cgroup_controllers(char ***join_controllers) {
164 int r;
165 FILE *f;
166 char buf[LINE_MAX];
167 Set *controllers;
168
169 /* Mount all available cgroup controllers that are built into the kernel. */
170
171 f = fopen("/proc/cgroups", "re");
172 if (!f) {
173 log_error("Failed to enumerate cgroup controllers: %m");
174 return 0;
175 }
176
177 controllers = set_new(string_hash_func, string_compare_func);
178 if (!controllers) {
179 r = -ENOMEM;
180 log_error("Failed to allocate controller set.");
181 goto finish;
182 }
183
184 /* Ignore the header line */
185 (void) fgets(buf, sizeof(buf), f);
186
187 for (;;) {
188 char *controller;
189 int enabled = 0;
190
191 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
192
193 if (feof(f))
194 break;
195
196 log_error("Failed to parse /proc/cgroups.");
197 r = -EIO;
198 goto finish;
199 }
200
201 if (!enabled) {
202 free(controller);
203 continue;
204 }
205
206 r = set_put(controllers, controller);
207 if (r < 0) {
208 log_error("Failed to add controller to set.");
209 free(controller);
210 goto finish;
211 }
212 }
213
214 for (;;) {
215 MountPoint p;
216 char *controller, *where, *options;
217 char ***k = NULL;
218
219 controller = set_steal_first(controllers);
220 if (!controller)
221 break;
222
223 if (join_controllers)
224 for (k = join_controllers; *k; k++)
225 if (strv_find(*k, controller))
226 break;
227
228 if (k && *k) {
229 char **i, **j;
230
231 for (i = *k, j = *k; *i; i++) {
232
233 if (!streq(*i, controller)) {
234 char *t;
235
236 t = set_remove(controllers, *i);
237 if (!t) {
238 free(*i);
239 continue;
240 }
241 free(t);
242 }
243
244 *(j++) = *i;
245 }
246
247 *j = NULL;
248
249 options = strv_join(*k, ",");
250 if (!options) {
251 log_error("Failed to join options");
252 free(controller);
253 r = -ENOMEM;
254 goto finish;
255 }
256
257 } else {
258 options = controller;
259 controller = NULL;
260 }
261
262 where = strappend("/sys/fs/cgroup/", options);
263 if (!where) {
264 log_error("Failed to build path");
265 free(options);
266 r = -ENOMEM;
267 goto finish;
268 }
269
270 zero(p);
271 p.what = "cgroup";
272 p.where = where;
273 p.type = "cgroup";
274 p.options = options;
275 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
276 p.fatal = false;
277
278 r = mount_one(&p, true);
279 free(controller);
280 free(where);
281
282 if (r < 0) {
283 free(options);
284 goto finish;
285 }
286
287 if (r > 0 && k && *k) {
288 char **i;
289
290 for (i = *k; *i; i++) {
291 char *t;
292
293 t = strappend("/sys/fs/cgroup/", *i);
294 if (!t) {
295 log_error("Failed to build path");
296 r = -ENOMEM;
297 free(options);
298 goto finish;
299 }
300
301 r = symlink(options, t);
302 free(t);
303
304 if (r < 0 && errno != EEXIST) {
305 log_error("Failed to create symlink: %m");
306 r = -errno;
307 free(options);
308 goto finish;
309 }
310 }
311 }
312
313 free(options);
314 }
315
316 r = 0;
317
318 finish:
319 set_free_free(controllers);
320
321 fclose(f);
322
323 return r;
324 }
325
326 static int symlink_and_label(const char *old_path, const char *new_path) {
327 int r;
328
329 assert(old_path);
330 assert(new_path);
331
332 r = label_context_set(new_path, S_IFLNK);
333 if (r < 0)
334 return r;
335
336 if (symlink(old_path, new_path) < 0)
337 r = -errno;
338
339 label_context_clear();
340
341 return r;
342 }
343
344 static int nftw_cb(
345 const char *fpath,
346 const struct stat *sb,
347 int tflag,
348 struct FTW *ftwbuf) {
349
350 /* No need to label /dev twice in a row... */
351 if (_unlikely_(ftwbuf->level == 0))
352 return FTW_CONTINUE;
353
354 label_fix(fpath, true);
355
356 /* /run/initramfs is static data and big, no need to
357 * dynamically relabel its contents at boot... */
358 if (_unlikely_(ftwbuf->level == 1 &&
359 tflag == FTW_D &&
360 streq(fpath, "/run/initramfs")))
361 return FTW_SKIP_SUBTREE;
362
363 return FTW_CONTINUE;
364 };
365
366 int mount_setup(bool loaded_policy) {
367
368 static const char symlinks[] =
369 "/proc/kcore\0" "/dev/core\0"
370 "/proc/self/fd\0" "/dev/fd\0"
371 "/proc/self/fd/0\0" "/dev/stdin\0"
372 "/proc/self/fd/1\0" "/dev/stdout\0"
373 "/proc/self/fd/2\0" "/dev/stderr\0";
374
375 static const char relabel[] =
376 "/run/initramfs/root-fsck\0"
377 "/run/initramfs/shutdown\0";
378
379 int r;
380 unsigned i;
381 const char *j, *k;
382
383 for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
384 r = mount_one(mount_table + i, true);
385
386 if (r < 0)
387 return r;
388 }
389
390 /* Nodes in devtmpfs and /run need to be manually updated for
391 * the appropriate labels, after mounting. The other virtual
392 * API file systems like /sys and /proc do not need that, they
393 * use the same label for all their files. */
394 if (loaded_policy) {
395 usec_t before_relabel, after_relabel;
396 char timespan[FORMAT_TIMESPAN_MAX];
397
398 before_relabel = now(CLOCK_MONOTONIC);
399
400 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
401 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
402
403 /* Explicitly relabel these */
404 NULSTR_FOREACH(j, relabel)
405 label_fix(j, true);
406
407 after_relabel = now(CLOCK_MONOTONIC);
408
409 log_info("Relabelled /dev and /run in %s.",
410 format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
411 }
412
413 /* Create a few default symlinks, which are normally created
414 * by udevd, but some scripts might need them before we start
415 * udevd. */
416 NULSTR_FOREACH_PAIR(j, k, symlinks)
417 symlink_and_label(j, k);
418
419 /* Create a few directories we always want around */
420 label_mkdir("/run/systemd", 0755);
421 label_mkdir("/run/systemd/system", 0755);
422
423 return 0;
424 }