1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <linux/loop.h>
5 #include <sched.h>
6 #include <stdio.h>
7 #include <sys/mount.h>
8 #include <unistd.h>
9 #include <linux/fs.h>
10 
11 #include "alloc-util.h"
12 #include "base-filesystem.h"
13 #include "chase-symlinks.h"
14 #include "dev-setup.h"
15 #include "devnum-util.h"
16 #include "env-util.h"
17 #include "escape.h"
18 #include "extension-release.h"
19 #include "fd-util.h"
20 #include "format-util.h"
21 #include "label.h"
22 #include "list.h"
23 #include "loop-util.h"
24 #include "loopback-setup.h"
25 #include "mkdir-label.h"
26 #include "mount-util.h"
27 #include "mountpoint-util.h"
28 #include "namespace-util.h"
29 #include "namespace.h"
30 #include "nsflags.h"
31 #include "nulstr-util.h"
32 #include "os-util.h"
33 #include "path-util.h"
34 #include "selinux-util.h"
35 #include "socket-util.h"
36 #include "sort-util.h"
37 #include "stat-util.h"
38 #include "string-table.h"
39 #include "string-util.h"
40 #include "strv.h"
41 #include "tmpfile-util.h"
42 #include "umask-util.h"
43 #include "user-util.h"
44 
45 #define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC)
46 
47 typedef enum MountMode {
48         /* This is ordered by priority! */
49         INACCESSIBLE,
50         OVERLAY_MOUNT,
51         MOUNT_IMAGES,
52         BIND_MOUNT,
53         BIND_MOUNT_RECURSIVE,
54         PRIVATE_TMP,
55         PRIVATE_TMP_READONLY,
56         PRIVATE_DEV,
57         BIND_DEV,
58         EMPTY_DIR,
59         SYSFS,
60         PROCFS,
61         READONLY,
62         READWRITE,
63         NOEXEC,
64         EXEC,
65         TMPFS,
66         RUN,
67         EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
68         EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
69         MQUEUEFS,
70         READWRITE_IMPLICIT, /* Should have the lowest priority. */
71         _MOUNT_MODE_MAX,
72 } MountMode;
73 
74 typedef struct MountEntry {
75         const char *path_const;   /* Memory allocated on stack or static */
76         MountMode mode:5;
77         bool ignore:1;            /* Ignore if path does not exist? */
78         bool has_prefix:1;        /* Already is prefixed by the root dir? */
79         bool read_only:1;         /* Shall this mount point be read-only? */
80         bool nosuid:1;            /* Shall set MS_NOSUID on the mount itself */
81         bool noexec:1;            /* Shall set MS_NOEXEC on the mount itself */
82         bool exec:1;              /* Shall clear MS_NOEXEC on the mount itself */
83         bool applied:1;           /* Already applied */
84         char *path_malloc;        /* Use this instead of 'path_const' if we had to allocate memory */
85         const char *unprefixed_path_const; /* If the path was amended with a prefix, these will save the original */
86         char *unprefixed_path_malloc;
87         const char *source_const; /* The source path, for bind mounts or images */
88         char *source_malloc;
89         const char *options_const;/* Mount options for tmpfs */
90         char *options_malloc;
91         unsigned long flags;      /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
92         unsigned n_followed;
93         LIST_HEAD(MountOptions, image_options);
94 } MountEntry;
95 
96 /* If MountAPIVFS= is used, let's mount /sys, /proc, /dev and /run into the it, but only as a fallback if the user hasn't mounted
97  * something there already. These mounts are hence overridden by any other explicitly configured mounts. */
98 static const MountEntry apivfs_table[] = {
99         { "/proc",               PROCFS,       false },
100         { "/dev",                BIND_DEV,     false },
101         { "/sys",                SYSFS,        false },
102         { "/run",                RUN,          false, .options_const = "mode=755" TMPFS_LIMITS_RUN, .flags = MS_NOSUID|MS_NODEV|MS_STRICTATIME },
103 };
104 
105 /* ProtectKernelTunables= option and the related filesystem APIs */
106 static const MountEntry protect_kernel_tunables_proc_table[] = {
107         { "/proc/acpi",          READONLY,           true  },
108         { "/proc/apm",           READONLY,           true  }, /* Obsolete API, there's no point in permitting access to this, ever */
109         { "/proc/asound",        READONLY,           true  },
110         { "/proc/bus",           READONLY,           true  },
111         { "/proc/fs",            READONLY,           true  },
112         { "/proc/irq",           READONLY,           true  },
113         { "/proc/kallsyms",      INACCESSIBLE,       true  },
114         { "/proc/kcore",         INACCESSIBLE,       true  },
115         { "/proc/latency_stats", READONLY,           true  },
116         { "/proc/mtrr",          READONLY,           true  },
117         { "/proc/scsi",          READONLY,           true  },
118         { "/proc/sys",           READONLY,           true  },
119         { "/proc/sysrq-trigger", READONLY,           true  },
120         { "/proc/timer_stats",   READONLY,           true  },
121 };
122 
123 static const MountEntry protect_kernel_tunables_sys_table[] = {
124         { "/sys",                READONLY,           false },
125         { "/sys/fs/bpf",         READONLY,           true  },
126         { "/sys/fs/cgroup",      READWRITE_IMPLICIT, false }, /* READONLY is set by ProtectControlGroups= option */
127         { "/sys/fs/selinux",     READWRITE_IMPLICIT, true  },
128         { "/sys/kernel/debug",   READONLY,           true  },
129         { "/sys/kernel/tracing", READONLY,           true  },
130 };
131 
132 /* ProtectKernelModules= option */
133 static const MountEntry protect_kernel_modules_table[] = {
134 #if HAVE_SPLIT_USR
135         { "/lib/modules",        INACCESSIBLE, true  },
136 #endif
137         { "/usr/lib/modules",    INACCESSIBLE, true  },
138 };
139 
140 /* ProtectKernelLogs= option */
141 static const MountEntry protect_kernel_logs_proc_table[] = {
142         { "/proc/kmsg",          INACCESSIBLE, true },
143 };
144 
145 static const MountEntry protect_kernel_logs_dev_table[] = {
146         { "/dev/kmsg",           INACCESSIBLE, true },
147 };
148 
149 /*
150  * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
151  * system should be protected by ProtectSystem=
152  */
153 static const MountEntry protect_home_read_only_table[] = {
154         { "/home",               READONLY,     true  },
155         { "/run/user",           READONLY,     true  },
156         { "/root",               READONLY,     true  },
157 };
158 
159 /* ProtectHome=tmpfs table */
160 static const MountEntry protect_home_tmpfs_table[] = {
161         { "/home",               TMPFS,        true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
162         { "/run/user",           TMPFS,        true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
163         { "/root",               TMPFS,        true, .read_only = true, .options_const = "mode=0700" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
164 };
165 
166 /* ProtectHome=yes table */
167 static const MountEntry protect_home_yes_table[] = {
168         { "/home",               INACCESSIBLE, true  },
169         { "/run/user",           INACCESSIBLE, true  },
170         { "/root",               INACCESSIBLE, true  },
171 };
172 
173 /* ProtectSystem=yes table */
174 static const MountEntry protect_system_yes_table[] = {
175         { "/usr",                READONLY,     false },
176         { "/boot",               READONLY,     true  },
177         { "/efi",                READONLY,     true  },
178 #if HAVE_SPLIT_USR
179         { "/lib",                READONLY,     true  },
180         { "/lib64",              READONLY,     true  },
181         { "/bin",                READONLY,     true  },
182 #  if HAVE_SPLIT_BIN
183         { "/sbin",               READONLY,     true  },
184 #  endif
185 #endif
186 };
187 
188 /* ProtectSystem=full includes ProtectSystem=yes */
189 static const MountEntry protect_system_full_table[] = {
190         { "/usr",                READONLY,     false },
191         { "/boot",               READONLY,     true  },
192         { "/efi",                READONLY,     true  },
193         { "/etc",                READONLY,     false },
194 #if HAVE_SPLIT_USR
195         { "/lib",                READONLY,     true  },
196         { "/lib64",              READONLY,     true  },
197         { "/bin",                READONLY,     true  },
198 #  if HAVE_SPLIT_BIN
199         { "/sbin",               READONLY,     true  },
200 #  endif
201 #endif
202 };
203 
204 /*
205  * ProtectSystem=strict table. In this strict mode, we mount everything
206  * read-only, except for /proc, /dev, /sys which are the kernel API VFS,
207  * which are left writable, but PrivateDevices= + ProtectKernelTunables=
208  * protect those, and these options should be fully orthogonal.
209  * (And of course /home and friends are also left writable, as ProtectHome=
210  * shall manage those, orthogonally).
211  */
212 static const MountEntry protect_system_strict_table[] = {
213         { "/",                   READONLY,           false },
214         { "/proc",               READWRITE_IMPLICIT, false },      /* ProtectKernelTunables= */
215         { "/sys",                READWRITE_IMPLICIT, false },      /* ProtectKernelTunables= */
216         { "/dev",                READWRITE_IMPLICIT, false },      /* PrivateDevices= */
217         { "/home",               READWRITE_IMPLICIT, true  },      /* ProtectHome= */
218         { "/run/user",           READWRITE_IMPLICIT, true  },      /* ProtectHome= */
219         { "/root",               READWRITE_IMPLICIT, true  },      /* ProtectHome= */
220 };
221 
222 static const char * const mount_mode_table[_MOUNT_MODE_MAX] = {
223         [INACCESSIBLE]         = "inaccessible",
224         [OVERLAY_MOUNT]        = "overlay",
225         [BIND_MOUNT]           = "bind",
226         [BIND_MOUNT_RECURSIVE] = "rbind",
227         [PRIVATE_TMP]          = "private-tmp",
228         [PRIVATE_DEV]          = "private-dev",
229         [BIND_DEV]             = "bind-dev",
230         [EMPTY_DIR]            = "empty",
231         [SYSFS]                = "sysfs",
232         [PROCFS]               = "procfs",
233         [READONLY]             = "read-only",
234         [READWRITE]            = "read-write",
235         [TMPFS]                = "tmpfs",
236         [MOUNT_IMAGES]         = "mount-images",
237         [READWRITE_IMPLICIT]   = "rw-implicit",
238         [EXEC]                 = "exec",
239         [NOEXEC]               = "noexec",
240         [MQUEUEFS]             = "mqueuefs",
241 };
242 
243 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(mount_mode, MountMode);
244 
mount_entry_path(const MountEntry * p)245 static const char *mount_entry_path(const MountEntry *p) {
246         assert(p);
247 
248         /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that,
249          * otherwise the stack/static ->path field is returned. */
250 
251         return p->path_malloc ?: p->path_const;
252 }
253 
mount_entry_unprefixed_path(const MountEntry * p)254 static const char *mount_entry_unprefixed_path(const MountEntry *p) {
255         assert(p);
256 
257         /* Returns the unprefixed path (ie: before prefix_where_needed() ran), if any */
258 
259         return p->unprefixed_path_malloc ?: p->unprefixed_path_const ?: mount_entry_path(p);
260 }
261 
mount_entry_consume_prefix(MountEntry * p,char * new_path)262 static void mount_entry_consume_prefix(MountEntry *p, char *new_path) {
263         assert(p);
264         assert(p->path_malloc || p->path_const);
265         assert(new_path);
266 
267         /* Saves current path in unprefixed_ variable, and takes over new_path */
268 
269         free_and_replace(p->unprefixed_path_malloc, p->path_malloc);
270         /* If we didn't have a path on the heap, then it's a static one */
271         if (!p->unprefixed_path_malloc)
272                 p->unprefixed_path_const = p->path_const;
273         p->path_malloc = new_path;
274         p->has_prefix = true;
275 }
276 
mount_entry_read_only(const MountEntry * p)277 static bool mount_entry_read_only(const MountEntry *p) {
278         assert(p);
279 
280         return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE, PRIVATE_TMP_READONLY);
281 }
282 
mount_entry_noexec(const MountEntry * p)283 static bool mount_entry_noexec(const MountEntry *p) {
284         assert(p);
285 
286         return p->noexec || IN_SET(p->mode, NOEXEC, INACCESSIBLE, SYSFS, PROCFS);
287 }
288 
mount_entry_exec(const MountEntry * p)289 static bool mount_entry_exec(const MountEntry *p) {
290         assert(p);
291 
292         return p->exec || p->mode == EXEC;
293 }
294 
mount_entry_source(const MountEntry * p)295 static const char *mount_entry_source(const MountEntry *p) {
296         assert(p);
297 
298         return p->source_malloc ?: p->source_const;
299 }
300 
mount_entry_options(const MountEntry * p)301 static const char *mount_entry_options(const MountEntry *p) {
302         assert(p);
303 
304         return p->options_malloc ?: p->options_const;
305 }
306 
mount_entry_done(MountEntry * p)307 static void mount_entry_done(MountEntry *p) {
308         assert(p);
309 
310         p->path_malloc = mfree(p->path_malloc);
311         p->unprefixed_path_malloc = mfree(p->unprefixed_path_malloc);
312         p->source_malloc = mfree(p->source_malloc);
313         p->options_malloc = mfree(p->options_malloc);
314         p->image_options = mount_options_free_all(p->image_options);
315 }
316 
append_access_mounts(MountEntry ** p,char ** strv,MountMode mode,bool forcibly_require_prefix)317 static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) {
318         assert(p);
319 
320         /* Adds a list of user-supplied READWRITE/READWRITE_IMPLICIT/READONLY/INACCESSIBLE entries */
321 
322         STRV_FOREACH(i, strv) {
323                 bool ignore = false, needs_prefix = false;
324                 const char *e = *i;
325 
326                 /* Look for any prefixes */
327                 if (startswith(e, "-")) {
328                         e++;
329                         ignore = true;
330                 }
331                 if (startswith(e, "+")) {
332                         e++;
333                         needs_prefix = true;
334                 }
335 
336                 if (!path_is_absolute(e))
337                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
338                                                "Path is not absolute: %s", e);
339 
340                 *((*p)++) = (MountEntry) {
341                         .path_const = e,
342                         .mode = mode,
343                         .ignore = ignore,
344                         .has_prefix = !needs_prefix && !forcibly_require_prefix,
345                 };
346         }
347 
348         return 0;
349 }
350 
append_empty_dir_mounts(MountEntry ** p,char ** strv)351 static int append_empty_dir_mounts(MountEntry **p, char **strv) {
352         assert(p);
353 
354         /* Adds tmpfs mounts to provide readable but empty directories. This is primarily used to implement the
355          * "/private/" boundary directories for DynamicUser=1. */
356 
357         STRV_FOREACH(i, strv) {
358 
359                 *((*p)++) = (MountEntry) {
360                         .path_const = *i,
361                         .mode = EMPTY_DIR,
362                         .ignore = false,
363                         .read_only = true,
364                         .options_const = "mode=755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
365                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
366                 };
367         }
368 
369         return 0;
370 }
371 
append_bind_mounts(MountEntry ** p,const BindMount * binds,size_t n)372 static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n) {
373         assert(p);
374 
375         for (size_t i = 0; i < n; i++) {
376                 const BindMount *b = binds + i;
377 
378                 *((*p)++) = (MountEntry) {
379                         .path_const = b->destination,
380                         .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT,
381                         .read_only = b->read_only,
382                         .nosuid = b->nosuid,
383                         .source_const = b->source,
384                         .ignore = b->ignore_enoent,
385                 };
386         }
387 
388         return 0;
389 }
390 
append_mount_images(MountEntry ** p,const MountImage * mount_images,size_t n)391 static int append_mount_images(MountEntry **p, const MountImage *mount_images, size_t n) {
392         assert(p);
393 
394         for (size_t i = 0; i < n; i++) {
395                 const MountImage *m = mount_images + i;
396 
397                 *((*p)++) = (MountEntry) {
398                         .path_const = m->destination,
399                         .mode = MOUNT_IMAGES,
400                         .source_const = m->source,
401                         .image_options = m->mount_options,
402                         .ignore = m->ignore_enoent,
403                 };
404         }
405 
406         return 0;
407 }
408 
append_extensions(MountEntry ** p,const char * root,const char * extension_dir,char ** hierarchies,const MountImage * mount_images,size_t n,char ** extension_directories)409 static int append_extensions(
410                 MountEntry **p,
411                 const char *root,
412                 const char *extension_dir,
413                 char **hierarchies,
414                 const MountImage *mount_images,
415                 size_t n,
416                 char **extension_directories) {
417 
418         _cleanup_strv_free_ char **overlays = NULL;
419         int r;
420 
421         if (n == 0 && strv_isempty(extension_directories))
422                 return 0;
423 
424         assert(p);
425         assert(extension_dir);
426 
427         /* Prepare a list of overlays, that will have as each element a string suitable for being
428          * passed as a lowerdir= parameter, so start with the hierarchy on the root.
429          * The overlays vector will have the same number of elements and will correspond to the
430          * hierarchies vector, so they can be iterated upon together. */
431         STRV_FOREACH(hierarchy, hierarchies) {
432                 _cleanup_free_ char *prefixed_hierarchy = NULL;
433 
434                 prefixed_hierarchy = path_join(root, *hierarchy);
435                 if (!prefixed_hierarchy)
436                         return -ENOMEM;
437 
438                 r = strv_consume(&overlays, TAKE_PTR(prefixed_hierarchy));
439                 if (r < 0)
440                         return r;
441         }
442 
443         /* First, prepare a mount for each image, but these won't be visible to the unit, instead
444          * they will be mounted in our propagate directory, and used as a source for the overlay. */
445         for (size_t i = 0; i < n; i++) {
446                 _cleanup_free_ char *mount_point = NULL;
447                 const MountImage *m = mount_images + i;
448 
449                 r = asprintf(&mount_point, "%s/%zu", extension_dir, i);
450                 if (r < 0)
451                         return -ENOMEM;
452 
453                 for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
454                         _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
455 
456                         prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
457                         if (!prefixed_hierarchy)
458                                 return -ENOMEM;
459 
460                         escaped = shell_escape(prefixed_hierarchy, ",:");
461                         if (!escaped)
462                                 return -ENOMEM;
463 
464                         /* Note that lowerdir= parameters are in 'reverse' order, so the
465                          * top-most directory in the overlay comes first in the list. */
466                         lowerdir = strjoin(escaped, ":", overlays[j]);
467                         if (!lowerdir)
468                                 return -ENOMEM;
469 
470                         free_and_replace(overlays[j], lowerdir);
471                 }
472 
473                 *((*p)++) = (MountEntry) {
474                         .path_malloc = TAKE_PTR(mount_point),
475                         .image_options = m->mount_options,
476                         .ignore = m->ignore_enoent,
477                         .source_const = m->source,
478                         .mode = EXTENSION_IMAGES,
479                         .has_prefix = true,
480                 };
481         }
482 
483         /* Secondly, extend the lowerdir= parameters with each ExtensionDirectory.
484          * Bind mount them in the same location as the ExtensionImages, so that we
485          * can check that they are valid trees (extension-release.d). */
486         STRV_FOREACH(extension_directory, extension_directories) {
487                 _cleanup_free_ char *mount_point = NULL, *source = NULL;
488                 const char *e = *extension_directory;
489                 bool ignore_enoent = false;
490 
491                 /* Pick up the counter where the ExtensionImages left it. */
492                 r = asprintf(&mount_point, "%s/%zu", extension_dir, n++);
493                 if (r < 0)
494                         return -ENOMEM;
495 
496                 /* Look for any prefixes */
497                 if (startswith(e, "-")) {
498                         e++;
499                         ignore_enoent = true;
500                 }
501                 /* Ignore this for now */
502                 if (startswith(e, "+"))
503                         e++;
504 
505                 source = strdup(e);
506                 if (!source)
507                         return -ENOMEM;
508 
509                 for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
510                         _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
511 
512                         prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
513                         if (!prefixed_hierarchy)
514                                 return -ENOMEM;
515 
516                         escaped = shell_escape(prefixed_hierarchy, ",:");
517                         if (!escaped)
518                                 return -ENOMEM;
519 
520                         /* Note that lowerdir= parameters are in 'reverse' order, so the
521                          * top-most directory in the overlay comes first in the list. */
522                         lowerdir = strjoin(escaped, ":", overlays[j]);
523                         if (!lowerdir)
524                                 return -ENOMEM;
525 
526                         free_and_replace(overlays[j], lowerdir);
527                 }
528 
529                 *((*p)++) = (MountEntry) {
530                         .path_malloc = TAKE_PTR(mount_point),
531                         .source_const = TAKE_PTR(source),
532                         .mode = EXTENSION_DIRECTORIES,
533                         .ignore = ignore_enoent,
534                         .has_prefix = true,
535                         .read_only = true,
536                 };
537         }
538 
539         /* Then, for each hierarchy, prepare an overlay with the list of lowerdir= strings
540          * set up earlier. */
541         for (size_t i = 0; hierarchies && hierarchies[i]; ++i) {
542                 _cleanup_free_ char *prefixed_hierarchy = NULL;
543 
544                 prefixed_hierarchy = path_join(root, hierarchies[i]);
545                 if (!prefixed_hierarchy)
546                         return -ENOMEM;
547 
548                 *((*p)++) = (MountEntry) {
549                         .path_malloc = TAKE_PTR(prefixed_hierarchy),
550                         .options_malloc = TAKE_PTR(overlays[i]),
551                         .mode = OVERLAY_MOUNT,
552                         .has_prefix = true,
553                         .ignore = true, /* If the source image doesn't set the ignore bit it will fail earlier. */
554                 };
555         }
556 
557         return 0;
558 }
559 
append_tmpfs_mounts(MountEntry ** p,const TemporaryFileSystem * tmpfs,size_t n)560 static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, size_t n) {
561         assert(p);
562 
563         for (size_t i = 0; i < n; i++) {
564                 const TemporaryFileSystem *t = tmpfs + i;
565                 _cleanup_free_ char *o = NULL, *str = NULL;
566                 unsigned long flags;
567                 bool ro = false;
568                 int r;
569 
570                 if (!path_is_absolute(t->path))
571                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
572                                                "Path is not absolute: %s",
573                                                t->path);
574 
575                 str = strjoin("mode=0755" NESTED_TMPFS_LIMITS ",", t->options);
576                 if (!str)
577                         return -ENOMEM;
578 
579                 r = mount_option_mangle(str, MS_NODEV|MS_STRICTATIME, &flags, &o);
580                 if (r < 0)
581                         return log_debug_errno(r, "Failed to parse mount option '%s': %m", str);
582 
583                 ro = flags & MS_RDONLY;
584                 if (ro)
585                         flags ^= MS_RDONLY;
586 
587                 *((*p)++) = (MountEntry) {
588                         .path_const = t->path,
589                         .mode = TMPFS,
590                         .read_only = ro,
591                         .options_malloc = TAKE_PTR(o),
592                         .flags = flags,
593                 };
594         }
595 
596         return 0;
597 }
598 
append_static_mounts(MountEntry ** p,const MountEntry * mounts,size_t n,bool ignore_protect)599 static int append_static_mounts(MountEntry **p, const MountEntry *mounts, size_t n, bool ignore_protect) {
600         assert(p);
601         assert(mounts);
602 
603         /* Adds a list of static pre-defined entries */
604 
605         for (size_t i = 0; i < n; i++)
606                 *((*p)++) = (MountEntry) {
607                         .path_const = mount_entry_path(mounts+i),
608                         .mode = mounts[i].mode,
609                         .ignore = mounts[i].ignore || ignore_protect,
610                 };
611 
612         return 0;
613 }
614 
append_protect_home(MountEntry ** p,ProtectHome protect_home,bool ignore_protect)615 static int append_protect_home(MountEntry **p, ProtectHome protect_home, bool ignore_protect) {
616         assert(p);
617 
618         switch (protect_home) {
619 
620         case PROTECT_HOME_NO:
621                 return 0;
622 
623         case PROTECT_HOME_READ_ONLY:
624                 return append_static_mounts(p, protect_home_read_only_table, ELEMENTSOF(protect_home_read_only_table), ignore_protect);
625 
626         case PROTECT_HOME_TMPFS:
627                 return append_static_mounts(p, protect_home_tmpfs_table, ELEMENTSOF(protect_home_tmpfs_table), ignore_protect);
628 
629         case PROTECT_HOME_YES:
630                 return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect);
631 
632         default:
633                 assert_not_reached();
634         }
635 }
636 
append_protect_system(MountEntry ** p,ProtectSystem protect_system,bool ignore_protect)637 static int append_protect_system(MountEntry **p, ProtectSystem protect_system, bool ignore_protect) {
638         assert(p);
639 
640         switch (protect_system) {
641 
642         case PROTECT_SYSTEM_NO:
643                 return 0;
644 
645         case PROTECT_SYSTEM_STRICT:
646                 return append_static_mounts(p, protect_system_strict_table, ELEMENTSOF(protect_system_strict_table), ignore_protect);
647 
648         case PROTECT_SYSTEM_YES:
649                 return append_static_mounts(p, protect_system_yes_table, ELEMENTSOF(protect_system_yes_table), ignore_protect);
650 
651         case PROTECT_SYSTEM_FULL:
652                 return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect);
653 
654         default:
655                 assert_not_reached();
656         }
657 }
658 
mount_path_compare(const MountEntry * a,const MountEntry * b)659 static int mount_path_compare(const MountEntry *a, const MountEntry *b) {
660         int d;
661 
662         /* ExtensionImages/Directories will be used by other mounts as a base, so sort them first
663          * regardless of the prefix - they are set up in the propagate directory anyway */
664         d = -CMP(a->mode == EXTENSION_IMAGES, b->mode == EXTENSION_IMAGES);
665         if (d != 0)
666                 return d;
667         d = -CMP(a->mode == EXTENSION_DIRECTORIES, b->mode == EXTENSION_DIRECTORIES);
668         if (d != 0)
669                 return d;
670 
671         /* If the paths are not equal, then order prefixes first */
672         d = path_compare(mount_entry_path(a), mount_entry_path(b));
673         if (d != 0)
674                 return d;
675 
676         /* If the paths are equal, check the mode */
677         return CMP((int) a->mode, (int) b->mode);
678 }
679 
prefix_where_needed(MountEntry * m,size_t n,const char * root_directory)680 static int prefix_where_needed(MountEntry *m, size_t n, const char *root_directory) {
681         /* Prefixes all paths in the bind mount table with the root directory if the entry needs that. */
682 
683         assert(m || n == 0);
684 
685         for (size_t i = 0; i < n; i++) {
686                 char *s;
687 
688                 if (m[i].has_prefix)
689                         continue;
690 
691                 s = path_join(root_directory, mount_entry_path(m+i));
692                 if (!s)
693                         return -ENOMEM;
694 
695                 mount_entry_consume_prefix(&m[i], s);
696         }
697 
698         return 0;
699 }
700 
drop_duplicates(MountEntry * m,size_t * n)701 static void drop_duplicates(MountEntry *m, size_t *n) {
702         MountEntry *f, *t, *previous;
703 
704         assert(m);
705         assert(n);
706 
707         /* Drops duplicate entries. Expects that the array is properly ordered already. */
708 
709         for (f = m, t = m, previous = NULL; f < m + *n; f++) {
710 
711                 /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
712                  * above. Note that we only drop duplicates that haven't been mounted yet. */
713                 if (previous &&
714                     path_equal(mount_entry_path(f), mount_entry_path(previous)) &&
715                     !f->applied && !previous->applied) {
716                         log_debug("%s (%s) is duplicate.", mount_entry_path(f), mount_mode_to_string(f->mode));
717                         /* Propagate the flags to the remaining entry */
718                         previous->read_only = previous->read_only || mount_entry_read_only(f);
719                         previous->noexec = previous->noexec || mount_entry_noexec(f);
720                         previous->exec = previous->exec || mount_entry_exec(f);
721                         mount_entry_done(f);
722                         continue;
723                 }
724 
725                 *t = *f;
726                 previous = t;
727                 t++;
728         }
729 
730         *n = t - m;
731 }
732 
drop_inaccessible(MountEntry * m,size_t * n)733 static void drop_inaccessible(MountEntry *m, size_t *n) {
734         MountEntry *f, *t;
735         const char *clear = NULL;
736 
737         assert(m);
738         assert(n);
739 
740         /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
741          * ordered already. */
742 
743         for (f = m, t = m; f < m + *n; f++) {
744 
745                 /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
746                  * it, as inaccessible paths really should drop the entire subtree. */
747                 if (clear && path_startswith(mount_entry_path(f), clear)) {
748                         log_debug("%s is masked by %s.", mount_entry_path(f), clear);
749                         mount_entry_done(f);
750                         continue;
751                 }
752 
753                 clear = f->mode == INACCESSIBLE ? mount_entry_path(f) : NULL;
754 
755                 *t = *f;
756                 t++;
757         }
758 
759         *n = t - m;
760 }
761 
drop_nop(MountEntry * m,size_t * n)762 static void drop_nop(MountEntry *m, size_t *n) {
763         MountEntry *f, *t;
764 
765         assert(m);
766         assert(n);
767 
768         /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
769          * list is ordered by prefixes. */
770 
771         for (f = m, t = m; f < m + *n; f++) {
772 
773                 /* Only suppress such subtrees for READONLY, READWRITE and READWRITE_IMPLICIT entries */
774                 if (IN_SET(f->mode, READONLY, READWRITE, READWRITE_IMPLICIT)) {
775                         MountEntry *found = NULL;
776 
777                         /* Now let's find the first parent of the entry we are looking at. */
778                         for (MountEntry *p = PTR_SUB1(t, m); p; p = PTR_SUB1(p, m))
779                                 if (path_startswith(mount_entry_path(f), mount_entry_path(p))) {
780                                         found = p;
781                                         break;
782                                 }
783 
784                         /* We found it, let's see if it's the same mode, if so, we can drop this entry */
785                         if (found && found->mode == f->mode) {
786                                 log_debug("%s (%s) is made redundant by %s (%s)",
787                                           mount_entry_path(f), mount_mode_to_string(f->mode),
788                                           mount_entry_path(found), mount_mode_to_string(found->mode));
789                                 mount_entry_done(f);
790                                 continue;
791                         }
792                 }
793 
794                 *t = *f;
795                 t++;
796         }
797 
798         *n = t - m;
799 }
800 
drop_outside_root(const char * root_directory,MountEntry * m,size_t * n)801 static void drop_outside_root(const char *root_directory, MountEntry *m, size_t *n) {
802         MountEntry *f, *t;
803 
804         assert(m);
805         assert(n);
806 
807         /* Nothing to do */
808         if (!root_directory)
809                 return;
810 
811         /* Drops all mounts that are outside of the root directory. */
812 
813         for (f = m, t = m; f < m + *n; f++) {
814 
815                 /* ExtensionImages/Directories bases are opened in /run/systemd/unit-extensions on the host */
816                 if (!IN_SET(f->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) && !path_startswith(mount_entry_path(f), root_directory)) {
817                         log_debug("%s is outside of root directory.", mount_entry_path(f));
818                         mount_entry_done(f);
819                         continue;
820                 }
821 
822                 *t = *f;
823                 t++;
824         }
825 
826         *n = t - m;
827 }
828 
clone_device_node(const char * d,const char * temporary_mount,bool * make_devnode)829 static int clone_device_node(
830                 const char *d,
831                 const char *temporary_mount,
832                 bool *make_devnode) {
833 
834         _cleanup_free_ char *sl = NULL;
835         const char *dn, *bn, *t;
836         struct stat st;
837         int r;
838 
839         if (stat(d, &st) < 0) {
840                 if (errno == ENOENT) {
841                         log_debug_errno(errno, "Device node '%s' to clone does not exist, ignoring.", d);
842                         return -ENXIO;
843                 }
844 
845                 return log_debug_errno(errno, "Failed to stat() device node '%s' to clone, ignoring: %m", d);
846         }
847 
848         if (!S_ISBLK(st.st_mode) &&
849             !S_ISCHR(st.st_mode))
850                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
851                                        "Device node '%s' to clone is not a device node, ignoring.",
852                                        d);
853 
854         dn = strjoina(temporary_mount, d);
855 
856         /* First, try to create device node properly */
857         if (*make_devnode) {
858                 mac_selinux_create_file_prepare(d, st.st_mode);
859                 r = mknod(dn, st.st_mode, st.st_rdev);
860                 mac_selinux_create_file_clear();
861                 if (r >= 0)
862                         goto add_symlink;
863                 if (errno != EPERM)
864                         return log_debug_errno(errno, "mknod failed for %s: %m", d);
865 
866                 /* This didn't work, let's not try this again for the next iterations. */
867                 *make_devnode = false;
868         }
869 
870         /* We're about to fall back to bind-mounting the device node. So create a dummy bind-mount target.
871          * Do not prepare device-node SELinux label (see issue 13762) */
872         r = mknod(dn, S_IFREG, 0);
873         if (r < 0 && errno != EEXIST)
874                 return log_debug_errno(errno, "mknod() fallback failed for '%s': %m", d);
875 
876         /* Fallback to bind-mounting: The assumption here is that all used device nodes carry standard
877          * properties. Specifically, the devices nodes we bind-mount should either be owned by root:root or
878          * root:tty (e.g. /dev/tty, /dev/ptmx) and should not carry ACLs. */
879         r = mount_nofollow_verbose(LOG_DEBUG, d, dn, NULL, MS_BIND, NULL);
880         if (r < 0)
881                 return r;
882 
883 add_symlink:
884         bn = path_startswith(d, "/dev/");
885         if (!bn)
886                 return 0;
887 
888         /* Create symlinks like /dev/char/1:9 → ../urandom */
889         if (asprintf(&sl, "%s/dev/%s/" DEVNUM_FORMAT_STR,
890                      temporary_mount,
891                      S_ISCHR(st.st_mode) ? "char" : "block",
892                      DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
893                 return log_oom();
894 
895         (void) mkdir_parents(sl, 0755);
896 
897         t = strjoina("../", bn);
898         if (symlink(t, sl) < 0)
899                 log_debug_errno(errno, "Failed to symlink '%s' to '%s', ignoring: %m", t, sl);
900 
901         return 0;
902 }
903 
mount_private_dev(MountEntry * m)904 static int mount_private_dev(MountEntry *m) {
905         static const char devnodes[] =
906                 "/dev/null\0"
907                 "/dev/zero\0"
908                 "/dev/full\0"
909                 "/dev/random\0"
910                 "/dev/urandom\0"
911                 "/dev/tty\0";
912 
913         char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
914         const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
915         bool can_mknod = true;
916         int r;
917 
918         assert(m);
919 
920         if (!mkdtemp(temporary_mount))
921                 return log_debug_errno(errno, "Failed to create temporary directory '%s': %m", temporary_mount);
922 
923         dev = strjoina(temporary_mount, "/dev");
924         (void) mkdir(dev, 0755);
925         r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755" TMPFS_LIMITS_DEV);
926         if (r < 0)
927                 goto fail;
928 
929         r = label_fix_container(dev, "/dev", 0);
930         if (r < 0) {
931                 log_debug_errno(errno, "Failed to fix label of '%s' as /dev: %m", dev);
932                 goto fail;
933         }
934 
935         devpts = strjoina(temporary_mount, "/dev/pts");
936         (void) mkdir(devpts, 0755);
937         r = mount_nofollow_verbose(LOG_DEBUG, "/dev/pts", devpts, NULL, MS_BIND, NULL);
938         if (r < 0)
939                 goto fail;
940 
941         /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx.
942          * When /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible.
943          * Thus, in that case make a clone.
944          * In nspawn and other containers it will be a symlink, in that case make it a symlink. */
945         r = is_symlink("/dev/ptmx");
946         if (r < 0) {
947                 log_debug_errno(r, "Failed to detect whether /dev/ptmx is a symlink or not: %m");
948                 goto fail;
949         } else if (r > 0) {
950                 devptmx = strjoina(temporary_mount, "/dev/ptmx");
951                 if (symlink("pts/ptmx", devptmx) < 0) {
952                         r = log_debug_errno(errno, "Failed to create a symlink '%s' to pts/ptmx: %m", devptmx);
953                         goto fail;
954                 }
955         } else {
956                 r = clone_device_node("/dev/ptmx", temporary_mount, &can_mknod);
957                 if (r < 0)
958                         goto fail;
959         }
960 
961         devshm = strjoina(temporary_mount, "/dev/shm");
962         (void) mkdir(devshm, 0755);
963         r = mount_nofollow_verbose(LOG_DEBUG, "/dev/shm", devshm, NULL, MS_BIND, NULL);
964         if (r < 0)
965                 goto fail;
966 
967         devmqueue = strjoina(temporary_mount, "/dev/mqueue");
968         (void) mkdir(devmqueue, 0755);
969         (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
970 
971         devhugepages = strjoina(temporary_mount, "/dev/hugepages");
972         (void) mkdir(devhugepages, 0755);
973         (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
974 
975         devlog = strjoina(temporary_mount, "/dev/log");
976         if (symlink("/run/systemd/journal/dev-log", devlog) < 0)
977                 log_debug_errno(errno, "Failed to create a symlink '%s' to /run/systemd/journal/dev-log, ignoring: %m", devlog);
978 
979         NULSTR_FOREACH(d, devnodes) {
980                 r = clone_device_node(d, temporary_mount, &can_mknod);
981                 /* ENXIO means the *source* is not a device file, skip creation in that case */
982                 if (r < 0 && r != -ENXIO)
983                         goto fail;
984         }
985 
986         r = dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
987         if (r < 0)
988                 log_debug_errno(r, "Failed to set up basic device tree at '%s', ignoring: %m", temporary_mount);
989 
990         /* Create the /dev directory if missing. It is more likely to be missing when the service is started
991          * with RootDirectory. This is consistent with mount units creating the mount points when missing. */
992         (void) mkdir_p_label(mount_entry_path(m), 0755);
993 
994         /* Unmount everything in old /dev */
995         r = umount_recursive(mount_entry_path(m), 0);
996         if (r < 0)
997                 log_debug_errno(r, "Failed to unmount directories below '%s', ignoring: %m", mount_entry_path(m));
998 
999         r = mount_nofollow_verbose(LOG_DEBUG, dev, mount_entry_path(m), NULL, MS_MOVE, NULL);
1000         if (r < 0)
1001                 goto fail;
1002 
1003         (void) rmdir(dev);
1004         (void) rmdir(temporary_mount);
1005 
1006         return 0;
1007 
1008 fail:
1009         if (devpts)
1010                 (void) umount_verbose(LOG_DEBUG, devpts, UMOUNT_NOFOLLOW);
1011 
1012         if (devshm)
1013                 (void) umount_verbose(LOG_DEBUG, devshm, UMOUNT_NOFOLLOW);
1014 
1015         if (devhugepages)
1016                 (void) umount_verbose(LOG_DEBUG, devhugepages, UMOUNT_NOFOLLOW);
1017 
1018         if (devmqueue)
1019                 (void) umount_verbose(LOG_DEBUG, devmqueue, UMOUNT_NOFOLLOW);
1020 
1021         (void) umount_verbose(LOG_DEBUG, dev, UMOUNT_NOFOLLOW);
1022         (void) rmdir(dev);
1023         (void) rmdir(temporary_mount);
1024 
1025         return r;
1026 }
1027 
mount_bind_dev(const MountEntry * m)1028 static int mount_bind_dev(const MountEntry *m) {
1029         int r;
1030 
1031         assert(m);
1032 
1033         /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the
1034          * service's /dev. This is only used when RootDirectory= is set. */
1035 
1036         (void) mkdir_p_label(mount_entry_path(m), 0755);
1037 
1038         r = path_is_mount_point(mount_entry_path(m), NULL, 0);
1039         if (r < 0)
1040                 return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m");
1041         if (r > 0) /* make this a NOP if /dev is already a mount point */
1042                 return 0;
1043 
1044         r = mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
1045         if (r < 0)
1046                 return r;
1047 
1048         return 1;
1049 }
1050 
mount_sysfs(const MountEntry * m)1051 static int mount_sysfs(const MountEntry *m) {
1052         int r;
1053 
1054         assert(m);
1055 
1056         (void) mkdir_p_label(mount_entry_path(m), 0755);
1057 
1058         r = path_is_mount_point(mount_entry_path(m), NULL, 0);
1059         if (r < 0)
1060                 return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m");
1061         if (r > 0) /* make this a NOP if /sys is already a mount point */
1062                 return 0;
1063 
1064         /* Bind mount the host's version so that we get all child mounts of it, too. */
1065         r = mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
1066         if (r < 0)
1067                 return r;
1068 
1069         return 1;
1070 }
1071 
mount_procfs(const MountEntry * m,const NamespaceInfo * ns_info)1072 static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
1073         _cleanup_free_ char *opts = NULL;
1074         const char *entry_path;
1075         int r, n;
1076 
1077         assert(m);
1078         assert(ns_info);
1079 
1080         if (ns_info->protect_proc != PROTECT_PROC_DEFAULT ||
1081             ns_info->proc_subset != PROC_SUBSET_ALL) {
1082 
1083                 /* Starting with kernel 5.8 procfs' hidepid= logic is truly per-instance (previously it
1084                  * pretended to be per-instance but actually was per-namespace), hence let's make use of it
1085                  * if requested. To make sure this logic succeeds only on kernels where hidepid= is
1086                  * per-instance, we'll exclusively use the textual value for hidepid=, since support was
1087                  * added in the same commit: if it's supported it is thus also per-instance. */
1088 
1089                 opts = strjoin("hidepid=",
1090                                ns_info->protect_proc == PROTECT_PROC_DEFAULT ? "off" :
1091                                protect_proc_to_string(ns_info->protect_proc),
1092                                ns_info->proc_subset == PROC_SUBSET_PID ? ",subset=pid" : "");
1093                 if (!opts)
1094                         return -ENOMEM;
1095         }
1096 
1097         entry_path = mount_entry_path(m);
1098         (void) mkdir_p_label(entry_path, 0755);
1099 
1100         /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in
1101          * one. i.e we don't reuse existing mounts here under any condition, we want a new instance owned by
1102          * our user namespace and with our hidepid= settings applied. Hence, let's get rid of everything
1103          * mounted on /proc/ first. */
1104 
1105         n = umount_recursive(entry_path, 0);
1106 
1107         r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
1108         if (r == -EINVAL && opts)
1109                 /* If this failed with EINVAL then this likely means the textual hidepid= stuff is
1110                  * not supported by the kernel, and thus the per-instance hidepid= neither, which
1111                  * means we really don't want to use it, since it would affect our host's /proc
1112                  * mount. Hence let's gracefully fallback to a classic, unrestricted version. */
1113                 r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
1114         if (r == -EPERM) {
1115                 /* When we do not have enough privileges to mount /proc, fallback to use existing /proc. */
1116 
1117                 if (n > 0)
1118                         /* /proc or some of sub-mounts are umounted in the above. Refuse incomplete tree.
1119                          * Propagate the original error code returned by mount() in the above. */
1120                         return -EPERM;
1121 
1122                 r = path_is_mount_point(entry_path, NULL, 0);
1123                 if (r < 0)
1124                         return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
1125                 if (r == 0) {
1126                         /* We lack permissions to mount a new instance of /proc, and it is not already
1127                          * mounted. But we can access the host's, so as a final fallback bind-mount it to
1128                          * the destination, as most likely we are inside a user manager in an unprivileged
1129                          * user namespace. */
1130                         r = mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL);
1131                         if (r < 0)
1132                                 return -EPERM;
1133                 }
1134         } else if (r < 0)
1135                 return r;
1136 
1137         return 1;
1138 }
1139 
mount_tmpfs(const MountEntry * m)1140 static int mount_tmpfs(const MountEntry *m) {
1141         const char *entry_path, *inner_path;
1142         int r;
1143 
1144         assert(m);
1145 
1146         entry_path = mount_entry_path(m);
1147         inner_path = mount_entry_unprefixed_path(m);
1148 
1149         /* First, get rid of everything that is below if there is anything. Then, overmount with our new
1150          * tmpfs */
1151 
1152         (void) mkdir_p_label(entry_path, 0755);
1153         (void) umount_recursive(entry_path, 0);
1154 
1155         r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", entry_path, "tmpfs", m->flags, mount_entry_options(m));
1156         if (r < 0)
1157                 return r;
1158 
1159         r = label_fix_container(entry_path, inner_path, 0);
1160         if (r < 0)
1161                 return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, inner_path);
1162 
1163         return 1;
1164 }
1165 
mount_run(const MountEntry * m)1166 static int mount_run(const MountEntry *m) {
1167         int r;
1168 
1169         assert(m);
1170 
1171         r = path_is_mount_point(mount_entry_path(m), NULL, 0);
1172         if (r < 0 && r != -ENOENT)
1173                 return log_debug_errno(r, "Unable to determine whether /run is already mounted: %m");
1174         if (r > 0) /* make this a NOP if /run is already a mount point */
1175                 return 0;
1176 
1177         return mount_tmpfs(m);
1178 }
1179 
mount_mqueuefs(const MountEntry * m)1180 static int mount_mqueuefs(const MountEntry *m) {
1181         int r;
1182         const char *entry_path;
1183 
1184         assert(m);
1185 
1186         entry_path = mount_entry_path(m);
1187 
1188         (void) mkdir_p_label(entry_path, 0755);
1189         (void) umount_recursive(entry_path, 0);
1190 
1191         r = mount_nofollow_verbose(LOG_DEBUG, "mqueue", entry_path, "mqueue", m->flags, mount_entry_options(m));
1192         if (r < 0)
1193                 return r;
1194 
1195         return 0;
1196 }
1197 
mount_image(const MountEntry * m,const char * root_directory)1198 static int mount_image(const MountEntry *m, const char *root_directory) {
1199 
1200         _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
1201                             *host_os_release_sysext_level = NULL;
1202         int r;
1203 
1204         assert(m);
1205 
1206         if (m->mode == EXTENSION_IMAGES) {
1207                 r = parse_os_release(
1208                                 empty_to_root(root_directory),
1209                                 "ID", &host_os_release_id,
1210                                 "VERSION_ID", &host_os_release_version_id,
1211                                 "SYSEXT_LEVEL", &host_os_release_sysext_level,
1212                                 NULL);
1213                 if (r < 0)
1214                         return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
1215                 if (isempty(host_os_release_id))
1216                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
1217         }
1218 
1219         r = verity_dissect_and_mount(
1220                                 /* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
1221                                 host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
1222         if (r == -ENOENT && m->ignore)
1223                 return 0;
1224         if (r == -ESTALE && host_os_release_id)
1225                 return log_error_errno(r,
1226                                        "Failed to mount image %s, extension-release metadata does not match the lower layer's: ID=%s%s%s%s%s",
1227                                        mount_entry_source(m),
1228                                        host_os_release_id,
1229                                        host_os_release_version_id ? " VERSION_ID=" : "",
1230                                        strempty(host_os_release_version_id),
1231                                        host_os_release_sysext_level ? " SYSEXT_LEVEL=" : "",
1232                                        strempty(host_os_release_sysext_level));
1233         if (r < 0)
1234                 return log_debug_errno(r, "Failed to mount image %s on %s: %m", mount_entry_source(m), mount_entry_path(m));
1235 
1236         return 1;
1237 }
1238 
mount_overlay(const MountEntry * m)1239 static int mount_overlay(const MountEntry *m) {
1240         const char *options;
1241         int r;
1242 
1243         assert(m);
1244 
1245         options = strjoina("lowerdir=", mount_entry_options(m));
1246 
1247         (void) mkdir_p_label(mount_entry_path(m), 0755);
1248 
1249         r = mount_nofollow_verbose(LOG_DEBUG, "overlay", mount_entry_path(m), "overlay", MS_RDONLY, options);
1250         if (r == -ENOENT && m->ignore)
1251                 return 0;
1252         if (r < 0)
1253                 return r;
1254 
1255         return 1;
1256 }
1257 
follow_symlink(const char * root_directory,MountEntry * m)1258 static int follow_symlink(
1259                 const char *root_directory,
1260                 MountEntry *m) {
1261 
1262         _cleanup_free_ char *target = NULL;
1263         int r;
1264 
1265         /* Let's chase symlinks, but only one step at a time. That's because depending where the symlink points we
1266          * might need to change the order in which we mount stuff. Hence: let's normalize piecemeal, and do one step at
1267          * a time by specifying CHASE_STEP. This function returns 0 if we resolved one step, and > 0 if we reached the
1268          * end and already have a fully normalized name. */
1269 
1270         r = chase_symlinks(mount_entry_path(m), root_directory, CHASE_STEP|CHASE_NONEXISTENT, &target, NULL);
1271         if (r < 0)
1272                 return log_debug_errno(r, "Failed to chase symlinks '%s': %m", mount_entry_path(m));
1273         if (r > 0) /* Reached the end, nothing more to resolve */
1274                 return 1;
1275 
1276         if (m->n_followed >= CHASE_SYMLINKS_MAX) /* put a boundary on things */
1277                 return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
1278                                        "Symlink loop on '%s'.",
1279                                        mount_entry_path(m));
1280 
1281         log_debug("Followed mount entry path symlink %s → %s.", mount_entry_path(m), target);
1282 
1283         mount_entry_consume_prefix(m, TAKE_PTR(target));
1284 
1285         m->n_followed ++;
1286 
1287         return 0;
1288 }
1289 
apply_one_mount(const char * root_directory,MountEntry * m,const NamespaceInfo * ns_info)1290 static int apply_one_mount(
1291                 const char *root_directory,
1292                 MountEntry *m,
1293                 const NamespaceInfo *ns_info) {
1294 
1295         _cleanup_free_ char *inaccessible = NULL;
1296         bool rbind = true, make = false;
1297         const char *what;
1298         int r;
1299 
1300         assert(m);
1301         assert(ns_info);
1302 
1303         log_debug("Applying namespace mount on %s", mount_entry_path(m));
1304 
1305         switch (m->mode) {
1306 
1307         case INACCESSIBLE: {
1308                 _cleanup_free_ char *tmp = NULL;
1309                 const char *runtime_dir;
1310                 struct stat target;
1311 
1312                 /* First, get rid of everything that is below if there
1313                  * is anything... Then, overmount it with an
1314                  * inaccessible path. */
1315                 (void) umount_recursive(mount_entry_path(m), 0);
1316 
1317                 if (lstat(mount_entry_path(m), &target) < 0) {
1318                         if (errno == ENOENT && m->ignore)
1319                                 return 0;
1320 
1321                         return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m",
1322                                                mount_entry_path(m));
1323                 }
1324 
1325                 if (geteuid() == 0)
1326                         runtime_dir = "/run";
1327                 else {
1328                         if (asprintf(&tmp, "/run/user/" UID_FMT, geteuid()) < 0)
1329                                 return -ENOMEM;
1330 
1331                         runtime_dir = tmp;
1332                 }
1333 
1334                 r = mode_to_inaccessible_node(runtime_dir, target.st_mode, &inaccessible);
1335                 if (r < 0)
1336                         return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
1337                                                "File type not supported for inaccessible mounts. Note that symlinks are not allowed");
1338                 what = inaccessible;
1339                 break;
1340         }
1341 
1342         case READONLY:
1343         case READWRITE:
1344         case READWRITE_IMPLICIT:
1345         case EXEC:
1346         case NOEXEC:
1347                 r = path_is_mount_point(mount_entry_path(m), root_directory, 0);
1348                 if (r == -ENOENT && m->ignore)
1349                         return 0;
1350                 if (r < 0)
1351                         return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m",
1352                                                mount_entry_path(m));
1353                 if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY
1354                             * and MS_NOEXEC bits for the mount point if needed. */
1355                         return 0;
1356                 /* This isn't a mount point yet, let's make it one. */
1357                 what = mount_entry_path(m);
1358                 break;
1359 
1360         case EXTENSION_DIRECTORIES: {
1361                 _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
1362                                 *host_os_release_sysext_level = NULL, *extension_name = NULL;
1363                 _cleanup_strv_free_ char **extension_release = NULL;
1364 
1365                 r = path_extract_filename(mount_entry_source(m), &extension_name);
1366                 if (r < 0)
1367                         return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m));
1368 
1369                 r = parse_os_release(
1370                                 empty_to_root(root_directory),
1371                                 "ID", &host_os_release_id,
1372                                 "VERSION_ID", &host_os_release_version_id,
1373                                 "SYSEXT_LEVEL", &host_os_release_sysext_level,
1374                                 NULL);
1375                 if (r < 0)
1376                         return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
1377                 if (isempty(host_os_release_id))
1378                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
1379 
1380                 r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release);
1381                 if (r == -ENOENT && m->ignore)
1382                         return 0;
1383                 if (r < 0)
1384                         return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name);
1385 
1386                 r = extension_release_validate(
1387                                 extension_name,
1388                                 host_os_release_id,
1389                                 host_os_release_version_id,
1390                                 host_os_release_sysext_level,
1391                                 /* host_sysext_scope */ NULL, /* Leave empty, we need to accept both system and portable */
1392                                 extension_release);
1393                 if (r == 0)
1394                         return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Directory %s extension-release metadata does not match the root's", extension_name);
1395                 if (r < 0)
1396                         return log_debug_errno(r, "Failed to compare directory %s extension-release metadata with the root's os-release: %m", extension_name);
1397 
1398                 _fallthrough_;
1399         }
1400 
1401         case BIND_MOUNT:
1402                 rbind = false;
1403 
1404                 _fallthrough_;
1405         case BIND_MOUNT_RECURSIVE: {
1406                 _cleanup_free_ char *chased = NULL;
1407 
1408                 /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note
1409                  * that bind mount source paths are always relative to the host root, hence we pass NULL as
1410                  * root directory to chase_symlinks() here. */
1411 
1412                 r = chase_symlinks(mount_entry_source(m), NULL, CHASE_TRAIL_SLASH, &chased, NULL);
1413                 if (r == -ENOENT && m->ignore) {
1414                         log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_source(m));
1415                         return 0;
1416                 }
1417                 if (r < 0)
1418                         return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_source(m));
1419 
1420                 log_debug("Followed source symlinks %s → %s.", mount_entry_source(m), chased);
1421 
1422                 free_and_replace(m->source_malloc, chased);
1423 
1424                 what = mount_entry_source(m);
1425                 make = true;
1426                 break;
1427         }
1428 
1429         case EMPTY_DIR:
1430         case TMPFS:
1431                 return mount_tmpfs(m);
1432 
1433         case PRIVATE_TMP:
1434         case PRIVATE_TMP_READONLY:
1435                 what = mount_entry_source(m);
1436                 make = true;
1437                 break;
1438 
1439         case PRIVATE_DEV:
1440                 return mount_private_dev(m);
1441 
1442         case BIND_DEV:
1443                 return mount_bind_dev(m);
1444 
1445         case SYSFS:
1446                 return mount_sysfs(m);
1447 
1448         case PROCFS:
1449                 return mount_procfs(m, ns_info);
1450 
1451         case RUN:
1452                 return mount_run(m);
1453 
1454         case MQUEUEFS:
1455                 return mount_mqueuefs(m);
1456 
1457         case MOUNT_IMAGES:
1458                 return mount_image(m, NULL);
1459 
1460         case EXTENSION_IMAGES:
1461                 return mount_image(m, root_directory);
1462 
1463         case OVERLAY_MOUNT:
1464                 return mount_overlay(m);
1465 
1466         default:
1467                 assert_not_reached();
1468         }
1469 
1470         assert(what);
1471 
1472         r = mount_nofollow_verbose(LOG_DEBUG, what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL);
1473         if (r < 0) {
1474                 bool try_again = false;
1475 
1476                 if (r == -ENOENT && make) {
1477                         int q;
1478 
1479                         /* Hmm, either the source or the destination are missing. Let's see if we can create
1480                            the destination, then try again. */
1481 
1482                         (void) mkdir_parents(mount_entry_path(m), 0755);
1483 
1484                         q = make_mount_point_inode_from_path(what, mount_entry_path(m), 0755);
1485                         if (q < 0 && q != -EEXIST)
1486                                 log_error_errno(q, "Failed to create destination mount point node '%s': %m",
1487                                                 mount_entry_path(m));
1488                         else
1489                                 try_again = true;
1490                 }
1491 
1492                 if (try_again)
1493                         r = mount_nofollow_verbose(LOG_DEBUG, what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL);
1494                 if (r < 0)
1495                         return log_error_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m));
1496         }
1497 
1498         log_debug("Successfully mounted %s to %s", what, mount_entry_path(m));
1499         return 0;
1500 }
1501 
make_read_only(const MountEntry * m,char ** deny_list,FILE * proc_self_mountinfo)1502 static int make_read_only(const MountEntry *m, char **deny_list, FILE *proc_self_mountinfo) {
1503         unsigned long new_flags = 0, flags_mask = 0;
1504         bool submounts;
1505         int r;
1506 
1507         assert(m);
1508         assert(proc_self_mountinfo);
1509 
1510         if (mount_entry_read_only(m) || m->mode == PRIVATE_DEV) {
1511                 new_flags |= MS_RDONLY;
1512                 flags_mask |= MS_RDONLY;
1513         }
1514 
1515         if (m->nosuid) {
1516                 new_flags |= MS_NOSUID;
1517                 flags_mask |= MS_NOSUID;
1518         }
1519 
1520         if (flags_mask == 0) /* No Change? */
1521                 return 0;
1522 
1523         /* We generally apply these changes recursively, except for /dev, and the cases we know there's
1524          * nothing further down.  Set /dev readonly, but not submounts like /dev/shm. Also, we only set the
1525          * per-mount read-only flag.  We can't set it on the superblock, if we are inside a user namespace
1526          * and running Linux <= 4.17. */
1527         submounts =
1528                 mount_entry_read_only(m) &&
1529                 !IN_SET(m->mode, EMPTY_DIR, TMPFS);
1530         if (submounts)
1531                 r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, deny_list, proc_self_mountinfo);
1532         else
1533                 r = bind_remount_one_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, proc_self_mountinfo);
1534 
1535         /* Note that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked
1536          * read-only already stays this way. This improves compatibility with container managers, where we
1537          * won't attempt to undo read-only mounts already applied. */
1538 
1539         if (r == -ENOENT && m->ignore)
1540                 return 0;
1541         if (r < 0)
1542                 return log_debug_errno(r, "Failed to re-mount '%s'%s: %m", mount_entry_path(m),
1543                                        submounts ? " and its submounts" : "");
1544         return 0;
1545 }
1546 
make_noexec(const MountEntry * m,char ** deny_list,FILE * proc_self_mountinfo)1547 static int make_noexec(const MountEntry *m, char **deny_list, FILE *proc_self_mountinfo) {
1548         unsigned long new_flags = 0, flags_mask = 0;
1549         bool submounts;
1550         int r;
1551 
1552         assert(m);
1553         assert(proc_self_mountinfo);
1554 
1555         if (mount_entry_noexec(m)) {
1556                 new_flags |= MS_NOEXEC;
1557                 flags_mask |= MS_NOEXEC;
1558         } else if (mount_entry_exec(m)) {
1559                 new_flags &= ~MS_NOEXEC;
1560                 flags_mask |= MS_NOEXEC;
1561         }
1562 
1563         if (flags_mask == 0) /* No Change? */
1564                 return 0;
1565 
1566         submounts = !IN_SET(m->mode, EMPTY_DIR, TMPFS);
1567 
1568         if (submounts)
1569                 r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, deny_list, proc_self_mountinfo);
1570         else
1571                 r = bind_remount_one_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, proc_self_mountinfo);
1572 
1573         if (r == -ENOENT && m->ignore)
1574                 return 0;
1575         if (r < 0)
1576                 return log_debug_errno(r, "Failed to re-mount '%s'%s: %m", mount_entry_path(m),
1577                                        submounts ? " and its submounts" : "");
1578         return 0;
1579 }
1580 
make_nosuid(const MountEntry * m,FILE * proc_self_mountinfo)1581 static int make_nosuid(const MountEntry *m, FILE *proc_self_mountinfo) {
1582         bool submounts;
1583         int r;
1584 
1585         assert(m);
1586         assert(proc_self_mountinfo);
1587 
1588         submounts = !IN_SET(m->mode, EMPTY_DIR, TMPFS);
1589 
1590         if (submounts)
1591                 r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), MS_NOSUID, MS_NOSUID, NULL, proc_self_mountinfo);
1592         else
1593                 r = bind_remount_one_with_mountinfo(mount_entry_path(m), MS_NOSUID, MS_NOSUID, proc_self_mountinfo);
1594         if (r == -ENOENT && m->ignore)
1595                 return 0;
1596         if (r < 0)
1597                 return log_debug_errno(r, "Failed to re-mount '%s'%s: %m", mount_entry_path(m),
1598                                        submounts ? " and its submounts" : "");
1599         return 0;
1600 }
1601 
namespace_info_mount_apivfs(const NamespaceInfo * ns_info)1602 static bool namespace_info_mount_apivfs(const NamespaceInfo *ns_info) {
1603         assert(ns_info);
1604 
1605         /*
1606          * ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=,
1607          * since to protect the API VFS mounts, they need to be around in the
1608          * first place...
1609          */
1610 
1611         return ns_info->mount_apivfs ||
1612                 ns_info->protect_control_groups ||
1613                 ns_info->protect_kernel_tunables ||
1614                 ns_info->protect_proc != PROTECT_PROC_DEFAULT ||
1615                 ns_info->proc_subset != PROC_SUBSET_ALL;
1616 }
1617 
namespace_calculate_mounts(const NamespaceInfo * ns_info,char ** read_write_paths,char ** read_only_paths,char ** inaccessible_paths,char ** exec_paths,char ** no_exec_paths,char ** empty_directories,size_t n_bind_mounts,size_t n_temporary_filesystems,size_t n_mount_images,size_t n_extension_images,size_t n_extension_directories,size_t n_hierarchies,const char * tmp_dir,const char * var_tmp_dir,const char * creds_path,const char * log_namespace,bool setup_propagate,const char * notify_socket)1618 static size_t namespace_calculate_mounts(
1619                 const NamespaceInfo *ns_info,
1620                 char** read_write_paths,
1621                 char** read_only_paths,
1622                 char** inaccessible_paths,
1623                 char** exec_paths,
1624                 char** no_exec_paths,
1625                 char** empty_directories,
1626                 size_t n_bind_mounts,
1627                 size_t n_temporary_filesystems,
1628                 size_t n_mount_images,
1629                 size_t n_extension_images,
1630                 size_t n_extension_directories,
1631                 size_t n_hierarchies,
1632                 const char* tmp_dir,
1633                 const char* var_tmp_dir,
1634                 const char *creds_path,
1635                 const char* log_namespace,
1636                 bool setup_propagate,
1637                 const char* notify_socket) {
1638 
1639         size_t protect_home_cnt;
1640         size_t protect_system_cnt =
1641                 (ns_info->protect_system == PROTECT_SYSTEM_STRICT ?
1642                  ELEMENTSOF(protect_system_strict_table) :
1643                  ((ns_info->protect_system == PROTECT_SYSTEM_FULL) ?
1644                   ELEMENTSOF(protect_system_full_table) :
1645                   ((ns_info->protect_system == PROTECT_SYSTEM_YES) ?
1646                    ELEMENTSOF(protect_system_yes_table) : 0)));
1647 
1648         protect_home_cnt =
1649                 (ns_info->protect_home == PROTECT_HOME_YES ?
1650                  ELEMENTSOF(protect_home_yes_table) :
1651                  ((ns_info->protect_home == PROTECT_HOME_READ_ONLY) ?
1652                   ELEMENTSOF(protect_home_read_only_table) :
1653                   ((ns_info->protect_home == PROTECT_HOME_TMPFS) ?
1654                    ELEMENTSOF(protect_home_tmpfs_table) : 0)));
1655 
1656         return !!tmp_dir + !!var_tmp_dir +
1657                 strv_length(read_write_paths) +
1658                 strv_length(read_only_paths) +
1659                 strv_length(inaccessible_paths) +
1660                 strv_length(exec_paths) +
1661                 strv_length(no_exec_paths) +
1662                 strv_length(empty_directories) +
1663                 n_bind_mounts +
1664                 n_mount_images +
1665                 (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
1666                         n_hierarchies + n_extension_images + n_extension_directories: 0) +
1667                 n_temporary_filesystems +
1668                 ns_info->private_dev +
1669                 (ns_info->protect_kernel_tunables ?
1670                  ELEMENTSOF(protect_kernel_tunables_proc_table) + ELEMENTSOF(protect_kernel_tunables_sys_table) : 0) +
1671                 (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
1672                 (ns_info->protect_kernel_logs ?
1673                  ELEMENTSOF(protect_kernel_logs_proc_table) + ELEMENTSOF(protect_kernel_logs_dev_table) : 0) +
1674                 (ns_info->protect_control_groups ? 1 : 0) +
1675                 protect_home_cnt + protect_system_cnt +
1676                 (ns_info->protect_hostname ? 2 : 0) +
1677                 (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
1678                 (creds_path ? 2 : 1) +
1679                 !!log_namespace +
1680                 setup_propagate + /* /run/systemd/incoming */
1681                 !!notify_socket +
1682                 ns_info->private_ipc; /* /dev/mqueue */
1683 }
1684 
1685 /* Walk all mount entries and dropping any unused mounts. This affects all
1686  * mounts:
1687  * - that are implicitly protected by a path that has been rendered inaccessible
1688  * - whose immediate parent requests the same protection mode as the mount itself
1689  * - that are outside of the relevant root directory
1690  * - which are duplicates
1691  */
drop_unused_mounts(const char * root_directory,MountEntry * mounts,size_t * n_mounts)1692 static void drop_unused_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
1693         assert(root_directory);
1694         assert(n_mounts);
1695         assert(mounts || *n_mounts == 0);
1696 
1697         typesafe_qsort(mounts, *n_mounts, mount_path_compare);
1698 
1699         drop_duplicates(mounts, n_mounts);
1700         drop_outside_root(root_directory, mounts, n_mounts);
1701         drop_inaccessible(mounts, n_mounts);
1702         drop_nop(mounts, n_mounts);
1703 }
1704 
create_symlinks_from_tuples(const char * root,char ** strv_symlinks)1705 static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
1706         int r;
1707 
1708         STRV_FOREACH_PAIR(src, dst, strv_symlinks) {
1709                 _cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
1710 
1711                 src_abs = path_join(root, *src);
1712                 dst_abs = path_join(root, *dst);
1713                 if (!src_abs || !dst_abs)
1714                         return -ENOMEM;
1715 
1716                 r = mkdir_parents_label(dst_abs, 0755);
1717                 if (r < 0)
1718                         return r;
1719 
1720                 r = symlink_idempotent(src_abs, dst_abs, true);
1721                 if (r < 0)
1722                         return r;
1723         }
1724 
1725         return 0;
1726 }
1727 
apply_mounts(const char * root,const NamespaceInfo * ns_info,MountEntry * mounts,size_t * n_mounts,char ** exec_dir_symlinks,char ** error_path)1728 static int apply_mounts(
1729                 const char *root,
1730                 const NamespaceInfo *ns_info,
1731                 MountEntry *mounts,
1732                 size_t *n_mounts,
1733                 char **exec_dir_symlinks,
1734                 char **error_path) {
1735 
1736         _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
1737         _cleanup_free_ char **deny_list = NULL;
1738         int r;
1739 
1740         if (n_mounts == 0) /* Shortcut: nothing to do */
1741                 return 0;
1742 
1743         assert(root);
1744         assert(mounts);
1745         assert(n_mounts);
1746 
1747         /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of
1748          * /proc. For example, this is the case with the option: 'InaccessiblePaths=/proc'. */
1749         proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
1750         if (!proc_self_mountinfo) {
1751                 r = -errno;
1752 
1753                 if (error_path)
1754                         *error_path = strdup("/proc/self/mountinfo");
1755 
1756                 return log_debug_errno(r, "Failed to open /proc/self/mountinfo: %m");
1757         }
1758 
1759         /* First round, establish all mounts we need */
1760         for (;;) {
1761                 bool again = false;
1762 
1763                 for (MountEntry *m = mounts; m < mounts + *n_mounts; ++m) {
1764 
1765                         if (m->applied)
1766                                 continue;
1767 
1768                         /* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */
1769                         r = follow_symlink(!IN_SET(m->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) ? root : NULL, m);
1770                         if (r < 0) {
1771                                 if (error_path && mount_entry_path(m))
1772                                         *error_path = strdup(mount_entry_path(m));
1773                                 return r;
1774                         }
1775                         if (r == 0) {
1776                                 /* We hit a symlinked mount point. The entry got rewritten and might
1777                                  * point to a very different place now. Let's normalize the changed
1778                                  * list, and start from the beginning. After all to mount the entry
1779                                  * at the new location we might need some other mounts first */
1780                                 again = true;
1781                                 break;
1782                         }
1783 
1784                         r = apply_one_mount(root, m, ns_info);
1785                         if (r < 0) {
1786                                 if (error_path && mount_entry_path(m))
1787                                         *error_path = strdup(mount_entry_path(m));
1788                                 return r;
1789                         }
1790 
1791                         m->applied = true;
1792                 }
1793 
1794                 if (!again)
1795                         break;
1796 
1797                 drop_unused_mounts(root, mounts, n_mounts);
1798         }
1799 
1800         /* Now that all filesystems have been set up, but before the
1801          * read-only switches are flipped, create the exec dirs symlinks.
1802          * Note that when /var/lib is not empty/tmpfs, these symlinks will already
1803          * exist, which means this will be a no-op. */
1804         r = create_symlinks_from_tuples(root, exec_dir_symlinks);
1805         if (r < 0)
1806                 return log_debug_errno(r, "Failed to set up ExecDirectories symlinks inside mount namespace: %m");
1807 
1808         /* Create a deny list we can pass to bind_mount_recursive() */
1809         deny_list = new(char*, (*n_mounts)+1);
1810         if (!deny_list)
1811                 return -ENOMEM;
1812         for (size_t j = 0; j < *n_mounts; j++)
1813                 deny_list[j] = (char*) mount_entry_path(mounts+j);
1814         deny_list[*n_mounts] = NULL;
1815 
1816         /* Second round, flip the ro bits if necessary. */
1817         for (MountEntry *m = mounts; m < mounts + *n_mounts; ++m) {
1818                 r = make_read_only(m, deny_list, proc_self_mountinfo);
1819                 if (r < 0) {
1820                         if (error_path && mount_entry_path(m))
1821                                 *error_path = strdup(mount_entry_path(m));
1822                         return r;
1823                 }
1824         }
1825 
1826         /* Third round, flip the noexec bits with a simplified deny list. */
1827         for (size_t j = 0; j < *n_mounts; j++)
1828                 if (IN_SET((mounts+j)->mode, EXEC, NOEXEC))
1829                         deny_list[j] = (char*) mount_entry_path(mounts+j);
1830         deny_list[*n_mounts] = NULL;
1831 
1832         for (MountEntry *m = mounts; m < mounts + *n_mounts; ++m) {
1833                 r = make_noexec(m, deny_list, proc_self_mountinfo);
1834                 if (r < 0) {
1835                         if (error_path && mount_entry_path(m))
1836                                 *error_path = strdup(mount_entry_path(m));
1837                         return r;
1838                 }
1839         }
1840 
1841         /* Fourth round, flip the nosuid bits without a deny list. */
1842         if (ns_info->mount_nosuid)
1843                 for (MountEntry *m = mounts; m < mounts + *n_mounts; ++m) {
1844                         r = make_nosuid(m, proc_self_mountinfo);
1845                         if (r < 0) {
1846                                 if (error_path && mount_entry_path(m))
1847                                         *error_path = strdup(mount_entry_path(m));
1848                                 return r;
1849                         }
1850                 }
1851 
1852         return 1;
1853 }
1854 
root_read_only(char ** read_only_paths,ProtectSystem protect_system)1855 static bool root_read_only(
1856                 char **read_only_paths,
1857                 ProtectSystem protect_system) {
1858 
1859         /* Determine whether the root directory is going to be read-only given the configured settings. */
1860 
1861         if (protect_system == PROTECT_SYSTEM_STRICT)
1862                 return true;
1863 
1864         if (prefixed_path_strv_contains(read_only_paths, "/"))
1865                 return true;
1866 
1867         return false;
1868 }
1869 
home_read_only(char ** read_only_paths,char ** inaccessible_paths,char ** empty_directories,const BindMount * bind_mounts,size_t n_bind_mounts,const TemporaryFileSystem * temporary_filesystems,size_t n_temporary_filesystems,ProtectHome protect_home)1870 static bool home_read_only(
1871                 char** read_only_paths,
1872                 char** inaccessible_paths,
1873                 char** empty_directories,
1874                 const BindMount *bind_mounts,
1875                 size_t n_bind_mounts,
1876                 const TemporaryFileSystem *temporary_filesystems,
1877                 size_t n_temporary_filesystems,
1878                 ProtectHome protect_home) {
1879 
1880         /* Determine whether the /home directory is going to be read-only given the configured settings. Yes,
1881          * this is a bit sloppy, since we don't bother checking for cases where / is affected by multiple
1882          * settings. */
1883 
1884         if (protect_home != PROTECT_HOME_NO)
1885                 return true;
1886 
1887         if (prefixed_path_strv_contains(read_only_paths, "/home") ||
1888             prefixed_path_strv_contains(inaccessible_paths, "/home") ||
1889             prefixed_path_strv_contains(empty_directories, "/home"))
1890                 return true;
1891 
1892         for (size_t i = 0; i < n_temporary_filesystems; i++)
1893                 if (path_equal(temporary_filesystems[i].path, "/home"))
1894                         return true;
1895 
1896         /* If /home is overmounted with some dir from the host it's not writable. */
1897         for (size_t i = 0; i < n_bind_mounts; i++)
1898                 if (path_equal(bind_mounts[i].destination, "/home"))
1899                         return true;
1900 
1901         return false;
1902 }
1903 
verity_settings_prepare(VeritySettings * verity,const char * root_image,const void * root_hash,size_t root_hash_size,const char * root_hash_path,const void * root_hash_sig,size_t root_hash_sig_size,const char * root_hash_sig_path,const char * verity_data_path)1904 static int verity_settings_prepare(
1905                 VeritySettings *verity,
1906                 const char *root_image,
1907                 const void *root_hash,
1908                 size_t root_hash_size,
1909                 const char *root_hash_path,
1910                 const void *root_hash_sig,
1911                 size_t root_hash_sig_size,
1912                 const char *root_hash_sig_path,
1913                 const char *verity_data_path) {
1914 
1915         int r;
1916 
1917         assert(verity);
1918 
1919         if (root_hash) {
1920                 void *d;
1921 
1922                 d = memdup(root_hash, root_hash_size);
1923                 if (!d)
1924                         return -ENOMEM;
1925 
1926                 free_and_replace(verity->root_hash, d);
1927                 verity->root_hash_size = root_hash_size;
1928                 verity->designator = PARTITION_ROOT;
1929         }
1930 
1931         if (root_hash_sig) {
1932                 void *d;
1933 
1934                 d = memdup(root_hash_sig, root_hash_sig_size);
1935                 if (!d)
1936                         return -ENOMEM;
1937 
1938                 free_and_replace(verity->root_hash_sig, d);
1939                 verity->root_hash_sig_size = root_hash_sig_size;
1940                 verity->designator = PARTITION_ROOT;
1941         }
1942 
1943         if (verity_data_path) {
1944                 r = free_and_strdup(&verity->data_path, verity_data_path);
1945                 if (r < 0)
1946                         return r;
1947         }
1948 
1949         r = verity_settings_load(
1950                         verity,
1951                         root_image,
1952                         root_hash_path,
1953                         root_hash_sig_path);
1954         if (r < 0)
1955                 return log_debug_errno(r, "Failed to load root hash: %m");
1956 
1957         return 0;
1958 }
1959 
setup_namespace(const char * root_directory,const char * root_image,const MountOptions * root_image_options,const NamespaceInfo * ns_info,char ** read_write_paths,char ** read_only_paths,char ** inaccessible_paths,char ** exec_paths,char ** no_exec_paths,char ** empty_directories,char ** exec_dir_symlinks,const BindMount * bind_mounts,size_t n_bind_mounts,const TemporaryFileSystem * temporary_filesystems,size_t n_temporary_filesystems,const MountImage * mount_images,size_t n_mount_images,const char * tmp_dir,const char * var_tmp_dir,const char * creds_path,const char * log_namespace,unsigned long mount_flags,const void * root_hash,size_t root_hash_size,const char * root_hash_path,const void * root_hash_sig,size_t root_hash_sig_size,const char * root_hash_sig_path,const char * verity_data_path,const MountImage * extension_images,size_t n_extension_images,char ** extension_directories,const char * propagate_dir,const char * incoming_dir,const char * extension_dir,const char * notify_socket,char ** error_path)1960 int setup_namespace(
1961                 const char* root_directory,
1962                 const char* root_image,
1963                 const MountOptions *root_image_options,
1964                 const NamespaceInfo *ns_info,
1965                 char** read_write_paths,
1966                 char** read_only_paths,
1967                 char** inaccessible_paths,
1968                 char** exec_paths,
1969                 char** no_exec_paths,
1970                 char** empty_directories,
1971                 char** exec_dir_symlinks,
1972                 const BindMount *bind_mounts,
1973                 size_t n_bind_mounts,
1974                 const TemporaryFileSystem *temporary_filesystems,
1975                 size_t n_temporary_filesystems,
1976                 const MountImage *mount_images,
1977                 size_t n_mount_images,
1978                 const char* tmp_dir,
1979                 const char* var_tmp_dir,
1980                 const char *creds_path,
1981                 const char *log_namespace,
1982                 unsigned long mount_flags,
1983                 const void *root_hash,
1984                 size_t root_hash_size,
1985                 const char *root_hash_path,
1986                 const void *root_hash_sig,
1987                 size_t root_hash_sig_size,
1988                 const char *root_hash_sig_path,
1989                 const char *verity_data_path,
1990                 const MountImage *extension_images,
1991                 size_t n_extension_images,
1992                 char **extension_directories,
1993                 const char *propagate_dir,
1994                 const char *incoming_dir,
1995                 const char *extension_dir,
1996                 const char *notify_socket,
1997                 char **error_path) {
1998 
1999         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2000         _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2001         _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
2002         _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
2003         _cleanup_strv_free_ char **hierarchies = NULL;
2004         MountEntry *m = NULL, *mounts = NULL;
2005         bool require_prefix = false, setup_propagate = false;
2006         const char *root;
2007         DissectImageFlags dissect_image_flags =
2008                 DISSECT_IMAGE_GENERIC_ROOT |
2009                 DISSECT_IMAGE_REQUIRE_ROOT |
2010                 DISSECT_IMAGE_DISCARD_ON_LOOP |
2011                 DISSECT_IMAGE_RELAX_VAR_CHECK |
2012                 DISSECT_IMAGE_FSCK |
2013                 DISSECT_IMAGE_USR_NO_ROOT |
2014                 DISSECT_IMAGE_GROWFS;
2015         size_t n_mounts;
2016         int r;
2017 
2018         assert(ns_info);
2019 
2020         /* Make sure that all mknod(), mkdir() calls we do are unaffected by the umask, and the access modes
2021          * we configure take effect */
2022         BLOCK_WITH_UMASK(0000);
2023 
2024         if (!isempty(propagate_dir) && !isempty(incoming_dir))
2025                 setup_propagate = true;
2026 
2027         if (mount_flags == 0)
2028                 mount_flags = MS_SHARED;
2029 
2030         if (root_image) {
2031                 /* Make the whole image read-only if we can determine that we only access it in a read-only fashion. */
2032                 if (root_read_only(read_only_paths,
2033                                    ns_info->protect_system) &&
2034                     home_read_only(read_only_paths, inaccessible_paths, empty_directories,
2035                                    bind_mounts, n_bind_mounts, temporary_filesystems, n_temporary_filesystems,
2036                                    ns_info->protect_home) &&
2037                     strv_isempty(read_write_paths))
2038                         dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
2039 
2040                 r = verity_settings_prepare(
2041                                 &verity,
2042                                 root_image,
2043                                 root_hash, root_hash_size, root_hash_path,
2044                                 root_hash_sig, root_hash_sig_size, root_hash_sig_path,
2045                                 verity_data_path);
2046                 if (r < 0)
2047                         return r;
2048 
2049                 SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, verity.data_path);
2050 
2051                 r = loop_device_make_by_path(
2052                                 root_image,
2053                                 FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
2054                                 FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
2055                                 &loop_device);
2056                 if (r < 0)
2057                         return log_debug_errno(r, "Failed to create loop device for root image: %m");
2058 
2059                 /* Make sure udevd won't issue BLKRRPART (which might flush out the loaded partition table)
2060                  * while we are still trying to mount things */
2061                 r = loop_device_flock(loop_device, LOCK_SH);
2062                 if (r < 0)
2063                         return log_debug_errno(r, "Failed to lock loopback device with LOCK_SH: %m");
2064 
2065                 r = dissect_image(
2066                                 loop_device->fd,
2067                                 &verity,
2068                                 root_image_options,
2069                                 loop_device->diskseq,
2070                                 loop_device->uevent_seqnum_not_before,
2071                                 loop_device->timestamp_not_before,
2072                                 dissect_image_flags,
2073                                 &dissected_image);
2074                 if (r < 0)
2075                         return log_debug_errno(r, "Failed to dissect image: %m");
2076 
2077                 r = dissected_image_load_verity_sig_partition(
2078                                 dissected_image,
2079                                 loop_device->fd,
2080                                 &verity);
2081                 if (r < 0)
2082                         return r;
2083 
2084                 r = dissected_image_decrypt(
2085                                 dissected_image,
2086                                 NULL,
2087                                 &verity,
2088                                 dissect_image_flags,
2089                                 &decrypted_image);
2090                 if (r < 0)
2091                         return log_debug_errno(r, "Failed to decrypt dissected image: %m");
2092         }
2093 
2094         if (root_directory)
2095                 root = root_directory;
2096         else {
2097                 /* /run/systemd should have been created by PID 1 early on already, but in some cases, like
2098                  * when running tests (test-execute), it might not have been created yet so let's make sure
2099                  * we create it if it doesn't already exist. */
2100                 (void) mkdir_p_label("/run/systemd", 0755);
2101 
2102                 /* Always create the mount namespace in a temporary directory, instead of operating directly
2103                  * in the root. The temporary directory prevents any mounts from being potentially obscured
2104                  * my other mounts we already applied.  We use the same mount point for all images, which is
2105                  * safe, since they all live in their own namespaces after all, and hence won't see each
2106                  * other. */
2107 
2108                 root = "/run/systemd/unit-root";
2109                 (void) mkdir_label(root, 0700);
2110                 require_prefix = true;
2111         }
2112 
2113         if (n_extension_images > 0 || !strv_isempty(extension_directories)) {
2114                 r = parse_env_extension_hierarchies(&hierarchies);
2115                 if (r < 0)
2116                         return r;
2117         }
2118 
2119         n_mounts = namespace_calculate_mounts(
2120                         ns_info,
2121                         read_write_paths,
2122                         read_only_paths,
2123                         inaccessible_paths,
2124                         exec_paths,
2125                         no_exec_paths,
2126                         empty_directories,
2127                         n_bind_mounts,
2128                         n_temporary_filesystems,
2129                         n_mount_images,
2130                         n_extension_images,
2131                         strv_length(extension_directories),
2132                         strv_length(hierarchies),
2133                         tmp_dir, var_tmp_dir,
2134                         creds_path,
2135                         log_namespace,
2136                         setup_propagate,
2137                         notify_socket);
2138 
2139         if (n_mounts > 0) {
2140                 m = mounts = new0(MountEntry, n_mounts);
2141                 if (!mounts)
2142                         return -ENOMEM;
2143 
2144                 r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix);
2145                 if (r < 0)
2146                         goto finish;
2147 
2148                 r = append_access_mounts(&m, read_only_paths, READONLY, require_prefix);
2149                 if (r < 0)
2150                         goto finish;
2151 
2152                 r = append_access_mounts(&m, inaccessible_paths, INACCESSIBLE, require_prefix);
2153                 if (r < 0)
2154                         goto finish;
2155 
2156                 r = append_access_mounts(&m, exec_paths, EXEC, require_prefix);
2157                 if (r < 0)
2158                         goto finish;
2159 
2160                 r = append_access_mounts(&m, no_exec_paths, NOEXEC, require_prefix);
2161                 if (r < 0)
2162                         goto finish;
2163 
2164                 r = append_empty_dir_mounts(&m, empty_directories);
2165                 if (r < 0)
2166                         goto finish;
2167 
2168                 r = append_bind_mounts(&m, bind_mounts, n_bind_mounts);
2169                 if (r < 0)
2170                         goto finish;
2171 
2172                 r = append_tmpfs_mounts(&m, temporary_filesystems, n_temporary_filesystems);
2173                 if (r < 0)
2174                         goto finish;
2175 
2176                 if (tmp_dir) {
2177                         bool ro = streq(tmp_dir, RUN_SYSTEMD_EMPTY);
2178 
2179                         *(m++) = (MountEntry) {
2180                                 .path_const = "/tmp",
2181                                 .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP,
2182                                 .source_const = tmp_dir,
2183                         };
2184                 }
2185 
2186                 if (var_tmp_dir) {
2187                         bool ro = streq(var_tmp_dir, RUN_SYSTEMD_EMPTY);
2188 
2189                         *(m++) = (MountEntry) {
2190                                 .path_const = "/var/tmp",
2191                                 .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP,
2192                                 .source_const = var_tmp_dir,
2193                         };
2194                 }
2195 
2196                 r = append_mount_images(&m, mount_images, n_mount_images);
2197                 if (r < 0)
2198                         goto finish;
2199 
2200                 r = append_extensions(&m, root, extension_dir, hierarchies, extension_images, n_extension_images, extension_directories);
2201                 if (r < 0)
2202                         goto finish;
2203 
2204                 if (ns_info->private_dev)
2205                         *(m++) = (MountEntry) {
2206                                 .path_const = "/dev",
2207                                 .mode = PRIVATE_DEV,
2208                                 .flags = DEV_MOUNT_OPTIONS,
2209                         };
2210 
2211                 /* In case /proc is successfully mounted with pid tree subset only (ProcSubset=pid), the
2212                    protective mounts to non-pid /proc paths would fail. But the pid only option may have
2213                    failed gracefully, so let's try the mounts but it's not fatal if they don't succeed. */
2214                 bool ignore_protect_proc = ns_info->ignore_protect_paths || ns_info->proc_subset == PROC_SUBSET_PID;
2215                 if (ns_info->protect_kernel_tunables) {
2216                         r = append_static_mounts(&m,
2217                                                  protect_kernel_tunables_proc_table,
2218                                                  ELEMENTSOF(protect_kernel_tunables_proc_table),
2219                                                  ignore_protect_proc);
2220                         if (r < 0)
2221                                 goto finish;
2222 
2223                         r = append_static_mounts(&m,
2224                                                  protect_kernel_tunables_sys_table,
2225                                                  ELEMENTSOF(protect_kernel_tunables_sys_table),
2226                                                  ns_info->ignore_protect_paths);
2227                         if (r < 0)
2228                                 goto finish;
2229                 }
2230 
2231                 if (ns_info->protect_kernel_modules) {
2232                         r = append_static_mounts(&m,
2233                                                  protect_kernel_modules_table,
2234                                                  ELEMENTSOF(protect_kernel_modules_table),
2235                                                  ns_info->ignore_protect_paths);
2236                         if (r < 0)
2237                                 goto finish;
2238                 }
2239 
2240                 if (ns_info->protect_kernel_logs) {
2241                         r = append_static_mounts(&m,
2242                                                  protect_kernel_logs_proc_table,
2243                                                  ELEMENTSOF(protect_kernel_logs_proc_table),
2244                                                  ignore_protect_proc);
2245                         if (r < 0)
2246                                 goto finish;
2247 
2248                         r = append_static_mounts(&m,
2249                                                  protect_kernel_logs_dev_table,
2250                                                  ELEMENTSOF(protect_kernel_logs_dev_table),
2251                                                  ns_info->ignore_protect_paths);
2252                         if (r < 0)
2253                                 goto finish;
2254                 }
2255 
2256                 if (ns_info->protect_control_groups)
2257                         *(m++) = (MountEntry) {
2258                                 .path_const = "/sys/fs/cgroup",
2259                                 .mode = READONLY,
2260                         };
2261 
2262                 r = append_protect_home(&m, ns_info->protect_home, ns_info->ignore_protect_paths);
2263                 if (r < 0)
2264                         goto finish;
2265 
2266                 r = append_protect_system(&m, ns_info->protect_system, false);
2267                 if (r < 0)
2268                         goto finish;
2269 
2270                 if (namespace_info_mount_apivfs(ns_info)) {
2271                         r = append_static_mounts(&m,
2272                                                  apivfs_table,
2273                                                  ELEMENTSOF(apivfs_table),
2274                                                  ns_info->ignore_protect_paths);
2275                         if (r < 0)
2276                                 goto finish;
2277                 }
2278 
2279                 /* Note, if proc is mounted with subset=pid then neither of the
2280                  * two paths will exist, i.e. they are implicitly protected by
2281                  * the mount option. */
2282                 if (ns_info->protect_hostname) {
2283                         *(m++) = (MountEntry) {
2284                                 .path_const = "/proc/sys/kernel/hostname",
2285                                 .mode = READONLY,
2286                                 .ignore = ignore_protect_proc,
2287                         };
2288                         *(m++) = (MountEntry) {
2289                                 .path_const = "/proc/sys/kernel/domainname",
2290                                 .mode = READONLY,
2291                                 .ignore = ignore_protect_proc,
2292                         };
2293                 }
2294 
2295                 if (ns_info->private_ipc)
2296                         *(m++) = (MountEntry) {
2297                                 .path_const = "/dev/mqueue",
2298                                 .mode = MQUEUEFS,
2299                                 .flags = MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME,
2300                         };
2301 
2302                 if (creds_path) {
2303                         /* If our service has a credentials store configured, then bind that one in, but hide
2304                          * everything else. */
2305 
2306                         *(m++) = (MountEntry) {
2307                                 .path_const = "/run/credentials",
2308                                 .mode = TMPFS,
2309                                 .read_only = true,
2310                                 .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
2311                                 .flags = MS_NODEV|MS_STRICTATIME|MS_NOSUID|MS_NOEXEC,
2312                         };
2313 
2314                         *(m++) = (MountEntry) {
2315                                 .path_const = creds_path,
2316                                 .mode = BIND_MOUNT,
2317                                 .read_only = true,
2318                                 .source_const = creds_path,
2319                         };
2320                 } else {
2321                         /* If our service has no credentials store configured, then make the whole
2322                          * credentials tree inaccessible wholesale. */
2323 
2324                         *(m++) = (MountEntry) {
2325                                 .path_const = "/run/credentials",
2326                                 .mode = INACCESSIBLE,
2327                                 .ignore = true,
2328                         };
2329                 }
2330 
2331                 if (log_namespace) {
2332                         _cleanup_free_ char *q = NULL;
2333 
2334                         q = strjoin("/run/systemd/journal.", log_namespace);
2335                         if (!q) {
2336                                 r = -ENOMEM;
2337                                 goto finish;
2338                         }
2339 
2340                         *(m++) = (MountEntry) {
2341                                 .path_const = "/run/systemd/journal",
2342                                 .mode = BIND_MOUNT_RECURSIVE,
2343                                 .read_only = true,
2344                                 .source_malloc = TAKE_PTR(q),
2345                         };
2346                 }
2347 
2348                 /* Will be used to add bind mounts at runtime */
2349                 if (setup_propagate)
2350                         *(m++) = (MountEntry) {
2351                                 .source_const = propagate_dir,
2352                                 .path_const = incoming_dir,
2353                                 .mode = BIND_MOUNT,
2354                                 .read_only = true,
2355                         };
2356 
2357                 if (notify_socket)
2358                         *(m++) = (MountEntry) {
2359                                 .path_const = notify_socket,
2360                                 .source_const = notify_socket,
2361                                 .mode = BIND_MOUNT,
2362                                 .read_only = true,
2363                         };
2364 
2365                 assert(mounts + n_mounts == m);
2366 
2367                 /* Prepend the root directory where that's necessary */
2368                 r = prefix_where_needed(mounts, n_mounts, root);
2369                 if (r < 0)
2370                         goto finish;
2371 
2372                 drop_unused_mounts(root, mounts, &n_mounts);
2373         }
2374 
2375         /* All above is just preparation, figuring out what to do. Let's now actually start doing something. */
2376 
2377         if (unshare(CLONE_NEWNS) < 0) {
2378                 r = log_debug_errno(errno, "Failed to unshare the mount namespace: %m");
2379                 if (IN_SET(r, -EACCES, -EPERM, -EOPNOTSUPP, -ENOSYS))
2380                         /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter
2381                          * in place that doesn't allow us to create namespaces (or a missing cap), then
2382                          * propagate a recognizable error back, which the caller can use to detect this case
2383                          * (and only this) and optionally continue without namespacing applied. */
2384                         r = -ENOANO;
2385 
2386                 goto finish;
2387         }
2388 
2389         /* Create the source directory to allow runtime propagation of mounts */
2390         if (setup_propagate)
2391                 (void) mkdir_p(propagate_dir, 0600);
2392 
2393         if (n_extension_images > 0 || !strv_isempty(extension_directories))
2394                 /* ExtensionImages/Directories mountpoint directories will be created while parsing the
2395                  * mounts to create, so have the parent ready */
2396                 (void) mkdir_p(extension_dir, 0600);
2397 
2398         /* Remount / as SLAVE so that nothing now mounted in the namespace
2399          * shows up in the parent */
2400         if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
2401                 r = log_debug_errno(errno, "Failed to remount '/' as SLAVE: %m");
2402                 goto finish;
2403         }
2404 
2405         if (root_image) {
2406                 /* A root image is specified, mount it to the right place */
2407                 r = dissected_image_mount(dissected_image, root, UID_INVALID, UID_INVALID, dissect_image_flags);
2408                 if (r < 0) {
2409                         log_debug_errno(r, "Failed to mount root image: %m");
2410                         goto finish;
2411                 }
2412 
2413                 /* Now release the block device lock, so that udevd is free to call BLKRRPART on the device
2414                  * if it likes. */
2415                 r = loop_device_flock(loop_device, LOCK_UN);
2416                 if (r < 0) {
2417                         log_debug_errno(r, "Failed to release lock on loopback block device: %m");
2418                         goto finish;
2419                 }
2420 
2421                 if (decrypted_image) {
2422                         r = decrypted_image_relinquish(decrypted_image);
2423                         if (r < 0) {
2424                                 log_debug_errno(r, "Failed to relinquish decrypted image: %m");
2425                                 goto finish;
2426                         }
2427                 }
2428 
2429                 loop_device_relinquish(loop_device);
2430 
2431         } else if (root_directory) {
2432 
2433                 /* A root directory is specified. Turn its directory into bind mount, if it isn't one yet. */
2434                 r = path_is_mount_point(root, NULL, AT_SYMLINK_FOLLOW);
2435                 if (r < 0) {
2436                         log_debug_errno(r, "Failed to detect that %s is a mount point or not: %m", root);
2437                         goto finish;
2438                 }
2439                 if (r == 0) {
2440                         r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL);
2441                         if (r < 0)
2442                                 goto finish;
2443                 }
2444 
2445         } else {
2446                 /* Let's mount the main root directory to the root directory to use */
2447                 r = mount_nofollow_verbose(LOG_DEBUG, "/", root, NULL, MS_BIND|MS_REC, NULL);
2448                 if (r < 0)
2449                         goto finish;
2450         }
2451 
2452         /* Try to set up the new root directory before mounting anything else there. */
2453         if (root_image || root_directory)
2454                 (void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
2455 
2456         /* Now make the magic happen */
2457         r = apply_mounts(root, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
2458         if (r < 0)
2459                 goto finish;
2460 
2461         /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
2462         r = mount_move_root(root);
2463         if (r == -EINVAL && root_directory) {
2464                 /* If we are using root_directory and we don't have privileges (ie: user manager in a user
2465                  * namespace) and the root_directory is already a mount point in the parent namespace,
2466                  * MS_MOVE will fail as we don't have permission to change it (with EINVAL rather than
2467                  * EPERM). Attempt to bind-mount it over itself (like we do above if it's not already a
2468                  * mount point) and try again. */
2469                 r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL);
2470                 if (r < 0)
2471                         goto finish;
2472                 r = mount_move_root(root);
2473         }
2474         if (r < 0) {
2475                 log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
2476                 goto finish;
2477         }
2478 
2479         /* Remount / as the desired mode. Note that this will not
2480          * reestablish propagation from our side to the host, since
2481          * what's disconnected is disconnected. */
2482         if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
2483                 r = log_debug_errno(errno, "Failed to remount '/' with desired mount flags: %m");
2484                 goto finish;
2485         }
2486 
2487         /* bind_mount_in_namespace() will MS_MOVE into that directory, and that's only
2488          * supported for non-shared mounts. This needs to happen after remounting / or it will fail. */
2489         if (setup_propagate) {
2490                 r = mount(NULL, incoming_dir, NULL, MS_SLAVE, NULL);
2491                 if (r < 0) {
2492                         log_error_errno(r, "Failed to remount %s with MS_SLAVE: %m", incoming_dir);
2493                         goto finish;
2494                 }
2495         }
2496 
2497         r = 0;
2498 
2499 finish:
2500         if (n_mounts > 0)
2501                 for (m = mounts; m < mounts + n_mounts; m++)
2502                         mount_entry_done(m);
2503 
2504         free(mounts);
2505 
2506         return r;
2507 }
2508 
bind_mount_free_many(BindMount * b,size_t n)2509 void bind_mount_free_many(BindMount *b, size_t n) {
2510         assert(b || n == 0);
2511 
2512         for (size_t i = 0; i < n; i++) {
2513                 free(b[i].source);
2514                 free(b[i].destination);
2515         }
2516 
2517         free(b);
2518 }
2519 
bind_mount_add(BindMount ** b,size_t * n,const BindMount * item)2520 int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) {
2521         _cleanup_free_ char *s = NULL, *d = NULL;
2522         BindMount *c;
2523 
2524         assert(b);
2525         assert(n);
2526         assert(item);
2527 
2528         s = strdup(item->source);
2529         if (!s)
2530                 return -ENOMEM;
2531 
2532         d = strdup(item->destination);
2533         if (!d)
2534                 return -ENOMEM;
2535 
2536         c = reallocarray(*b, *n + 1, sizeof(BindMount));
2537         if (!c)
2538                 return -ENOMEM;
2539 
2540         *b = c;
2541 
2542         c[(*n) ++] = (BindMount) {
2543                 .source = TAKE_PTR(s),
2544                 .destination = TAKE_PTR(d),
2545                 .read_only = item->read_only,
2546                 .nosuid = item->nosuid,
2547                 .recursive = item->recursive,
2548                 .ignore_enoent = item->ignore_enoent,
2549         };
2550 
2551         return 0;
2552 }
2553 
mount_image_free_many(MountImage * m,size_t * n)2554 MountImage* mount_image_free_many(MountImage *m, size_t *n) {
2555         assert(n);
2556         assert(m || *n == 0);
2557 
2558         for (size_t i = 0; i < *n; i++) {
2559                 free(m[i].source);
2560                 free(m[i].destination);
2561                 mount_options_free_all(m[i].mount_options);
2562         }
2563 
2564         free(m);
2565         *n = 0;
2566         return NULL;
2567 }
2568 
mount_image_add(MountImage ** m,size_t * n,const MountImage * item)2569 int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
2570         _cleanup_free_ char *s = NULL, *d = NULL;
2571         _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
2572         MountImage *c;
2573 
2574         assert(m);
2575         assert(n);
2576         assert(item);
2577 
2578         s = strdup(item->source);
2579         if (!s)
2580                 return -ENOMEM;
2581 
2582         if (item->destination) {
2583                 d = strdup(item->destination);
2584                 if (!d)
2585                         return -ENOMEM;
2586         }
2587 
2588         LIST_FOREACH(mount_options, i, item->mount_options) {
2589                 _cleanup_(mount_options_free_allp) MountOptions *o = NULL;
2590 
2591                 o = new(MountOptions, 1);
2592                 if (!o)
2593                         return -ENOMEM;
2594 
2595                 *o = (MountOptions) {
2596                         .partition_designator = i->partition_designator,
2597                         .options = strdup(i->options),
2598                 };
2599                 if (!o->options)
2600                         return -ENOMEM;
2601 
2602                 LIST_APPEND(mount_options, options, TAKE_PTR(o));
2603         }
2604 
2605         c = reallocarray(*m, *n + 1, sizeof(MountImage));
2606         if (!c)
2607                 return -ENOMEM;
2608 
2609         *m = c;
2610 
2611         c[(*n) ++] = (MountImage) {
2612                 .source = TAKE_PTR(s),
2613                 .destination = TAKE_PTR(d),
2614                 .mount_options = TAKE_PTR(options),
2615                 .ignore_enoent = item->ignore_enoent,
2616                 .type = item->type,
2617         };
2618 
2619         return 0;
2620 }
2621 
temporary_filesystem_free_many(TemporaryFileSystem * t,size_t n)2622 void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n) {
2623         assert(t || n == 0);
2624 
2625         for (size_t i = 0; i < n; i++) {
2626                 free(t[i].path);
2627                 free(t[i].options);
2628         }
2629 
2630         free(t);
2631 }
2632 
temporary_filesystem_add(TemporaryFileSystem ** t,size_t * n,const char * path,const char * options)2633 int temporary_filesystem_add(
2634                 TemporaryFileSystem **t,
2635                 size_t *n,
2636                 const char *path,
2637                 const char *options) {
2638 
2639         _cleanup_free_ char *p = NULL, *o = NULL;
2640         TemporaryFileSystem *c;
2641 
2642         assert(t);
2643         assert(n);
2644         assert(path);
2645 
2646         p = strdup(path);
2647         if (!p)
2648                 return -ENOMEM;
2649 
2650         if (!isempty(options)) {
2651                 o = strdup(options);
2652                 if (!o)
2653                         return -ENOMEM;
2654         }
2655 
2656         c = reallocarray(*t, *n + 1, sizeof(TemporaryFileSystem));
2657         if (!c)
2658                 return -ENOMEM;
2659 
2660         *t = c;
2661 
2662         c[(*n) ++] = (TemporaryFileSystem) {
2663                 .path = TAKE_PTR(p),
2664                 .options = TAKE_PTR(o),
2665         };
2666 
2667         return 0;
2668 }
2669 
make_tmp_prefix(const char * prefix)2670 static int make_tmp_prefix(const char *prefix) {
2671         _cleanup_free_ char *t = NULL;
2672         _cleanup_close_ int fd = -1;
2673         int r;
2674 
2675         /* Don't do anything unless we know the dir is actually missing */
2676         r = access(prefix, F_OK);
2677         if (r >= 0)
2678                 return 0;
2679         if (errno != ENOENT)
2680                 return -errno;
2681 
2682         RUN_WITH_UMASK(000)
2683                 r = mkdir_parents(prefix, 0755);
2684         if (r < 0)
2685                 return r;
2686 
2687         r = tempfn_random(prefix, NULL, &t);
2688         if (r < 0)
2689                 return r;
2690 
2691         /* umask will corrupt this access mode, but that doesn't matter, we need to call chmod() anyway for
2692          * the suid bit, below. */
2693         fd = open_mkdir_at(AT_FDCWD, t, O_EXCL|O_CLOEXEC, 0777);
2694         if (fd < 0)
2695                 return fd;
2696 
2697         r = RET_NERRNO(fchmod(fd, 01777));
2698         if (r < 0) {
2699                 (void) rmdir(t);
2700                 return r;
2701         }
2702 
2703         r = RET_NERRNO(rename(t, prefix));
2704         if (r < 0) {
2705                 (void) rmdir(t);
2706                 return r == -EEXIST ? 0 : r; /* it's fine if someone else created the dir by now */
2707         }
2708 
2709         return 0;
2710 
2711 }
2712 
setup_one_tmp_dir(const char * id,const char * prefix,char ** path,char ** tmp_path)2713 static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) {
2714         _cleanup_free_ char *x = NULL;
2715         _cleanup_free_ char *y = NULL;
2716         sd_id128_t boot_id;
2717         bool rw = true;
2718         int r;
2719 
2720         assert(id);
2721         assert(prefix);
2722         assert(path);
2723 
2724         /* We include the boot id in the directory so that after a
2725          * reboot we can easily identify obsolete directories. */
2726 
2727         r = sd_id128_get_boot(&boot_id);
2728         if (r < 0)
2729                 return r;
2730 
2731         x = strjoin(prefix, "/systemd-private-", SD_ID128_TO_STRING(boot_id), "-", id, "-XXXXXX");
2732         if (!x)
2733                 return -ENOMEM;
2734 
2735         r = make_tmp_prefix(prefix);
2736         if (r < 0)
2737                 return r;
2738 
2739         RUN_WITH_UMASK(0077)
2740                 if (!mkdtemp(x)) {
2741                         if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno))
2742                                 rw = false;
2743                         else
2744                                 return -errno;
2745                 }
2746 
2747         if (rw) {
2748                 y = strjoin(x, "/tmp");
2749                 if (!y)
2750                         return -ENOMEM;
2751 
2752                 RUN_WITH_UMASK(0000)
2753                         if (mkdir(y, 0777 | S_ISVTX) < 0)
2754                                     return -errno;
2755 
2756                 r = label_fix_container(y, prefix, 0);
2757                 if (r < 0)
2758                         return r;
2759 
2760                 if (tmp_path)
2761                         *tmp_path = TAKE_PTR(y);
2762         } else {
2763                 /* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
2764                  * read-only. This way the service will get the EROFS result as if it was writing to the real
2765                  * file system. */
2766                 RUN_WITH_UMASK(0000)
2767                         r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
2768                 if (r < 0)
2769                         return r;
2770 
2771                 r = free_and_strdup(&x, RUN_SYSTEMD_EMPTY);
2772                 if (r < 0)
2773                         return r;
2774         }
2775 
2776         *path = TAKE_PTR(x);
2777         return 0;
2778 }
2779 
setup_tmp_dirs(const char * id,char ** tmp_dir,char ** var_tmp_dir)2780 int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
2781         _cleanup_(namespace_cleanup_tmpdirp) char *a = NULL;
2782         _cleanup_(rmdir_and_freep) char *a_tmp = NULL;
2783         char *b;
2784         int r;
2785 
2786         assert(id);
2787         assert(tmp_dir);
2788         assert(var_tmp_dir);
2789 
2790         r = setup_one_tmp_dir(id, "/tmp", &a, &a_tmp);
2791         if (r < 0)
2792                 return r;
2793 
2794         r = setup_one_tmp_dir(id, "/var/tmp", &b, NULL);
2795         if (r < 0)
2796                 return r;
2797 
2798         a_tmp = mfree(a_tmp); /* avoid rmdir */
2799         *tmp_dir = TAKE_PTR(a);
2800         *var_tmp_dir = TAKE_PTR(b);
2801 
2802         return 0;
2803 }
2804 
setup_shareable_ns(const int ns_storage_socket[static2],unsigned long nsflag)2805 int setup_shareable_ns(const int ns_storage_socket[static 2], unsigned long nsflag) {
2806         _cleanup_close_ int ns = -1;
2807         int r, q;
2808         const char *ns_name, *ns_path;
2809 
2810         assert(ns_storage_socket);
2811         assert(ns_storage_socket[0] >= 0);
2812         assert(ns_storage_socket[1] >= 0);
2813 
2814         ns_name = namespace_single_flag_to_string(nsflag);
2815         assert(ns_name);
2816 
2817         /* We use the passed socketpair as a storage buffer for our
2818          * namespace reference fd. Whatever process runs this first
2819          * shall create a new namespace, all others should just join
2820          * it. To serialize that we use a file lock on the socket
2821          * pair.
2822          *
2823          * It's a bit crazy, but hey, works great! */
2824 
2825         if (lockf(ns_storage_socket[0], F_LOCK, 0) < 0)
2826                 return -errno;
2827 
2828         ns = receive_one_fd(ns_storage_socket[0], MSG_DONTWAIT);
2829         if (ns == -EAGAIN) {
2830                 /* Nothing stored yet, so let's create a new namespace. */
2831 
2832                 if (unshare(nsflag) < 0) {
2833                         r = -errno;
2834                         goto fail;
2835                 }
2836 
2837                 (void) loopback_setup();
2838 
2839                 ns_path = strjoina("/proc/self/ns/", ns_name);
2840                 ns = open(ns_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
2841                 if (ns < 0) {
2842                         r = -errno;
2843                         goto fail;
2844                 }
2845 
2846                 r = 1;
2847 
2848         } else if (ns < 0) {
2849                 r = ns;
2850                 goto fail;
2851 
2852         } else {
2853                 /* Yay, found something, so let's join the namespace */
2854                 if (setns(ns, nsflag) < 0) {
2855                         r = -errno;
2856                         goto fail;
2857                 }
2858 
2859                 r = 0;
2860         }
2861 
2862         q = send_one_fd(ns_storage_socket[1], ns, MSG_DONTWAIT);
2863         if (q < 0) {
2864                 r = q;
2865                 goto fail;
2866         }
2867 
2868 fail:
2869         (void) lockf(ns_storage_socket[0], F_ULOCK, 0);
2870         return r;
2871 }
2872 
open_shareable_ns_path(const int ns_storage_socket[static2],const char * path,unsigned long nsflag)2873 int open_shareable_ns_path(const int ns_storage_socket[static 2], const char *path, unsigned long nsflag) {
2874         _cleanup_close_ int ns = -1;
2875         int q, r;
2876 
2877         assert(ns_storage_socket);
2878         assert(ns_storage_socket[0] >= 0);
2879         assert(ns_storage_socket[1] >= 0);
2880         assert(path);
2881 
2882         /* If the storage socket doesn't contain a ns fd yet, open one via the file system and store it in
2883          * it. This is supposed to be called ahead of time, i.e. before setup_shareable_ns() which will
2884          * allocate a new anonymous ns if needed. */
2885 
2886         if (lockf(ns_storage_socket[0], F_LOCK, 0) < 0)
2887                 return -errno;
2888 
2889         ns = receive_one_fd(ns_storage_socket[0], MSG_DONTWAIT);
2890         if (ns == -EAGAIN) {
2891                 /* Nothing stored yet. Open the file from the file system. */
2892 
2893                 ns = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
2894                 if (ns < 0) {
2895                         r = -errno;
2896                         goto fail;
2897                 }
2898 
2899                 r = fd_is_ns(ns, nsflag);
2900                 if (r == 0) { /* Not a ns of our type? Refuse early. */
2901                         r = -EINVAL;
2902                         goto fail;
2903                 }
2904                 if (r < 0 && r != -EUCLEAN) /* EUCLEAN: we don't know */
2905                         goto fail;
2906 
2907                 r = 1;
2908 
2909         } else if (ns < 0) {
2910                 r = ns;
2911                 goto fail;
2912         } else
2913                 r = 0; /* Already allocated */
2914 
2915         q = send_one_fd(ns_storage_socket[1], ns, MSG_DONTWAIT);
2916         if (q < 0) {
2917                 r = q;
2918                 goto fail;
2919         }
2920 
2921 fail:
2922         (void) lockf(ns_storage_socket[0], F_ULOCK, 0);
2923         return r;
2924 }
2925 
ns_type_supported(NamespaceType type)2926 bool ns_type_supported(NamespaceType type) {
2927         const char *t, *ns_proc;
2928 
2929         t = namespace_type_to_string(type);
2930         if (!t) /* Don't know how to translate this? Then it's not supported */
2931                 return false;
2932 
2933         ns_proc = strjoina("/proc/self/ns/", t);
2934         return access(ns_proc, F_OK) == 0;
2935 }
2936 
2937 static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
2938         [PROTECT_HOME_NO]        = "no",
2939         [PROTECT_HOME_YES]       = "yes",
2940         [PROTECT_HOME_READ_ONLY] = "read-only",
2941         [PROTECT_HOME_TMPFS]     = "tmpfs",
2942 };
2943 
2944 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES);
2945 
2946 static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
2947         [PROTECT_SYSTEM_NO]     = "no",
2948         [PROTECT_SYSTEM_YES]    = "yes",
2949         [PROTECT_SYSTEM_FULL]   = "full",
2950         [PROTECT_SYSTEM_STRICT] = "strict",
2951 };
2952 
2953 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES);
2954 
2955 static const char* const namespace_type_table[] = {
2956         [NAMESPACE_MOUNT]  = "mnt",
2957         [NAMESPACE_CGROUP] = "cgroup",
2958         [NAMESPACE_UTS]    = "uts",
2959         [NAMESPACE_IPC]    = "ipc",
2960         [NAMESPACE_USER]   = "user",
2961         [NAMESPACE_PID]    = "pid",
2962         [NAMESPACE_NET]    = "net",
2963 };
2964 
2965 DEFINE_STRING_TABLE_LOOKUP(namespace_type, NamespaceType);
2966 
2967 static const char* const protect_proc_table[_PROTECT_PROC_MAX] = {
2968         [PROTECT_PROC_DEFAULT]    = "default",
2969         [PROTECT_PROC_NOACCESS]   = "noaccess",
2970         [PROTECT_PROC_INVISIBLE]  = "invisible",
2971         [PROTECT_PROC_PTRACEABLE] = "ptraceable",
2972 };
2973 
2974 DEFINE_STRING_TABLE_LOOKUP(protect_proc, ProtectProc);
2975 
2976 static const char* const proc_subset_table[_PROC_SUBSET_MAX] = {
2977         [PROC_SUBSET_ALL] = "all",
2978         [PROC_SUBSET_PID] = "pid",
2979 };
2980 
2981 DEFINE_STRING_TABLE_LOOKUP(proc_subset, ProcSubset);
2982