]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/namespace.c
Merge pull request #8319 from keszybz/yet-another-symlink-installation-tweak
[thirdparty/systemd.git] / src / core / namespace.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
15ae422b
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
15ae422b
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
15ae422b 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
15ae422b
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
07630cea 22#include <sched.h>
15ae422b 23#include <stdio.h>
07630cea
LP
24#include <string.h>
25#include <sys/mount.h>
15ae422b 26#include <sys/stat.h>
07630cea 27#include <unistd.h>
25e870b5 28#include <linux/fs.h>
15ae422b 29
b5efdb8a 30#include "alloc-util.h"
10404d52 31#include "base-filesystem.h"
7f112f50 32#include "dev-setup.h"
3ffd4af2 33#include "fd-util.h"
d944dc95 34#include "fs-util.h"
e908468b 35#include "label.h"
915e6d16 36#include "loop-util.h"
07630cea
LP
37#include "loopback-setup.h"
38#include "missing.h"
39#include "mkdir.h"
4349cd7c 40#include "mount-util.h"
3ffd4af2 41#include "namespace.h"
07630cea 42#include "path-util.h"
d7b8eec7 43#include "selinux-util.h"
2583fbea 44#include "socket-util.h"
36ce7110 45#include "stat-util.h"
8b43440b 46#include "string-table.h"
07630cea
LP
47#include "string-util.h"
48#include "strv.h"
affb60b1 49#include "umask-util.h"
ee104e11 50#include "user-util.h"
07630cea 51#include "util.h"
15ae422b 52
737ba3c8 53#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC)
54
c17ec25e 55typedef enum MountMode {
15ae422b
LP
56 /* This is ordered by priority! */
57 INACCESSIBLE,
d2d6c096
LP
58 BIND_MOUNT,
59 BIND_MOUNT_RECURSIVE,
ac0930c8 60 PRIVATE_TMP,
7f112f50 61 PRIVATE_DEV,
5d997827 62 BIND_DEV,
6c47cd7d 63 EMPTY_DIR,
5d997827
LP
64 SYSFS,
65 PROCFS,
66 READONLY,
59eeb84b 67 READWRITE,
2abd4e38 68 TMPFS,
c17ec25e 69} MountMode;
15ae422b 70
34de407a 71typedef struct MountEntry {
5327c910 72 const char *path_const; /* Memory allocated on stack or static */
cfbeb4ef 73 MountMode mode:5;
5327c910
LP
74 bool ignore:1; /* Ignore if path does not exist? */
75 bool has_prefix:1; /* Already is prefixed by the root dir? */
cfbeb4ef 76 bool read_only:1; /* Shall this mount point be read-only? */
55fe7432 77 char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */
d2d6c096
LP
78 const char *source_const; /* The source path, for bind mounts */
79 char *source_malloc;
2abd4e38
YW
80 const char *options_const;/* Mount options for tmpfs */
81 char *options_malloc;
82 unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
34de407a 83} MountEntry;
15ae422b 84
5d997827
LP
85/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted
86 * something there already. These mounts are hence overriden by any other explicitly configured mounts. */
87static const MountEntry apivfs_table[] = {
88 { "/proc", PROCFS, false },
89 { "/dev", BIND_DEV, false },
90 { "/sys", SYSFS, false },
91};
f471b2af 92
11a30cec 93/* ProtectKernelTunables= option and the related filesystem APIs */
34de407a 94static const MountEntry protect_kernel_tunables_table[] = {
c6232fb0
LP
95 { "/proc/sys", READONLY, false },
96 { "/proc/sysrq-trigger", READONLY, true },
97 { "/proc/latency_stats", READONLY, true },
98 { "/proc/mtrr", READONLY, true },
aa70f38b 99 { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */
c6232fb0
LP
100 { "/proc/acpi", READONLY, true },
101 { "/proc/timer_stats", READONLY, true },
102 { "/proc/asound", READONLY, true },
103 { "/proc/bus", READONLY, true },
104 { "/proc/fs", READONLY, true },
105 { "/proc/irq", READONLY, true },
106 { "/sys", READONLY, false },
107 { "/sys/kernel/debug", READONLY, true },
108 { "/sys/kernel/tracing", READONLY, true },
13a141f0 109 { "/sys/fs/bpf", READONLY, true },
c6232fb0 110 { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */
3a0bf6d6 111 { "/sys/fs/selinux", READWRITE, true },
11a30cec
DH
112};
113
c575770b 114/* ProtectKernelModules= option */
34de407a 115static const MountEntry protect_kernel_modules_table[] = {
349cc4a5 116#if HAVE_SPLIT_USR
c6232fb0 117 { "/lib/modules", INACCESSIBLE, true },
c575770b 118#endif
c6232fb0 119 { "/usr/lib/modules", INACCESSIBLE, true },
c575770b
DH
120};
121
b6c432ca
DH
122/*
123 * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
124 * system should be protected by ProtectSystem=
125 */
34de407a 126static const MountEntry protect_home_read_only_table[] = {
c6232fb0
LP
127 { "/home", READONLY, true },
128 { "/run/user", READONLY, true },
129 { "/root", READONLY, true },
b6c432ca
DH
130};
131
e4da7d8c
YW
132/* ProtectHome=tmpfs table */
133static const MountEntry protect_home_tmpfs_table[] = {
134 { "/home", TMPFS, true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME },
135 { "/run/user", TMPFS, true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME },
136 { "/root", TMPFS, true, .read_only = true, .options_const = "mode=0700", .flags = MS_NODEV|MS_STRICTATIME },
137};
138
b6c432ca 139/* ProtectHome=yes table */
34de407a 140static const MountEntry protect_home_yes_table[] = {
c6232fb0
LP
141 { "/home", INACCESSIBLE, true },
142 { "/run/user", INACCESSIBLE, true },
143 { "/root", INACCESSIBLE, true },
b6c432ca
DH
144};
145
f471b2af 146/* ProtectSystem=yes table */
34de407a 147static const MountEntry protect_system_yes_table[] = {
c6232fb0
LP
148 { "/usr", READONLY, false },
149 { "/boot", READONLY, true },
150 { "/efi", READONLY, true },
7486f305
AB
151#if HAVE_SPLIT_USR
152 { "/lib", READONLY, true },
153 { "/lib64", READONLY, true },
154 { "/bin", READONLY, true },
155 { "/sbin", READONLY, true },
156#endif
f471b2af
DH
157};
158
159/* ProtectSystem=full includes ProtectSystem=yes */
34de407a 160static const MountEntry protect_system_full_table[] = {
c6232fb0
LP
161 { "/usr", READONLY, false },
162 { "/boot", READONLY, true },
163 { "/efi", READONLY, true },
164 { "/etc", READONLY, false },
7486f305
AB
165#if HAVE_SPLIT_USR
166 { "/lib", READONLY, true },
167 { "/lib64", READONLY, true },
168 { "/bin", READONLY, true },
169 { "/sbin", READONLY, true },
170#endif
f471b2af
DH
171};
172
173/*
174 * ProtectSystem=strict table. In this strict mode, we mount everything
175 * read-only, except for /proc, /dev, /sys which are the kernel API VFS,
176 * which are left writable, but PrivateDevices= + ProtectKernelTunables=
177 * protect those, and these options should be fully orthogonal.
178 * (And of course /home and friends are also left writable, as ProtectHome=
179 * shall manage those, orthogonally).
180 */
34de407a 181static const MountEntry protect_system_strict_table[] = {
ddbe0412
LP
182 { "/", READONLY, false },
183 { "/proc", READWRITE, false }, /* ProtectKernelTunables= */
184 { "/sys", READWRITE, false }, /* ProtectKernelTunables= */
185 { "/dev", READWRITE, false }, /* PrivateDevices= */
186 { "/home", READWRITE, true }, /* ProtectHome= */
187 { "/run/user", READWRITE, true }, /* ProtectHome= */
188 { "/root", READWRITE, true }, /* ProtectHome= */
f471b2af
DH
189};
190
34de407a 191static const char *mount_entry_path(const MountEntry *p) {
f0a4feb0
DH
192 assert(p);
193
5327c910
LP
194 /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that,
195 * otherwise the stack/static ->path field is returned. */
f0a4feb0 196
5327c910 197 return p->path_malloc ?: p->path_const;
f0a4feb0
DH
198}
199
34de407a 200static bool mount_entry_read_only(const MountEntry *p) {
cfbeb4ef
LP
201 assert(p);
202
203 return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE);
204}
205
d2d6c096
LP
206static const char *mount_entry_source(const MountEntry *p) {
207 assert(p);
208
209 return p->source_malloc ?: p->source_const;
210}
211
2abd4e38
YW
212static const char *mount_entry_options(const MountEntry *p) {
213 assert(p);
214
215 return p->options_malloc ?: p->options_const;
216}
217
1eb7e08e
LP
218static void mount_entry_done(MountEntry *p) {
219 assert(p);
220
221 p->path_malloc = mfree(p->path_malloc);
222 p->source_malloc = mfree(p->source_malloc);
2abd4e38 223 p->options_malloc = mfree(p->options_malloc);
1eb7e08e
LP
224}
225
d18aff04 226static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) {
15ae422b
LP
227 char **i;
228
613b411c
LP
229 assert(p);
230
5327c910
LP
231 /* Adds a list of user-supplied READWRITE/READONLY/INACCESSIBLE entries */
232
15ae422b 233 STRV_FOREACH(i, strv) {
5327c910
LP
234 bool ignore = false, needs_prefix = false;
235 const char *e = *i;
15ae422b 236
5327c910
LP
237 /* Look for any prefixes */
238 if (startswith(e, "-")) {
239 e++;
9c94d52e 240 ignore = true;
ea92ae33 241 }
5327c910
LP
242 if (startswith(e, "+")) {
243 e++;
244 needs_prefix = true;
245 }
ea92ae33 246
5327c910 247 if (!path_is_absolute(e))
15ae422b
LP
248 return -EINVAL;
249
34de407a 250 *((*p)++) = (MountEntry) {
5327c910
LP
251 .path_const = e,
252 .mode = mode,
253 .ignore = ignore,
d18aff04 254 .has_prefix = !needs_prefix && !forcibly_require_prefix,
5327c910 255 };
15ae422b
LP
256 }
257
258 return 0;
259}
260
6c47cd7d
LP
261static int append_empty_dir_mounts(MountEntry **p, char **strv) {
262 char **i;
263
264 assert(p);
265
266 /* Adds tmpfs mounts to provide readable but empty directories. This is primarily used to implement the
267 * "/private/" boundary directories for DynamicUser=1. */
268
269 STRV_FOREACH(i, strv) {
270
271 *((*p)++) = (MountEntry) {
272 .path_const = *i,
273 .mode = EMPTY_DIR,
274 .ignore = false,
275 .has_prefix = false,
276 .read_only = true,
2abd4e38
YW
277 .options_const = "mode=755",
278 .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
6c47cd7d
LP
279 };
280 }
281
282 return 0;
283}
284
d2d6c096
LP
285static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n) {
286 unsigned i;
287
288 assert(p);
289
290 for (i = 0; i < n; i++) {
291 const BindMount *b = binds + i;
292
293 *((*p)++) = (MountEntry) {
294 .path_const = b->destination,
295 .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT,
296 .read_only = b->read_only,
297 .source_const = b->source,
4ca763a9 298 .ignore = b->ignore_enoent,
d2d6c096
LP
299 };
300 }
301
302 return 0;
303}
304
2abd4e38
YW
305static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, unsigned n) {
306 unsigned i;
307 int r;
308
309 assert(p);
310
311 for (i = 0; i < n; i++) {
312 const TemporaryFileSystem *t = tmpfs + i;
313 _cleanup_free_ char *o = NULL, *str = NULL;
314 unsigned long flags = MS_NODEV|MS_STRICTATIME;
315 bool ro = false;
316
317 if (!path_is_absolute(t->path))
318 return -EINVAL;
319
320 if (!isempty(t->options)) {
321 str = strjoin("mode=0755,", t->options);
322 if (!str)
323 return -ENOMEM;
324
325 r = mount_option_mangle(str, MS_NODEV|MS_STRICTATIME, &flags, &o);
326 if (r < 0)
327 return r;
328
329 ro = !!(flags & MS_RDONLY);
330 if (ro)
331 flags ^= MS_RDONLY;
332 }
333
334 *((*p)++) = (MountEntry) {
335 .path_const = t->path,
336 .mode = TMPFS,
337 .read_only = ro,
338 .options_malloc = o,
339 .flags = flags,
340 };
341
342 o = NULL;
343 }
344
345 return 0;
346}
347
34de407a 348static int append_static_mounts(MountEntry **p, const MountEntry *mounts, unsigned n, bool ignore_protect) {
f471b2af 349 unsigned i;
11a30cec
DH
350
351 assert(p);
f471b2af 352 assert(mounts);
11a30cec 353
5327c910 354 /* Adds a list of static pre-defined entries */
f471b2af 355
5327c910 356 for (i = 0; i < n; i++)
34de407a
LP
357 *((*p)++) = (MountEntry) {
358 .path_const = mount_entry_path(mounts+i),
5327c910
LP
359 .mode = mounts[i].mode,
360 .ignore = mounts[i].ignore || ignore_protect,
361 };
f471b2af
DH
362
363 return 0;
364}
365
34de407a 366static int append_protect_home(MountEntry **p, ProtectHome protect_home, bool ignore_protect) {
c575770b
DH
367 assert(p);
368
5327c910 369 switch (protect_home) {
b6c432ca 370
5327c910 371 case PROTECT_HOME_NO:
b6c432ca
DH
372 return 0;
373
b6c432ca 374 case PROTECT_HOME_READ_ONLY:
5327c910
LP
375 return append_static_mounts(p, protect_home_read_only_table, ELEMENTSOF(protect_home_read_only_table), ignore_protect);
376
e4da7d8c
YW
377 case PROTECT_HOME_TMPFS:
378 return append_static_mounts(p, protect_home_tmpfs_table, ELEMENTSOF(protect_home_tmpfs_table), ignore_protect);
379
b6c432ca 380 case PROTECT_HOME_YES:
5327c910
LP
381 return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect);
382
b6c432ca 383 default:
5327c910 384 assert_not_reached("Unexpected ProtectHome= value");
b6c432ca 385 }
b6c432ca
DH
386}
387
34de407a 388static int append_protect_system(MountEntry **p, ProtectSystem protect_system, bool ignore_protect) {
f471b2af
DH
389 assert(p);
390
5327c910
LP
391 switch (protect_system) {
392
393 case PROTECT_SYSTEM_NO:
f471b2af
DH
394 return 0;
395
f471b2af 396 case PROTECT_SYSTEM_STRICT:
5327c910
LP
397 return append_static_mounts(p, protect_system_strict_table, ELEMENTSOF(protect_system_strict_table), ignore_protect);
398
f471b2af 399 case PROTECT_SYSTEM_YES:
5327c910
LP
400 return append_static_mounts(p, protect_system_yes_table, ELEMENTSOF(protect_system_yes_table), ignore_protect);
401
f471b2af 402 case PROTECT_SYSTEM_FULL:
5327c910
LP
403 return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect);
404
f471b2af 405 default:
5327c910 406 assert_not_reached("Unexpected ProtectSystem= value");
f471b2af 407 }
11a30cec
DH
408}
409
c17ec25e 410static int mount_path_compare(const void *a, const void *b) {
34de407a 411 const MountEntry *p = a, *q = b;
a0827e2b 412 int d;
15ae422b 413
6ee1a919 414 /* If the paths are not equal, then order prefixes first */
34de407a 415 d = path_compare(mount_entry_path(p), mount_entry_path(q));
6ee1a919
LP
416 if (d != 0)
417 return d;
15ae422b 418
6ee1a919
LP
419 /* If the paths are equal, check the mode */
420 if (p->mode < q->mode)
421 return -1;
15ae422b 422
6ee1a919
LP
423 if (p->mode > q->mode)
424 return 1;
15ae422b 425
6ee1a919 426 return 0;
15ae422b
LP
427}
428
34de407a 429static int prefix_where_needed(MountEntry *m, unsigned n, const char *root_directory) {
5327c910
LP
430 unsigned i;
431
432 /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs
433 * that. */
434
435 if (!root_directory)
436 return 0;
437
438 for (i = 0; i < n; i++) {
439 char *s;
440
441 if (m[i].has_prefix)
442 continue;
443
34de407a 444 s = prefix_root(root_directory, mount_entry_path(m+i));
5327c910
LP
445 if (!s)
446 return -ENOMEM;
447
e282f51f 448 free_and_replace(m[i].path_malloc, s);
5327c910
LP
449 m[i].has_prefix = true;
450 }
451
452 return 0;
453}
454
34de407a
LP
455static void drop_duplicates(MountEntry *m, unsigned *n) {
456 MountEntry *f, *t, *previous;
15ae422b 457
c17ec25e 458 assert(m);
15ae422b 459 assert(n);
15ae422b 460
fe3c2583
LP
461 /* Drops duplicate entries. Expects that the array is properly ordered already. */
462
1d54cd5d 463 for (f = m, t = m, previous = NULL; f < m + *n; f++) {
15ae422b 464
fe3c2583
LP
465 /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
466 * above. */
34de407a
LP
467 if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) {
468 log_debug("%s is duplicate.", mount_entry_path(f));
469 previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */
1eb7e08e 470 mount_entry_done(f);
15ae422b 471 continue;
fe3c2583 472 }
15ae422b 473
e2d7c1a0 474 *t = *f;
15ae422b 475 previous = t;
fe3c2583
LP
476 t++;
477 }
478
479 *n = t - m;
480}
481
34de407a
LP
482static void drop_inaccessible(MountEntry *m, unsigned *n) {
483 MountEntry *f, *t;
fe3c2583
LP
484 const char *clear = NULL;
485
486 assert(m);
487 assert(n);
488
489 /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
490 * ordered already. */
491
1d54cd5d 492 for (f = m, t = m; f < m + *n; f++) {
fe3c2583
LP
493
494 /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
495 * it, as inaccessible paths really should drop the entire subtree. */
34de407a
LP
496 if (clear && path_startswith(mount_entry_path(f), clear)) {
497 log_debug("%s is masked by %s.", mount_entry_path(f), clear);
1eb7e08e 498 mount_entry_done(f);
fe3c2583
LP
499 continue;
500 }
15ae422b 501
34de407a 502 clear = f->mode == INACCESSIBLE ? mount_entry_path(f) : NULL;
fe3c2583
LP
503
504 *t = *f;
15ae422b
LP
505 t++;
506 }
507
c17ec25e 508 *n = t - m;
15ae422b
LP
509}
510
34de407a
LP
511static void drop_nop(MountEntry *m, unsigned *n) {
512 MountEntry *f, *t;
7648a565
LP
513
514 assert(m);
515 assert(n);
516
517 /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
518 * list is ordered by prefixes. */
519
1d54cd5d 520 for (f = m, t = m; f < m + *n; f++) {
7648a565
LP
521
522 /* Only suppress such subtrees for READONLY and READWRITE entries */
523 if (IN_SET(f->mode, READONLY, READWRITE)) {
34de407a 524 MountEntry *p;
7648a565
LP
525 bool found = false;
526
527 /* Now let's find the first parent of the entry we are looking at. */
528 for (p = t-1; p >= m; p--) {
34de407a 529 if (path_startswith(mount_entry_path(f), mount_entry_path(p))) {
7648a565
LP
530 found = true;
531 break;
532 }
533 }
534
535 /* We found it, let's see if it's the same mode, if so, we can drop this entry */
536 if (found && p->mode == f->mode) {
34de407a 537 log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p));
1eb7e08e 538 mount_entry_done(f);
7648a565
LP
539 continue;
540 }
541 }
542
543 *t = *f;
544 t++;
545 }
546
547 *n = t - m;
548}
549
34de407a
LP
550static void drop_outside_root(const char *root_directory, MountEntry *m, unsigned *n) {
551 MountEntry *f, *t;
cd2902c9
LP
552
553 assert(m);
554 assert(n);
555
1d54cd5d 556 /* Nothing to do */
cd2902c9
LP
557 if (!root_directory)
558 return;
559
560 /* Drops all mounts that are outside of the root directory. */
561
1d54cd5d 562 for (f = m, t = m; f < m + *n; f++) {
cd2902c9 563
34de407a
LP
564 if (!path_startswith(mount_entry_path(f), root_directory)) {
565 log_debug("%s is outside of root directory.", mount_entry_path(f));
1eb7e08e 566 mount_entry_done(f);
cd2902c9
LP
567 continue;
568 }
569
570 *t = *f;
571 t++;
572 }
573
574 *n = t - m;
575}
576
414b304b 577static int clone_device_node(const char *d, const char *temporary_mount) {
6f7f3a33 578 const char *dn;
b5e99f23
ДГ
579 struct stat st;
580 int r;
581
414b304b 582 if (stat(d, &st) < 0) {
b5e99f23
ДГ
583 if (errno == ENOENT)
584 return 0;
585 return -errno;
586 }
587
588 if (!S_ISBLK(st.st_mode) &&
589 !S_ISCHR(st.st_mode))
590 return -EINVAL;
591
592 if (st.st_rdev == 0)
593 return 0;
594
6f7f3a33 595 dn = strjoina(temporary_mount, d);
b5e99f23
ДГ
596
597 mac_selinux_create_file_prepare(d, st.st_mode);
598 r = mknod(dn, st.st_mode, st.st_rdev);
599 mac_selinux_create_file_clear();
b5e99f23 600 if (r < 0)
225874dc 601 return log_debug_errno(errno, "mknod failed for %s: %m", d);
b5e99f23 602
98b1d2b8 603 return 1;
b5e99f23
ДГ
604}
605
5d997827 606static int mount_private_dev(MountEntry *m) {
7f112f50
LP
607 static const char devnodes[] =
608 "/dev/null\0"
609 "/dev/zero\0"
610 "/dev/full\0"
611 "/dev/random\0"
612 "/dev/urandom\0"
613 "/dev/tty\0";
614
2b85f4e1 615 char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
63cc4c31 616 const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
7f112f50
LP
617 _cleanup_umask_ mode_t u;
618 int r;
619
620 assert(m);
621
622 u = umask(0000);
623
2b85f4e1
LP
624 if (!mkdtemp(temporary_mount))
625 return -errno;
626
63c372cb 627 dev = strjoina(temporary_mount, "/dev");
dc751688 628 (void) mkdir(dev, 0755);
737ba3c8 629 if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) {
2b85f4e1
LP
630 r = -errno;
631 goto fail;
632 }
633
63c372cb 634 devpts = strjoina(temporary_mount, "/dev/pts");
dc751688 635 (void) mkdir(devpts, 0755);
2b85f4e1
LP
636 if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
637 r = -errno;
638 goto fail;
639 }
640
414b304b
ДГ
641 /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx
642 * when /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible
643 * thus, in that case make a clone
644 *
645 * in nspawn and other containers it will be a symlink, in that case make it a symlink
646 */
36ce7110
LP
647 r = is_symlink("/dev/ptmx");
648 if (r < 0)
3164e3cb 649 goto fail;
36ce7110 650 if (r > 0) {
414b304b
ДГ
651 devptmx = strjoina(temporary_mount, "/dev/ptmx");
652 if (symlink("pts/ptmx", devptmx) < 0) {
653 r = -errno;
654 goto fail;
655 }
656 } else {
657 r = clone_device_node("/dev/ptmx", temporary_mount);
152c475f
LP
658 if (r < 0)
659 goto fail;
660 if (r == 0) {
661 r = -ENXIO;
414b304b 662 goto fail;
152c475f 663 }
414b304b 664 }
e06b6479 665
63c372cb 666 devshm = strjoina(temporary_mount, "/dev/shm");
8d953682 667 (void) mkdir(devshm, 0755);
2b85f4e1
LP
668 r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
669 if (r < 0) {
670 r = -errno;
671 goto fail;
672 }
673
63c372cb 674 devmqueue = strjoina(temporary_mount, "/dev/mqueue");
dc751688 675 (void) mkdir(devmqueue, 0755);
3164e3cb 676 (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
2b85f4e1 677
63c372cb 678 devhugepages = strjoina(temporary_mount, "/dev/hugepages");
dc751688 679 (void) mkdir(devhugepages, 0755);
3164e3cb 680 (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
2b85f4e1 681
63c372cb 682 devlog = strjoina(temporary_mount, "/dev/log");
3164e3cb 683 (void) symlink("/run/systemd/journal/dev-log", devlog);
82d25240 684
7f112f50 685 NULSTR_FOREACH(d, devnodes) {
b5e99f23
ДГ
686 r = clone_device_node(d, temporary_mount);
687 if (r < 0)
2b85f4e1 688 goto fail;
7f112f50
LP
689 }
690
03cfe0d5 691 dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
7f112f50 692
ee818b89
AC
693 /* Create the /dev directory if missing. It is more likely to be
694 * missing when the service is started with RootDirectory. This is
695 * consistent with mount units creating the mount points when missing.
696 */
34de407a 697 (void) mkdir_p_label(mount_entry_path(m), 0755);
ee818b89 698
9e5f8252 699 /* Unmount everything in old /dev */
34de407a
LP
700 umount_recursive(mount_entry_path(m), 0);
701 if (mount(dev, mount_entry_path(m), NULL, MS_MOVE, NULL) < 0) {
2b85f4e1
LP
702 r = -errno;
703 goto fail;
704 }
7f112f50 705
2b85f4e1
LP
706 rmdir(dev);
707 rmdir(temporary_mount);
7f112f50 708
2b85f4e1 709 return 0;
7f112f50 710
2b85f4e1
LP
711fail:
712 if (devpts)
713 umount(devpts);
7f112f50 714
2b85f4e1
LP
715 if (devshm)
716 umount(devshm);
7f112f50 717
2b85f4e1
LP
718 if (devhugepages)
719 umount(devhugepages);
7f112f50 720
2b85f4e1
LP
721 if (devmqueue)
722 umount(devmqueue);
7f112f50 723
d267c5aa
ZJS
724 umount(dev);
725 rmdir(dev);
2b85f4e1 726 rmdir(temporary_mount);
7f112f50 727
2b85f4e1 728 return r;
7f112f50
LP
729}
730
2a2969fd 731static int mount_bind_dev(const MountEntry *m) {
5d997827
LP
732 int r;
733
734 assert(m);
735
736 /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's
737 * /dev. This is only used when RootDirectory= is set. */
738
645767d6
LP
739 (void) mkdir_p_label(mount_entry_path(m), 0755);
740
5d997827
LP
741 r = path_is_mount_point(mount_entry_path(m), NULL, 0);
742 if (r < 0)
743 return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m");
744 if (r > 0) /* make this a NOP if /dev is already a mount point */
745 return 0;
746
747 if (mount("/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
748 return log_debug_errno(errno, "Failed to bind mount %s: %m", mount_entry_path(m));
749
750 return 1;
751}
752
2a2969fd 753static int mount_sysfs(const MountEntry *m) {
5d997827
LP
754 int r;
755
756 assert(m);
757
645767d6
LP
758 (void) mkdir_p_label(mount_entry_path(m), 0755);
759
5d997827
LP
760 r = path_is_mount_point(mount_entry_path(m), NULL, 0);
761 if (r < 0)
762 return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m");
763 if (r > 0) /* make this a NOP if /sys is already a mount point */
764 return 0;
765
766 /* Bind mount the host's version so that we get all child mounts of it, too. */
767 if (mount("/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
768 return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
769
770 return 1;
771}
772
2a2969fd 773static int mount_procfs(const MountEntry *m) {
5d997827
LP
774 int r;
775
776 assert(m);
777
645767d6
LP
778 (void) mkdir_p_label(mount_entry_path(m), 0755);
779
5d997827
LP
780 r = path_is_mount_point(mount_entry_path(m), NULL, 0);
781 if (r < 0)
782 return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
783 if (r > 0) /* make this a NOP if /proc is already a mount point */
784 return 0;
785
786 /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in one */
787 if (mount("proc", mount_entry_path(m), "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0)
788 return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
789
790 return 1;
791}
792
2abd4e38 793static int mount_tmpfs(const MountEntry *m) {
6c47cd7d
LP
794 assert(m);
795
2abd4e38 796 /* First, get rid of everything that is below if there is anything. Then, overmount with our new tmpfs */
6c47cd7d
LP
797
798 (void) mkdir_p_label(mount_entry_path(m), 0755);
799 (void) umount_recursive(mount_entry_path(m), 0);
800
2abd4e38 801 if (mount("tmpfs", mount_entry_path(m), "tmpfs", m->flags, mount_entry_options(m)) < 0)
6c47cd7d
LP
802 return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
803
804 return 1;
805}
806
d2d6c096
LP
807static int mount_entry_chase(
808 const char *root_directory,
2a2969fd 809 const MountEntry *m,
d2d6c096 810 const char *path,
4ca763a9 811 bool chase_nonexistent,
d2d6c096
LP
812 char **location) {
813
8fceda93
LP
814 char *chased;
815 int r;
816
817 assert(m);
818
819 /* Since mount() will always follow symlinks and we need to take the different root directory into account we
d2d6c096
LP
820 * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if
821 * that applies). The result is stored in "location". */
8fceda93 822
4ca763a9 823 r = chase_symlinks(path, root_directory, chase_nonexistent ? CHASE_NONEXISTENT : 0, &chased);
8fceda93 824 if (r == -ENOENT && m->ignore) {
d2d6c096 825 log_debug_errno(r, "Path %s does not exist, ignoring.", path);
8fceda93
LP
826 return 0;
827 }
828 if (r < 0)
d2d6c096 829 return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path);
8fceda93 830
d2d6c096 831 log_debug("Followed symlinks %s → %s.", path, chased);
8fceda93 832
d2d6c096
LP
833 free(*location);
834 *location = chased;
8fceda93
LP
835
836 return 1;
837}
838
ac0930c8 839static int apply_mount(
8fceda93 840 const char *root_directory,
89bd586c 841 MountEntry *m) {
ac0930c8 842
a227a4be 843 bool rbind = true, make = false;
15ae422b 844 const char *what;
15ae422b 845 int r;
15ae422b 846
c17ec25e 847 assert(m);
15ae422b 848
4ca763a9 849 r = mount_entry_chase(root_directory, m, mount_entry_path(m), !IN_SET(m->mode, INACCESSIBLE, READONLY, READWRITE), &m->path_malloc);
8fceda93
LP
850 if (r <= 0)
851 return r;
852
34de407a 853 log_debug("Applying namespace mount on %s", mount_entry_path(m));
fe3c2583 854
c17ec25e 855 switch (m->mode) {
15ae422b 856
160cfdbe
LP
857 case INACCESSIBLE: {
858 struct stat target;
6d313367
LP
859
860 /* First, get rid of everything that is below if there
861 * is anything... Then, overmount it with an
c4b41707 862 * inaccessible path. */
34de407a 863 (void) umount_recursive(mount_entry_path(m), 0);
6d313367 864
34de407a
LP
865 if (lstat(mount_entry_path(m), &target) < 0)
866 return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m));
15ae422b 867
c4b41707 868 what = mode_to_inaccessible_node(target.st_mode);
5fd7cf6f
LP
869 if (!what) {
870 log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed");
c4b41707
AP
871 return -ELOOP;
872 }
873 break;
160cfdbe 874 }
fe3c2583 875
15ae422b 876 case READONLY:
15ae422b 877 case READWRITE:
8fceda93 878 r = path_is_mount_point(mount_entry_path(m), root_directory, 0);
d944dc95 879 if (r < 0)
34de407a 880 return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m));
6b7c9f8b
LP
881 if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
882 return 0;
6b7c9f8b 883 /* This isn't a mount point yet, let's make it one. */
34de407a 884 what = mount_entry_path(m);
6b7c9f8b 885 break;
15ae422b 886
d2d6c096
LP
887 case BIND_MOUNT:
888 rbind = false;
d2d6c096 889
4831981d 890 _fallthrough_;
d2d6c096
LP
891 case BIND_MOUNT_RECURSIVE:
892 /* Also chase the source mount */
5d997827 893
4ca763a9 894 r = mount_entry_chase(root_directory, m, mount_entry_source(m), false, &m->source_malloc);
d2d6c096
LP
895 if (r <= 0)
896 return r;
897
898 what = mount_entry_source(m);
a227a4be 899 make = true;
d2d6c096
LP
900 break;
901
6c47cd7d 902 case EMPTY_DIR:
2abd4e38
YW
903 case TMPFS:
904 return mount_tmpfs(m);
6c47cd7d 905
ac0930c8 906 case PRIVATE_TMP:
89bd586c 907 what = mount_entry_source(m);
a227a4be 908 make = true;
15ae422b 909 break;
e364ad06 910
d6797c92 911 case PRIVATE_DEV:
5d997827
LP
912 return mount_private_dev(m);
913
914 case BIND_DEV:
915 return mount_bind_dev(m);
916
917 case SYSFS:
918 return mount_sysfs(m);
919
920 case PROCFS:
921 return mount_procfs(m);
d6797c92 922
e364ad06
LP
923 default:
924 assert_not_reached("Unknown mode");
15ae422b
LP
925 }
926
ac0930c8 927 assert(what);
15ae422b 928
a227a4be
LP
929 if (mount(what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL) < 0) {
930 bool try_again = false;
931 r = -errno;
932
933 if (r == -ENOENT && make) {
934 struct stat st;
935
936 /* Hmm, either the source or the destination are missing. Let's see if we can create the destination, then try again */
937
938 if (stat(what, &st) >= 0) {
939
940 (void) mkdir_parents(mount_entry_path(m), 0755);
941
942 if (S_ISDIR(st.st_mode))
943 try_again = mkdir(mount_entry_path(m), 0755) >= 0;
944 else
945 try_again = touch(mount_entry_path(m)) >= 0;
946 }
947 }
948
949 if (try_again) {
950 if (mount(what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL) < 0)
951 r = -errno;
952 else
953 r = 0;
954 }
955
956 if (r < 0)
957 return log_debug_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m));
958 }
6b7c9f8b 959
34de407a 960 log_debug("Successfully mounted %s to %s", what, mount_entry_path(m));
6b7c9f8b 961 return 0;
ac0930c8 962}
15ae422b 963
2a2969fd 964static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) {
6b7c9f8b 965 int r = 0;
15ae422b 966
c17ec25e 967 assert(m);
ac9de0b3 968 assert(proc_self_mountinfo);
ac0930c8 969
2abd4e38
YW
970 if (mount_entry_read_only(m)) {
971 if (IN_SET(m->mode, EMPTY_DIR, TMPFS)) {
972 /* Make superblock readonly */
973 if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT | MS_RDONLY | m->flags, mount_entry_options(m)) < 0)
974 r = -errno;
975 } else
976 r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), true, blacklist, proc_self_mountinfo);
977 } else if (m->mode == PRIVATE_DEV) {
978 /* Superblock can be readonly but the submounts can't */
34de407a 979 if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
6b7c9f8b 980 r = -errno;
737ba3c8 981 } else
6b7c9f8b
LP
982 return 0;
983
984 /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only
985 * already stays this way. This improves compatibility with container managers, where we won't attempt to undo
986 * read-only mounts already applied. */
ac0930c8 987
8fceda93
LP
988 if (r == -ENOENT && m->ignore)
989 r = 0;
5327c910 990
1d54cd5d 991 return r;
d944dc95
LP
992}
993
bb0ff3fb 994static bool namespace_info_mount_apivfs(const char *root_directory, const NamespaceInfo *ns_info) {
5d997827
LP
995 assert(ns_info);
996
9c988f93
DH
997 /*
998 * ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=,
999 * since to protect the API VFS mounts, they need to be around in the
1000 * first place... and RootDirectory= or RootImage= need to be set.
1001 */
5d997827 1002
9c988f93
DH
1003 /* root_directory should point to a mount point */
1004 return root_directory &&
1005 (ns_info->mount_apivfs ||
1006 ns_info->protect_control_groups ||
1007 ns_info->protect_kernel_tunables);
5d997827
LP
1008}
1009
2652c6c1 1010static unsigned namespace_calculate_mounts(
9c988f93 1011 const char* root_directory,
bb0ff3fb 1012 const NamespaceInfo *ns_info,
2652c6c1
DH
1013 char** read_write_paths,
1014 char** read_only_paths,
1015 char** inaccessible_paths,
6c47cd7d 1016 char** empty_directories,
d2d6c096 1017 unsigned n_bind_mounts,
2abd4e38 1018 unsigned n_temporary_filesystems,
2652c6c1
DH
1019 const char* tmp_dir,
1020 const char* var_tmp_dir,
2652c6c1
DH
1021 ProtectHome protect_home,
1022 ProtectSystem protect_system) {
1023
b6c432ca 1024 unsigned protect_home_cnt;
f471b2af
DH
1025 unsigned protect_system_cnt =
1026 (protect_system == PROTECT_SYSTEM_STRICT ?
1027 ELEMENTSOF(protect_system_strict_table) :
1028 ((protect_system == PROTECT_SYSTEM_FULL) ?
1029 ELEMENTSOF(protect_system_full_table) :
1030 ((protect_system == PROTECT_SYSTEM_YES) ?
1031 ELEMENTSOF(protect_system_yes_table) : 0)));
1032
b6c432ca
DH
1033 protect_home_cnt =
1034 (protect_home == PROTECT_HOME_YES ?
1035 ELEMENTSOF(protect_home_yes_table) :
1036 ((protect_home == PROTECT_HOME_READ_ONLY) ?
e4da7d8c
YW
1037 ELEMENTSOF(protect_home_read_only_table) :
1038 ((protect_home == PROTECT_HOME_TMPFS) ?
1039 ELEMENTSOF(protect_home_tmpfs_table) : 0)));
b6c432ca 1040
2652c6c1
DH
1041 return !!tmp_dir + !!var_tmp_dir +
1042 strv_length(read_write_paths) +
1043 strv_length(read_only_paths) +
1044 strv_length(inaccessible_paths) +
6c47cd7d 1045 strv_length(empty_directories) +
d2d6c096 1046 n_bind_mounts +
2abd4e38 1047 n_temporary_filesystems +
c575770b
DH
1048 ns_info->private_dev +
1049 (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
1050 (ns_info->protect_control_groups ? 1 : 0) +
1051 (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
5d997827 1052 protect_home_cnt + protect_system_cnt +
9c988f93 1053 (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table) : 0);
2652c6c1
DH
1054}
1055
613b411c 1056int setup_namespace(
ee818b89 1057 const char* root_directory,
915e6d16 1058 const char* root_image,
bb0ff3fb 1059 const NamespaceInfo *ns_info,
2a624c36
AP
1060 char** read_write_paths,
1061 char** read_only_paths,
1062 char** inaccessible_paths,
6c47cd7d 1063 char** empty_directories,
d2d6c096
LP
1064 const BindMount *bind_mounts,
1065 unsigned n_bind_mounts,
2abd4e38
YW
1066 const TemporaryFileSystem *temporary_filesystems,
1067 unsigned n_temporary_filesystems,
a004cb4c
LP
1068 const char* tmp_dir,
1069 const char* var_tmp_dir,
1b8689f9
LP
1070 ProtectHome protect_home,
1071 ProtectSystem protect_system,
915e6d16
LP
1072 unsigned long mount_flags,
1073 DissectImageFlags dissect_image_flags) {
15ae422b 1074
915e6d16 1075 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
78ebe980 1076 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
915e6d16 1077 _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
78ebe980 1078 _cleanup_free_ void *root_hash = NULL;
34de407a 1079 MountEntry *m, *mounts = NULL;
78ebe980 1080 size_t root_hash_size = 0;
d944dc95 1081 bool make_slave = false;
e908468b 1082 const char *root;
f0a4feb0 1083 unsigned n_mounts;
d18aff04 1084 bool require_prefix = false;
c17ec25e 1085 int r = 0;
15ae422b 1086
915e6d16
LP
1087 assert(ns_info);
1088
613b411c 1089 if (mount_flags == 0)
c17ec25e 1090 mount_flags = MS_SHARED;
ac0930c8 1091
915e6d16
LP
1092 if (root_image) {
1093 dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
1094
1095 if (protect_system == PROTECT_SYSTEM_STRICT && strv_isempty(read_write_paths))
1096 dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
1097
1098 r = loop_device_make_by_path(root_image,
1099 dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
1100 &loop_device);
1101 if (r < 0)
1102 return r;
1103
78ebe980
LP
1104 r = root_hash_load(root_image, &root_hash, &root_hash_size);
1105 if (r < 0)
1106 return r;
1107
1108 r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
1109 if (r < 0)
1110 return r;
1111
1112 r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
915e6d16
LP
1113 if (r < 0)
1114 return r;
915e6d16
LP
1115 }
1116
e908468b
LP
1117 if (root_directory)
1118 root = root_directory;
2abd4e38 1119 else if (root_image || n_bind_mounts > 0 || n_temporary_filesystems > 0) {
e908468b
LP
1120
1121 /* If we are booting from an image, create a mount point for the image, if it's still missing. We use
1122 * the same mount point for all images, which is safe, since they all live in their own namespaces
1123 * after all, and hence won't see each other. We also use such a root directory whenever there are bind
1124 * mounts configured, so that their source mounts are never obstructed by mounts we already applied
1125 * while we are applying them. */
1126
1127 root = "/run/systemd/unit-root";
1128 (void) mkdir_label(root, 0700);
d18aff04 1129 require_prefix = true;
e908468b
LP
1130 } else
1131 root = NULL;
1132
cfbeb4ef 1133 n_mounts = namespace_calculate_mounts(
e908468b 1134 root,
cfbeb4ef
LP
1135 ns_info,
1136 read_write_paths,
1137 read_only_paths,
1138 inaccessible_paths,
6c47cd7d 1139 empty_directories,
f5c52a77 1140 n_bind_mounts,
2abd4e38 1141 n_temporary_filesystems,
cfbeb4ef
LP
1142 tmp_dir, var_tmp_dir,
1143 protect_home, protect_system);
613b411c 1144
2652c6c1 1145 /* Set mount slave mode */
e908468b 1146 if (root || n_mounts > 0)
d944dc95
LP
1147 make_slave = true;
1148
f0a4feb0 1149 if (n_mounts > 0) {
34de407a 1150 m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry));
d18aff04 1151 r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix);
613b411c 1152 if (r < 0)
f0a4feb0 1153 goto finish;
613b411c 1154
d18aff04 1155 r = append_access_mounts(&m, read_only_paths, READONLY, require_prefix);
613b411c 1156 if (r < 0)
f0a4feb0 1157 goto finish;
613b411c 1158
d18aff04 1159 r = append_access_mounts(&m, inaccessible_paths, INACCESSIBLE, require_prefix);
613b411c 1160 if (r < 0)
f0a4feb0 1161 goto finish;
7ff7394d 1162
6c47cd7d
LP
1163 r = append_empty_dir_mounts(&m, empty_directories);
1164 if (r < 0)
1165 goto finish;
1166
d2d6c096
LP
1167 r = append_bind_mounts(&m, bind_mounts, n_bind_mounts);
1168 if (r < 0)
1169 goto finish;
1170
2abd4e38
YW
1171 r = append_tmpfs_mounts(&m, temporary_filesystems, n_temporary_filesystems);
1172 if (r < 0)
1173 goto finish;
1174
613b411c 1175 if (tmp_dir) {
34de407a 1176 *(m++) = (MountEntry) {
5327c910
LP
1177 .path_const = "/tmp",
1178 .mode = PRIVATE_TMP,
89bd586c 1179 .source_const = tmp_dir,
5327c910 1180 };
613b411c 1181 }
7ff7394d 1182
613b411c 1183 if (var_tmp_dir) {
34de407a 1184 *(m++) = (MountEntry) {
5327c910 1185 .path_const = "/var/tmp",
89bd586c
YW
1186 .mode = PRIVATE_TMP,
1187 .source_const = var_tmp_dir,
5327c910 1188 };
7ff7394d 1189 }
ac0930c8 1190
c575770b 1191 if (ns_info->private_dev) {
34de407a 1192 *(m++) = (MountEntry) {
5327c910
LP
1193 .path_const = "/dev",
1194 .mode = PRIVATE_DEV,
1195 };
7f112f50
LP
1196 }
1197
c575770b 1198 if (ns_info->protect_kernel_tunables) {
5327c910 1199 r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table), ns_info->ignore_protect_paths);
c575770b 1200 if (r < 0)
f0a4feb0 1201 goto finish;
c575770b
DH
1202 }
1203
1204 if (ns_info->protect_kernel_modules) {
5327c910 1205 r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table), ns_info->ignore_protect_paths);
c575770b 1206 if (r < 0)
f0a4feb0 1207 goto finish;
c575770b 1208 }
59eeb84b 1209
c575770b 1210 if (ns_info->protect_control_groups) {
34de407a 1211 *(m++) = (MountEntry) {
5327c910
LP
1212 .path_const = "/sys/fs/cgroup",
1213 .mode = READONLY,
1214 };
59eeb84b
LP
1215 }
1216
5327c910 1217 r = append_protect_home(&m, protect_home, ns_info->ignore_protect_paths);
b6c432ca 1218 if (r < 0)
f0a4feb0 1219 goto finish;
417116f2 1220
5327c910 1221 r = append_protect_system(&m, protect_system, false);
f471b2af 1222 if (r < 0)
f0a4feb0 1223 goto finish;
417116f2 1224
e908468b 1225 if (namespace_info_mount_apivfs(root, ns_info)) {
5d997827
LP
1226 r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
1227 if (r < 0)
1228 goto finish;
1229 }
1230
f0a4feb0 1231 assert(mounts + n_mounts == m);
ac0930c8 1232
5327c910 1233 /* Prepend the root directory where that's necessary */
e908468b 1234 r = prefix_where_needed(mounts, n_mounts, root);
5327c910
LP
1235 if (r < 0)
1236 goto finish;
1237
34de407a 1238 qsort(mounts, n_mounts, sizeof(MountEntry), mount_path_compare);
fe3c2583 1239
f0a4feb0 1240 drop_duplicates(mounts, &n_mounts);
e908468b 1241 drop_outside_root(root, mounts, &n_mounts);
f0a4feb0
DH
1242 drop_inaccessible(mounts, &n_mounts);
1243 drop_nop(mounts, &n_mounts);
15ae422b
LP
1244 }
1245
d944dc95
LP
1246 if (unshare(CLONE_NEWNS) < 0) {
1247 r = -errno;
1248 goto finish;
1249 }
1e4e94c8 1250
d944dc95 1251 if (make_slave) {
c2c13f2d
LP
1252 /* Remount / as SLAVE so that nothing now mounted in the namespace
1253 shows up in the parent */
d944dc95
LP
1254 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
1255 r = -errno;
1256 goto finish;
1257 }
ee818b89
AC
1258 }
1259
915e6d16 1260 if (root_image) {
e908468b 1261 /* A root image is specified, mount it to the right place */
2d3a5a73 1262 r = dissected_image_mount(dissected_image, root, UID_INVALID, dissect_image_flags);
915e6d16
LP
1263 if (r < 0)
1264 goto finish;
1265
07ce7407
TM
1266 if (decrypted_image) {
1267 r = decrypted_image_relinquish(decrypted_image);
1268 if (r < 0)
1269 goto finish;
1270 }
78ebe980 1271
915e6d16
LP
1272 loop_device_relinquish(loop_device);
1273
1274 } else if (root_directory) {
1275
e908468b
LP
1276 /* A root directory is specified. Turn its directory into bind mount, if it isn't one yet. */
1277 r = path_is_mount_point(root, NULL, AT_SYMLINK_FOLLOW);
8f1ad200 1278 if (r < 0)
d944dc95 1279 goto finish;
8f1ad200 1280 if (r == 0) {
e908468b 1281 if (mount(root, root, NULL, MS_BIND|MS_REC, NULL) < 0) {
8f1ad200
LP
1282 r = -errno;
1283 goto finish;
1284 }
d944dc95 1285 }
e908468b
LP
1286
1287 } else if (root) {
1288
1289 /* Let's mount the main root directory to the root directory to use */
1290 if (mount("/", root, NULL, MS_BIND|MS_REC, NULL) < 0) {
1291 r = -errno;
1292 goto finish;
1293 }
ee818b89 1294 }
c2c13f2d 1295
4e0c20de
LP
1296 /* Try to set up the new root directory before mounting anything else there. */
1297 if (root_image || root_directory)
1298 (void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
1299
f0a4feb0 1300 if (n_mounts > 0) {
ac9de0b3 1301 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
6b7c9f8b
LP
1302 char **blacklist;
1303 unsigned j;
1304
ac9de0b3
TR
1305 /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc.
1306 * For example, this is the case with the option: 'InaccessiblePaths=/proc' */
1307 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
1308 if (!proc_self_mountinfo) {
1309 r = -errno;
1310 goto finish;
1311 }
1312
6b7c9f8b 1313 /* First round, add in all special mounts we need */
f0a4feb0 1314 for (m = mounts; m < mounts + n_mounts; ++m) {
89bd586c 1315 r = apply_mount(root, m);
c2c13f2d 1316 if (r < 0)
d944dc95 1317 goto finish;
c2c13f2d 1318 }
15ae422b 1319
6b7c9f8b 1320 /* Create a blacklist we can pass to bind_mount_recursive() */
f0a4feb0
DH
1321 blacklist = newa(char*, n_mounts+1);
1322 for (j = 0; j < n_mounts; j++)
34de407a 1323 blacklist[j] = (char*) mount_entry_path(mounts+j);
6b7c9f8b
LP
1324 blacklist[j] = NULL;
1325
1326 /* Second round, flip the ro bits if necessary. */
f0a4feb0 1327 for (m = mounts; m < mounts + n_mounts; ++m) {
ac9de0b3 1328 r = make_read_only(m, blacklist, proc_self_mountinfo);
c2c13f2d 1329 if (r < 0)
d944dc95 1330 goto finish;
c2c13f2d 1331 }
15ae422b
LP
1332 }
1333
e908468b 1334 if (root) {
ee818b89 1335 /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
e908468b 1336 r = mount_move_root(root);
d944dc95
LP
1337 if (r < 0)
1338 goto finish;
ee818b89
AC
1339 }
1340
55fe7432 1341 /* Remount / as the desired mode. Note that this will not
c2c13f2d
LP
1342 * reestablish propagation from our side to the host, since
1343 * what's disconnected is disconnected. */
d944dc95
LP
1344 if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
1345 r = -errno;
1346 goto finish;
1347 }
15ae422b 1348
d944dc95 1349 r = 0;
15ae422b 1350
d944dc95 1351finish:
f0a4feb0 1352 for (m = mounts; m < mounts + n_mounts; m++)
1eb7e08e 1353 mount_entry_done(m);
613b411c
LP
1354
1355 return r;
1356}
1357
d2d6c096
LP
1358void bind_mount_free_many(BindMount *b, unsigned n) {
1359 unsigned i;
1360
1361 assert(b || n == 0);
1362
1363 for (i = 0; i < n; i++) {
1364 free(b[i].source);
1365 free(b[i].destination);
1366 }
1367
1368 free(b);
1369}
1370
1371int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) {
1372 _cleanup_free_ char *s = NULL, *d = NULL;
1373 BindMount *c;
1374
1375 assert(b);
1376 assert(n);
1377 assert(item);
1378
1379 s = strdup(item->source);
1380 if (!s)
1381 return -ENOMEM;
1382
1383 d = strdup(item->destination);
1384 if (!d)
1385 return -ENOMEM;
1386
aa484f35 1387 c = reallocarray(*b, *n + 1, sizeof(BindMount));
d2d6c096
LP
1388 if (!c)
1389 return -ENOMEM;
1390
1391 *b = c;
1392
1393 c[(*n) ++] = (BindMount) {
1394 .source = s,
1395 .destination = d,
1396 .read_only = item->read_only,
1397 .recursive = item->recursive,
1398 .ignore_enoent = item->ignore_enoent,
1399 };
1400
1401 s = d = NULL;
1402 return 0;
1403}
1404
2abd4e38
YW
1405void temporary_filesystem_free_many(TemporaryFileSystem *t, unsigned n) {
1406 unsigned i;
1407
1408 assert(t || n == 0);
1409
1410 for (i = 0; i < n; i++) {
1411 free(t[i].path);
1412 free(t[i].options);
1413 }
1414
1415 free(t);
1416}
1417
1418int temporary_filesystem_add(
1419 TemporaryFileSystem **t,
1420 unsigned *n,
1421 const char *path,
1422 const char *options) {
1423
1424 _cleanup_free_ char *p = NULL, *o = NULL;
1425 TemporaryFileSystem *c;
1426
1427 assert(t);
1428 assert(n);
1429 assert(path);
1430
1431 p = strdup(path);
1432 if (!p)
1433 return -ENOMEM;
1434
1435 if (!isempty(options)) {
1436 o = strdup(options);
1437 if (!o)
1438 return -ENOMEM;
1439 }
1440
aa484f35 1441 c = reallocarray(*t, *n + 1, sizeof(TemporaryFileSystem));
2abd4e38
YW
1442 if (!c)
1443 return -ENOMEM;
1444
1445 *t = c;
1446
1447 c[(*n) ++] = (TemporaryFileSystem) {
1448 .path = p,
1449 .options = o,
1450 };
1451
1452 p = o = NULL;
1453 return 0;
1454}
1455
613b411c
LP
1456static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
1457 _cleanup_free_ char *x = NULL;
6b46ea73
LP
1458 char bid[SD_ID128_STRING_MAX];
1459 sd_id128_t boot_id;
1460 int r;
613b411c
LP
1461
1462 assert(id);
1463 assert(prefix);
1464 assert(path);
1465
6b46ea73
LP
1466 /* We include the boot id in the directory so that after a
1467 * reboot we can easily identify obsolete directories. */
1468
1469 r = sd_id128_get_boot(&boot_id);
1470 if (r < 0)
1471 return r;
1472
605405c6 1473 x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX");
613b411c
LP
1474 if (!x)
1475 return -ENOMEM;
1476
1477 RUN_WITH_UMASK(0077)
1478 if (!mkdtemp(x))
1479 return -errno;
1480
1481 RUN_WITH_UMASK(0000) {
1482 char *y;
1483
63c372cb 1484 y = strjoina(x, "/tmp");
613b411c
LP
1485
1486 if (mkdir(y, 0777 | S_ISVTX) < 0)
1487 return -errno;
c17ec25e 1488 }
15ae422b 1489
613b411c
LP
1490 *path = x;
1491 x = NULL;
1492
1493 return 0;
1494}
1495
1496int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
1497 char *a, *b;
1498 int r;
1499
1500 assert(id);
1501 assert(tmp_dir);
1502 assert(var_tmp_dir);
1503
1504 r = setup_one_tmp_dir(id, "/tmp", &a);
1505 if (r < 0)
1506 return r;
1507
1508 r = setup_one_tmp_dir(id, "/var/tmp", &b);
1509 if (r < 0) {
1510 char *t;
1511
63c372cb 1512 t = strjoina(a, "/tmp");
613b411c
LP
1513 rmdir(t);
1514 rmdir(a);
1515
1516 free(a);
1517 return r;
1518 }
1519
1520 *tmp_dir = a;
1521 *var_tmp_dir = b;
1522
1523 return 0;
1524}
1525
1526int setup_netns(int netns_storage_socket[2]) {
1527 _cleanup_close_ int netns = -1;
3ee897d6 1528 int r, q;
613b411c
LP
1529
1530 assert(netns_storage_socket);
1531 assert(netns_storage_socket[0] >= 0);
1532 assert(netns_storage_socket[1] >= 0);
1533
1534 /* We use the passed socketpair as a storage buffer for our
76cd584b
LP
1535 * namespace reference fd. Whatever process runs this first
1536 * shall create a new namespace, all others should just join
1537 * it. To serialize that we use a file lock on the socket
1538 * pair.
613b411c
LP
1539 *
1540 * It's a bit crazy, but hey, works great! */
1541
1542 if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
1543 return -errno;
1544
3ee897d6
LP
1545 netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
1546 if (netns == -EAGAIN) {
613b411c
LP
1547 /* Nothing stored yet, so let's create a new namespace */
1548
1549 if (unshare(CLONE_NEWNET) < 0) {
1550 r = -errno;
1551 goto fail;
1552 }
1553
1554 loopback_setup();
1555
1556 netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1557 if (netns < 0) {
1558 r = -errno;
1559 goto fail;
1560 }
1561
1562 r = 1;
613b411c 1563
3ee897d6
LP
1564 } else if (netns < 0) {
1565 r = netns;
1566 goto fail;
613b411c 1567
3ee897d6
LP
1568 } else {
1569 /* Yay, found something, so let's join the namespace */
613b411c
LP
1570 if (setns(netns, CLONE_NEWNET) < 0) {
1571 r = -errno;
1572 goto fail;
1573 }
1574
1575 r = 0;
1576 }
1577
3ee897d6
LP
1578 q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
1579 if (q < 0) {
1580 r = q;
613b411c
LP
1581 goto fail;
1582 }
1583
1584fail:
fe048ce5 1585 (void) lockf(netns_storage_socket[0], F_ULOCK, 0);
15ae422b
LP
1586 return r;
1587}
417116f2 1588
6e2d7c4f
MS
1589bool ns_type_supported(NamespaceType type) {
1590 const char *t, *ns_proc;
1591
0fa5b831
LP
1592 t = namespace_type_to_string(type);
1593 if (!t) /* Don't know how to translate this? Then it's not supported */
6e2d7c4f
MS
1594 return false;
1595
6e2d7c4f 1596 ns_proc = strjoina("/proc/self/ns/", t);
6e2d7c4f
MS
1597 return access(ns_proc, F_OK) == 0;
1598}
1599
1b8689f9
LP
1600static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
1601 [PROTECT_HOME_NO] = "no",
1602 [PROTECT_HOME_YES] = "yes",
1603 [PROTECT_HOME_READ_ONLY] = "read-only",
e4da7d8c 1604 [PROTECT_HOME_TMPFS] = "tmpfs",
417116f2
LP
1605};
1606
1b8689f9
LP
1607DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
1608
5e1c6154
YW
1609ProtectHome parse_protect_home_or_bool(const char *s) {
1610 int r;
1611
1612 r = parse_boolean(s);
1613 if (r > 0)
1614 return PROTECT_HOME_YES;
1615 if (r == 0)
1616 return PROTECT_HOME_NO;
1617
1618 return protect_home_from_string(s);
1619}
1620
1b8689f9
LP
1621static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
1622 [PROTECT_SYSTEM_NO] = "no",
1623 [PROTECT_SYSTEM_YES] = "yes",
1624 [PROTECT_SYSTEM_FULL] = "full",
3f815163 1625 [PROTECT_SYSTEM_STRICT] = "strict",
1b8689f9
LP
1626};
1627
1628DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
6e2d7c4f 1629
03c791aa
YW
1630ProtectSystem parse_protect_system_or_bool(const char *s) {
1631 int r;
1632
1633 r = parse_boolean(s);
1634 if (r > 0)
1635 return PROTECT_SYSTEM_YES;
1636 if (r == 0)
1637 return PROTECT_SYSTEM_NO;
1638
1639 return protect_system_from_string(s);
1640}
1641
6e2d7c4f
MS
1642static const char* const namespace_type_table[] = {
1643 [NAMESPACE_MOUNT] = "mnt",
1644 [NAMESPACE_CGROUP] = "cgroup",
1645 [NAMESPACE_UTS] = "uts",
1646 [NAMESPACE_IPC] = "ipc",
1647 [NAMESPACE_USER] = "user",
1648 [NAMESPACE_PID] = "pid",
1649 [NAMESPACE_NET] = "net",
1650};
1651
1652DEFINE_STRING_TABLE_LOOKUP(namespace_type, NamespaceType);