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