diff options
Diffstat (limited to 'src/core/namespace.c')
-rw-r--r-- | src/core/namespace.c | 216 |
1 files changed, 129 insertions, 87 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c index db9a7aa5e7..67b203ba76 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -58,8 +58,7 @@ typedef enum MountMode { } MountMode; typedef struct BindMount { - const char *path; /* stack memory, doesn't need to be freed explicitly */ - char *chased; /* malloc()ed memory, needs to be freed */ + char *path; MountMode mode; bool ignore; /* Ignore if path does not exist */ } BindMount; @@ -155,10 +154,23 @@ static const TargetMount protect_system_strict_table[] = { { "/root", READWRITE, true }, /* ProtectHome= */ }; -static void set_bind_mount(BindMount **p, const char *path, MountMode mode, bool ignore) { - (*p)->path = path; - (*p)->mode = mode; - (*p)->ignore = ignore; +static void set_bind_mount(BindMount *p, char *path, MountMode mode, bool ignore) { + p->path = path; + p->mode = mode; + p->ignore = ignore; +} + +static int append_one_mount(BindMount **p, const char *root_directory, + const char *path, MountMode mode, bool ignore) { + char *lpath; + assert(p); + + lpath = prefix_root(root_directory, path); + if (!lpath) + return -ENOMEM; + + set_bind_mount((*p)++, lpath, mode, ignore); + return 0; } static int append_mounts(BindMount **p, char **strv, MountMode mode) { @@ -168,6 +180,7 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) { STRV_FOREACH(i, strv) { bool ignore = false; + char *path; if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) { (*i)++; @@ -177,52 +190,66 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) { if (!path_is_absolute(*i)) return -EINVAL; - set_bind_mount(p, *i, mode, ignore); - (*p)++; + path = strdup(*i); + if (!path) + return -ENOMEM; + + set_bind_mount((*p)++, path, mode, ignore); } return 0; } -static int append_target_mounts(BindMount **p, const char *root_directory, const TargetMount *mounts, const size_t size) { +static int append_target_mounts(BindMount **p, const char *root_directory, + const TargetMount *mounts, const size_t size, bool ignore_protect) { unsigned i; assert(p); assert(mounts); for (i = 0; i < size; i++) { + bool ignore; /* * Here we assume that the ignore field is set during * declaration we do not support "-" at the beginning. */ const TargetMount *m = &mounts[i]; - const char *path = prefix_roota(root_directory, m->path); + char *path; + + path = prefix_root(root_directory, m->path); + if (!path) + return -ENOMEM; if (!path_is_absolute(path)) return -EINVAL; - set_bind_mount(p, path, m->mode, m->ignore); - (*p)++; + /* + * Ignore paths if they are not present. First we use our + * static tables otherwise fallback to Unit context. + */ + ignore = m->ignore || ignore_protect; + + set_bind_mount((*p)++, path, m->mode, ignore); } return 0; } -static int append_protect_kernel_tunables(BindMount **p, const char *root_directory) { +static int append_protect_kernel_tunables(BindMount **p, const char *root_directory, bool ignore_protect) { assert(p); return append_target_mounts(p, root_directory, protect_kernel_tunables_table, - ELEMENTSOF(protect_kernel_tunables_table)); + ELEMENTSOF(protect_kernel_tunables_table), ignore_protect); } -static int append_protect_kernel_modules(BindMount **p, const char *root_directory) { +static int append_protect_kernel_modules(BindMount **p, const char *root_directory, bool ignore_protect) { assert(p); return append_target_mounts(p, root_directory, protect_kernel_modules_table, - ELEMENTSOF(protect_kernel_modules_table)); + ELEMENTSOF(protect_kernel_modules_table), ignore_protect); } -static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home) { +static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home, bool ignore_protect) { int r = 0; assert(p); @@ -233,11 +260,12 @@ static int append_protect_home(BindMount **p, const char *root_directory, Protec switch (protect_home) { case PROTECT_HOME_READ_ONLY: r = append_target_mounts(p, root_directory, protect_home_read_only_table, - ELEMENTSOF(protect_home_read_only_table)); + ELEMENTSOF(protect_home_read_only_table), + ignore_protect); break; case PROTECT_HOME_YES: r = append_target_mounts(p, root_directory, protect_home_yes_table, - ELEMENTSOF(protect_home_yes_table)); + ELEMENTSOF(protect_home_yes_table), ignore_protect); break; default: r = -EINVAL; @@ -247,7 +275,7 @@ static int append_protect_home(BindMount **p, const char *root_directory, Protec return r; } -static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system) { +static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system, bool ignore_protect) { int r = 0; assert(p); @@ -258,15 +286,15 @@ static int append_protect_system(BindMount **p, const char *root_directory, Prot switch (protect_system) { case PROTECT_SYSTEM_STRICT: r = append_target_mounts(p, root_directory, protect_system_strict_table, - ELEMENTSOF(protect_system_strict_table)); + ELEMENTSOF(protect_system_strict_table), ignore_protect); break; case PROTECT_SYSTEM_YES: r = append_target_mounts(p, root_directory, protect_system_yes_table, - ELEMENTSOF(protect_system_yes_table)); + ELEMENTSOF(protect_system_yes_table), ignore_protect); break; case PROTECT_SYSTEM_FULL: r = append_target_mounts(p, root_directory, protect_system_full_table, - ELEMENTSOF(protect_system_full_table)); + ELEMENTSOF(protect_system_full_table), ignore_protect); break; default: r = -EINVAL; @@ -303,12 +331,13 @@ static void drop_duplicates(BindMount *m, unsigned *n) { /* Drops duplicate entries. Expects that the array is properly ordered already. */ - for (f = m, t = m, previous = NULL; f < m+*n; f++) { + for (f = m, t = m, previous = NULL; f < m + *n; f++) { /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() * above. */ if (previous && path_equal(f->path, previous->path)) { log_debug("%s is duplicate.", f->path); + f->path = mfree(f->path); continue; } @@ -330,12 +359,13 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly * ordered already. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop * it, as inaccessible paths really should drop the entire subtree. */ if (clear && path_startswith(f->path, clear)) { log_debug("%s is masked by %s.", f->path, clear); + f->path = mfree(f->path); continue; } @@ -357,7 +387,7 @@ static void drop_nop(BindMount *m, unsigned *n) { /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the * list is ordered by prefixes. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { /* Only suppress such subtrees for READONLY and READWRITE entries */ if (IN_SET(f->mode, READONLY, READWRITE)) { @@ -375,6 +405,7 @@ static void drop_nop(BindMount *m, unsigned *n) { /* We found it, let's see if it's the same mode, if so, we can drop this entry */ if (found && p->mode == f->mode) { log_debug("%s is redundant by %s", f->path, p->path); + f->path = mfree(f->path); continue; } } @@ -392,15 +423,17 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned assert(m); assert(n); + /* Nothing to do */ if (!root_directory) return; /* Drops all mounts that are outside of the root directory. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { if (!path_startswith(f->path, root_directory)) { log_debug("%s is outside of root directory.", f->path); + f->path = mfree(f->path); continue; } @@ -591,7 +624,6 @@ static int apply_mount( return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path); 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. */ return 0; - /* This isn't a mount point yet, let's make it one. */ what = m->path; break; @@ -640,9 +672,10 @@ static int make_read_only(BindMount *m, char **blacklist) { return r; } +/* Chase symlinks and remove failed paths from mounts */ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { BindMount *f, *t; - int r; + int r = 0; assert(m); assert(n); @@ -651,19 +684,25 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we * can't resolve the path, and which have been marked for such removal. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { + int k; + _cleanup_free_ char *chased = NULL; + + k = chase_symlinks(f->path, root_directory, &chased); + if (k < 0) { + /* Get only real errors */ + if (r >= 0 && (k != -ENOENT || !f->ignore)) + r = k; - r = chase_symlinks(f->path, root_directory, &f->chased); - if (r == -ENOENT && f->ignore) /* Doesn't exist? Then remove it! */ + log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + /* Doesn't exist or failed? Then remove it and continue! */ + f->path = mfree(f->path); continue; - if (r < 0) - return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + } - if (path_equal(f->path, f->chased)) - f->chased = mfree(f->chased); - else { - log_debug("Chased %s → %s", f->path, f->chased); - f->path = f->chased; + if (!path_equal(f->path, chased)) { + log_debug("Chased %s → %s", f->path, chased); + free_and_replace(f->path, chased); } *t = *f; @@ -671,7 +710,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned } *n = t - m; - return 0; + return r; } static unsigned namespace_calculate_mounts( @@ -724,96 +763,99 @@ int setup_namespace( BindMount *m, *mounts = NULL; bool make_slave = false; - unsigned n; + unsigned n_mounts; int r = 0; if (mount_flags == 0) mount_flags = MS_SHARED; - n = namespace_calculate_mounts(ns_info, - read_write_paths, - read_only_paths, - inaccessible_paths, - tmp_dir, var_tmp_dir, - protect_home, protect_system); + n_mounts = namespace_calculate_mounts(ns_info, + read_write_paths, + read_only_paths, + inaccessible_paths, + tmp_dir, var_tmp_dir, + protect_home, protect_system); /* Set mount slave mode */ - if (root_directory || n > 0) + if (root_directory || n_mounts > 0) make_slave = true; - if (n > 0) { - m = mounts = (BindMount *) alloca0(n * sizeof(BindMount)); + if (n_mounts > 0) { + m = mounts = (BindMount *) alloca0(n_mounts * sizeof(BindMount)); r = append_mounts(&m, read_write_paths, READWRITE); if (r < 0) - return r; + goto finish; r = append_mounts(&m, read_only_paths, READONLY); if (r < 0) - return r; + goto finish; r = append_mounts(&m, inaccessible_paths, INACCESSIBLE); if (r < 0) - return r; + goto finish; if (tmp_dir) { - m->path = prefix_roota(root_directory, "/tmp"); - m->mode = PRIVATE_TMP; - m++; + r = append_one_mount(&m, root_directory, "/tmp", PRIVATE_TMP, false); + if (r < 0) + goto finish; } if (var_tmp_dir) { - m->path = prefix_roota(root_directory, "/var/tmp"); - m->mode = PRIVATE_VAR_TMP; - m++; + r = append_one_mount(&m, root_directory, "/var/tmp", PRIVATE_VAR_TMP, false); + if (r < 0) + goto finish; } if (ns_info->private_dev) { - m->path = prefix_roota(root_directory, "/dev"); - m->mode = PRIVATE_DEV; - m++; + r = append_one_mount(&m, root_directory, "/dev", PRIVATE_DEV, false); + if (r < 0) + goto finish; } if (ns_info->protect_kernel_tunables) { - r = append_protect_kernel_tunables(&m, root_directory); + r = append_protect_kernel_tunables(&m, root_directory, + ns_info->ignore_protect_paths); if (r < 0) - return r; + goto finish; } if (ns_info->protect_kernel_modules) { - r = append_protect_kernel_modules(&m, root_directory); + r = append_protect_kernel_modules(&m, root_directory, + ns_info->ignore_protect_paths); if (r < 0) - return r; + goto finish; } if (ns_info->protect_control_groups) { - m->path = prefix_roota(root_directory, "/sys/fs/cgroup"); - m->mode = READONLY; - m++; + r = append_one_mount(&m, root_directory, "/sys/fs/cgroup", READONLY, false); + if (r < 0) + goto finish; } - r = append_protect_home(&m, root_directory, protect_home); + r = append_protect_home(&m, root_directory, protect_home, + ns_info->ignore_protect_paths); if (r < 0) - return r; + goto finish; - r = append_protect_system(&m, root_directory, protect_system); + r = append_protect_system(&m, root_directory, protect_system, false); if (r < 0) - return r; + goto finish; - assert(mounts + n == m); + assert(mounts + n_mounts == m); /* Resolve symlinks manually first, as mount() will always follow them relative to the host's * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit * racy. */ - r = chase_all_symlinks(root_directory, mounts, &n); + r = chase_all_symlinks(root_directory, mounts, &n_mounts); if (r < 0) goto finish; - qsort(mounts, n, sizeof(BindMount), mount_path_compare); + qsort(mounts, n_mounts, sizeof(BindMount), mount_path_compare); - drop_duplicates(mounts, &n); - drop_outside_root(root_directory, mounts, &n); - drop_inaccessible(mounts, &n); - drop_nop(mounts, &n); + drop_duplicates(mounts, &n_mounts); + drop_outside_root(root_directory, mounts, &n_mounts); + drop_inaccessible(mounts, &n_mounts); + drop_nop(mounts, &n_mounts); } if (unshare(CLONE_NEWNS) < 0) { @@ -843,25 +885,25 @@ int setup_namespace( } } - if (n > 0) { + if (n_mounts > 0) { char **blacklist; unsigned j; /* First round, add in all special mounts we need */ - for (m = mounts; m < mounts + n; ++m) { + for (m = mounts; m < mounts + n_mounts; ++m) { r = apply_mount(m, tmp_dir, var_tmp_dir); if (r < 0) goto finish; } /* Create a blacklist we can pass to bind_mount_recursive() */ - blacklist = newa(char*, n+1); - for (j = 0; j < n; j++) + blacklist = newa(char*, n_mounts+1); + for (j = 0; j < n_mounts; j++) blacklist[j] = (char*) mounts[j].path; blacklist[j] = NULL; /* Second round, flip the ro bits if necessary. */ - for (m = mounts; m < mounts + n; ++m) { + for (m = mounts; m < mounts + n_mounts; ++m) { r = make_read_only(m, blacklist); if (r < 0) goto finish; @@ -886,8 +928,8 @@ int setup_namespace( r = 0; finish: - for (m = mounts; m < mounts + n; m++) - free(m->chased); + for (m = mounts; m < mounts + n_mounts; m++) + free(m->path); return r; } |