Unverified Commit 9ddc6b44 by Christian Brauner Committed by GitHub

Merge pull request #2300 from LizaTretyakova/mount_injection

Mount injection API
parents a633a1ed 54fc984b
......@@ -23,6 +23,7 @@
#define _GNU_SOURCE
#include "config.h"
#include "confile.h"
#include <arpa/inet.h>
#include <dirent.h>
......@@ -647,6 +648,23 @@ unsigned long add_required_remount_flags(const char *s, const char *d,
#endif
}
static int add_shmount_to_list(struct lxc_conf *conf)
{
char new_mount[MAXPATHLEN];
/* Offset for the leading '/' since the path_cont
* is absolute inside the container.
*/
int offset = 1, ret = -1;
ret = snprintf(new_mount, sizeof(new_mount),
"%s %s none bind,create=dir 0 0", conf->shmount.path_host,
conf->shmount.path_cont + offset);
if (ret < 0 || (size_t)ret >= sizeof(new_mount))
return -1;
return add_elem_to_mount_list(new_mount, conf);
}
static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler)
{
int i, r;
......@@ -783,6 +801,14 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
}
}
if (flags & LXC_AUTO_SHMOUNTS_MASK) {
int ret = add_shmount_to_list(conf);
if (ret < 0) {
ERROR("Failed to add shmount entry to container config");
return -1;
}
}
return 0;
}
......@@ -2714,6 +2740,9 @@ struct lxc_conf *lxc_conf_init(void)
new->lsm_aa_profile = NULL;
new->lsm_se_context = NULL;
new->tmp_umount_proc = false;
new->tmp_umount_proc = 0;
new->shmount.path_host = NULL;
new->shmount.path_cont = NULL;
/* if running in a new user namespace, init and COMMAND
* default to running as UID/GID 0 when using lxc-execute */
......@@ -4041,6 +4070,8 @@ void lxc_conf_free(struct lxc_conf *conf)
lxc_clear_procs(conf, "lxc.proc");
free(conf->cgroup_meta.dir);
free(conf->cgroup_meta.controllers);
free(conf->shmount.path_host);
free(conf->shmount.path_cont);
free(conf);
}
......
......@@ -189,6 +189,9 @@ enum {
LXC_AUTO_CGROUP_FULL_NOSPEC = 0x0E0, /* /sys/fs/cgroup (full mount, r/w or mixed, depending on caps) */
LXC_AUTO_CGROUP_FORCE = 0x100, /* mount cgroups even when cgroup namespaces are supported */
LXC_AUTO_CGROUP_MASK = 0x1F0, /* all known cgroup options, doe not contain LXC_AUTO_CGROUP_FORCE */
LXC_AUTO_SHMOUNTS = 0x200, /* shared mount point */
LXC_AUTO_SHMOUNTS_MASK = 0x200, /* shared mount point mask */
LXC_AUTO_ALL_MASK = 0x1FF, /* all known settings */
};
......@@ -367,6 +370,13 @@ struct lxc_conf {
/* procs */
struct lxc_list procs;
struct shmount {
/* Absolute path to the shared mount point on the host */
char *path_host;
/* Absolute path (in the container) to the shared mount point */
char *path_cont;
} shmount;
};
extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
......
......@@ -1659,6 +1659,7 @@ static int set_config_mount_auto(const char *key, const char *value,
{ "cgroup-full:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED | LXC_AUTO_CGROUP_FORCE },
{ "cgroup-full:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO | LXC_AUTO_CGROUP_FORCE },
{ "cgroup-full:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW | LXC_AUTO_CGROUP_FORCE },
{ "shmounts:", LXC_AUTO_SHMOUNTS_MASK, LXC_AUTO_SHMOUNTS },
/* For adding anything that is just a single on/off, but has no
* options: keep mask and flag identical and just define the enum
* value as an unused bit so far
......@@ -1676,6 +1677,8 @@ static int set_config_mount_auto(const char *key, const char *value,
return -1;
for (autoptr = autos;; autoptr = NULL) {
bool is_shmounts = false;
token = strtok_r(autoptr, " \t", &sptr);
if (!token) {
ret = 0;
......@@ -1685,6 +1688,12 @@ static int set_config_mount_auto(const char *key, const char *value,
for (i = 0; allowed_auto_mounts[i].token; i++) {
if (!strcmp(allowed_auto_mounts[i].token, token))
break;
if (strcmp("shmounts:", allowed_auto_mounts[i].token) == 0 &&
strncmp("shmounts:", token, sizeof("shmounts:") - 1) == 0) {
is_shmounts = true;
break;
}
}
if (!allowed_auto_mounts[i].token) {
......@@ -1694,6 +1703,24 @@ static int set_config_mount_auto(const char *key, const char *value,
lxc_conf->auto_mounts &= ~allowed_auto_mounts[i].mask;
lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag;
if (is_shmounts) {
lxc_conf->shmount.path_host = strdup(token + (sizeof("shmounts:") - 1));
if (!lxc_conf->shmount.path_host) {
SYSERROR("Failed to copy shmounts host path");
break;
}
if (strcmp(lxc_conf->shmount.path_host, "") == 0) {
ERROR("Invalid shmounts path: empty");
break;
}
lxc_conf->shmount.path_cont = strdup("/dev/.lxc-mounts");
if(!lxc_conf->shmount.path_cont) {
SYSERROR("Failed to copy shmounts container path");
break;
}
}
}
free(autos);
......@@ -1725,6 +1752,10 @@ static int set_config_mount(const char *key, const char *value,
return 0;
}
int add_elem_to_mount_list(const char *value, struct lxc_conf *lxc_conf) {
return set_config_mount(NULL, value, lxc_conf, NULL);
}
static int set_config_cap_keep(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
......
......@@ -28,7 +28,9 @@
#include <stdbool.h>
#include <stdio.h>
#include <lxc/attach_options.h>
#include <lxc/lxccontainer.h>
struct lxc_conf;
struct lxc_list;
......@@ -121,4 +123,6 @@ bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath,
extern bool network_new_hwaddrs(struct lxc_conf *conf);
extern int add_elem_to_mount_list(const char *value, struct lxc_conf *lxc_conf);
#endif /* __LXC_CONFILE_H */
......@@ -30,9 +30,11 @@
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
......@@ -460,6 +462,25 @@ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \
return ret; \
}
#define WRAP_API_6(rettype, fnname, t1, t2, t3, t4, t5, t6) \
static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3, \
t4 a4, t5 a5, t6 a6) \
{ \
rettype ret; \
bool reset_config = false; \
\
if (!current_config && c && c->lxc_conf) { \
current_config = c->lxc_conf; \
reset_config = true; \
} \
\
ret = do_##fnname(c, a1, a2, a3, a4, a5, a6); \
if (reset_config) \
current_config = NULL; \
\
return ret; \
}
WRAP_API(bool, lxcapi_is_defined)
static const char *do_lxcapi_state(struct lxc_container *c)
......@@ -4893,6 +4914,234 @@ static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool ver
WRAP_API_2(bool, lxcapi_restore, char *, bool)
/* @st_mode is the st_mode field of the stat(source) return struct */
static int create_mount_target(const char *dest, mode_t st_mode)
{
char *dirdup, *destdirname;
int ret;
dirdup = strdup(dest);
if (!dirdup) {
SYSERROR("Failed to duplicate target name \"%s\"", dest);
return -1;
}
destdirname = dirname(dirdup);
ret = mkdir_p(destdirname, 0755);
if (ret < 0) {
SYSERROR("Failed to create \"%s\"", destdirname);
free(dirdup);
return ret;
}
free(dirdup);
(void)remove(dest);
if (S_ISDIR(st_mode))
ret = mkdir(dest, 0000);
else
ret = mknod(dest, S_IFREG | 0000, 0);
if (ret < 0) {
SYSERROR("Failed to create mount target \"%s\"", dest);
return -1;
}
return 0;
}
static int do_lxcapi_mount(struct lxc_container *c, const char *source,
const char *target, const char *filesystemtype,
unsigned long mountflags, const void *data,
struct lxc_mount *mnt)
{
char *suff, *sret;
char template[MAXPATHLEN], path[MAXPATHLEN];
pid_t pid, init_pid;
struct stat sb;
int ret = -1, fd = -EBADF;
if (!c || !c->lxc_conf) {
ERROR("Container or configuration is NULL");
return -EINVAL;
}
if (!c->lxc_conf->shmount.path_host) {
ERROR("Host path to shared mountpoint must be specified in the config\n");
return -EINVAL;
}
ret = snprintf(template, sizeof(template), "%s/.lxcmount_XXXXXX", c->lxc_conf->shmount.path_host);
if (ret < 0 || (size_t)ret >= sizeof(template)) {
SYSERROR("Error writing shmounts tempdir name");
goto out;
}
/* Create a temporary file / dir under the shared mountpoint */
if (!source || strcmp(source, "") == 0) {
/* If source is not specified, maybe we want to mount a filesystem? */
sb.st_mode = S_IFDIR;
} else {
ret = stat(source, &sb);
if (ret < 0) {
SYSERROR("Error getting stat info about the source \"%s\"", source);
goto out;
}
}
if (S_ISDIR(sb.st_mode)) {
sret = mkdtemp(template);
if (!sret) {
SYSERROR("Could not create shmounts temporary dir");
goto out;
}
} else {
fd = lxc_make_tmpfile(template, false);
if (fd < 0) {
SYSERROR("Could not create shmounts temporary file");
goto out;
}
}
/* Do the fork */
pid = fork();
if (pid < 0) {
SYSERROR("Could not fork");
goto out;
}
if (pid == 0) {
/* Do the mount */
ret = mount(source, template, filesystemtype, mountflags, data);
if (ret < 0) {
SYSERROR("Failed to mount onto \"%s\"", template);
_exit(EXIT_FAILURE);
}
TRACE("Mounted \"%s\" onto \"%s\"", source, template);
init_pid = do_lxcapi_init_pid(c);
if (init_pid < 0) {
ERROR("Failed to obtain container's init pid");
_exit(EXIT_FAILURE);
}
/* Enter the container namespaces */
if (!lxc_list_empty(&c->lxc_conf->id_map)) {
if (!switch_to_ns(init_pid, "user")){
ERROR("Failed to enter user namespace");
_exit(EXIT_FAILURE);
}
}
if (!switch_to_ns(init_pid, "mnt")) {
ERROR("Failed to enter mount namespace");
_exit(EXIT_FAILURE);
}
ret = create_mount_target(target, sb.st_mode);
if (ret < 0)
_exit(EXIT_FAILURE);
TRACE("Created mount target \"%s\"", target);
suff = strrchr(template, '/');
if (!suff)
_exit(EXIT_FAILURE);
ret = snprintf(path, sizeof(path), "%s%s", c->lxc_conf->shmount.path_cont, suff);
if (ret < 0 || (size_t)ret >= sizeof(path)) {
SYSERROR("Error writing container mountpoint name");
_exit(EXIT_FAILURE);
}
ret = mount(path, target, NULL, MS_MOVE | MS_REC, NULL);
if (ret < 0) {
SYSERROR("Failed to move the mount from \"%s\" to \"%s\"", path, target);
_exit(EXIT_FAILURE);
}
TRACE("Moved mount from \"%s\" to \"%s\"", path, target);
_exit(EXIT_SUCCESS);
}
ret = wait_for_pid(pid);
if (ret < 0) {
SYSERROR("Wait for the child with pid %ld failed", (long) pid);
goto out;
}
ret = 0;
(void)umount2(template, MNT_DETACH);
(void)unlink(template);
out:
if (fd >= 0)
close(fd);
return ret;
}
WRAP_API_6(int, lxcapi_mount, const char *, const char *, const char *,
unsigned long, const void *, struct lxc_mount *)
static int do_lxcapi_umount(struct lxc_container *c, const char *target,
unsigned long flags, struct lxc_mount *mnt)
{
pid_t pid, init_pid;
int ret = -1;
if (!c || !c->lxc_conf) {
ERROR("Container or configuration is NULL");
return -EINVAL;
}
/* Do the fork */
pid = fork();
if (pid < 0) {
SYSERROR("Could not fork");
return -1;
}
if (pid == 0) {
init_pid = do_lxcapi_init_pid(c);
if (init_pid < 0) {
ERROR("Failed to obtain container's init pid");
_exit(EXIT_FAILURE);
}
/* Enter the container namespaces */
if (!lxc_list_empty(&c->lxc_conf->id_map)) {
if (!switch_to_ns(init_pid, "user")) {
ERROR("Failed to enter user namespace");
_exit(EXIT_FAILURE);
}
}
if (!switch_to_ns(init_pid, "mnt")) {
ERROR("Failed to enter mount namespace");
_exit(EXIT_FAILURE);
}
/* Do the unmount */
ret = umount2(target, flags);
if (ret < 0) {
SYSERROR("Failed to umount \"%s\"", target);
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
ret = wait_for_pid(pid);
if (ret < 0) {
SYSERROR("Wait for the child with pid %ld failed", (long)pid);
return -ret;
}
return 0;
}
WRAP_API_3(int, lxcapi_umount, const char *, unsigned long, struct lxc_mount*)
static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...)
{
va_list ap;
......@@ -5045,6 +5294,8 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
c->restore = lxcapi_restore;
c->migrate = lxcapi_migrate;
c->console_log = lxcapi_console_log;
c->mount = lxcapi_mount;
c->umount = lxcapi_umount;
return c;
......
......@@ -42,6 +42,7 @@ extern "C" {
#define LXC_CLONE_MAXFLAGS (1 << 5) /*!< Number of \c LXC_CLONE_* flags */
#define LXC_CREATE_QUIET (1 << 0) /*!< Redirect \c stdin to \c /dev/zero and \c stdout and \c stderr to \c /dev/null */
#define LXC_CREATE_MAXFLAGS (1 << 1) /*!< Number of \c LXC_CREATE* flags */
#define LXC_MOUNT_API_V1 1
struct bdev_specs;
......@@ -53,6 +54,10 @@ struct migrate_opts;
struct lxc_console_log;
struct lxc_mount {
int version;
};
/*!
* An LXC container.
*
......@@ -846,6 +851,20 @@ struct lxc_container {
* \return \c true if the container was rebooted successfully, else \c false.
*/
bool (*reboot2)(struct lxc_container *c, int timeout);
/*!
* \brief Mount the host's path `source` onto the container's path `target`.
*/
int (*mount)(struct lxc_container *c,
const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data, struct lxc_mount *mnt);
/*!
* \brief Unmount the container's path `target`.
*/
int (*umount)(struct lxc_container *c, const char *target,
unsigned long mountflags, struct lxc_mount *mnt);
};
/*!
......
......@@ -1529,6 +1529,75 @@ static inline int do_share_ns(void *arg)
return 0;
}
static int lxc_setup_shmount(struct lxc_conf *conf)
{
size_t len_cont;
char *full_cont_path;
int ret = -1;
/* Construct the shmount path under the container root. */
len_cont = strlen(conf->rootfs.mount) + 1 + strlen(conf->shmount.path_cont);
/* +1 for the terminating '\0' */
full_cont_path = malloc(len_cont + 1);
if (!full_cont_path) {
SYSERROR("Not enough memory");
return -ENOMEM;
}
ret = snprintf(full_cont_path, len_cont + 1, "%s/%s",
conf->rootfs.mount, conf->shmount.path_cont);
if (ret < 0 || ret >= len_cont + 1) {
SYSERROR("Failed to create filename");
free(full_cont_path);
return -1;
}
/* Check if shmount point is already set up. */
if (is_shared_mountpoint(conf->shmount.path_host)) {
INFO("Path \"%s\" is already MS_SHARED. Reusing",
conf->shmount.path_host);
free(full_cont_path);
return 0;
}
/* Create host and cont mount paths */
ret = mkdir_p(conf->shmount.path_host, 0711);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"",
conf->shmount.path_host);
free(full_cont_path);
return ret;
}
ret = mkdir_p(full_cont_path, 0711);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", full_cont_path);
free(full_cont_path);
return ret;
}
/* Prepare host mountpoint */
ret = mount("tmpfs", conf->shmount.path_host, "tmpfs", 0,
"size=100k,mode=0711");
if (ret < 0) {
SYSERROR("Failed to mount \"%s\"", conf->shmount.path_host);
free(full_cont_path);
return ret;
}
ret = mount(conf->shmount.path_host, conf->shmount.path_host, "none",
MS_REC | MS_SHARED, "");
if (ret < 0) {
SYSERROR("Failed to make shared \"%s\"", conf->shmount.path_host);
free(full_cont_path);
return ret;
}
INFO("Setup shared mount point \"%s\"", conf->shmount.path_host);
free(full_cont_path);
return 0;
}
/* lxc_spawn() performs crucial setup tasks and clone()s the new process which
* exec()s the requested container binary.
* Note that lxc_spawn() runs in the parent namespaces. Any operations performed
......@@ -1606,6 +1675,25 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
if (conf->shmount.path_host) {
if (!conf->shmount.path_cont) {
lxc_sync_fini(handler);
return -1;
}
ret = lxc_setup_shmount(conf);
if (ret < 0) {
ERROR("Failed to setup shared mount point");
lxc_sync_fini(handler);
return -1;
}
}
if (!cgroup_init(handler)) {
ERROR("Failed initializing cgroup support");
goto out_delete_net;
}
if (!cgroup_ops->create(cgroup_ops, handler)) {
ERROR("Failed creating cgroups");
goto out_delete_net;
......
......@@ -1219,19 +1219,12 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
return hval;
}
/*
* Detect whether / is mounted MS_SHARED. The only way I know of to
* check that is through /proc/self/mountinfo.
* I'm only checking for /. If the container rootfs or mount location
* is MS_SHARED, but not '/', then you're out of luck - figuring that
* out would be too much work to be worth it.
*/
int detect_shared_rootfs(void)
bool is_shared_mountpoint(const char *path)
{
char buf[LXC_LINELEN], *p;
char buf[LXC_LINELEN];
FILE *f;
int i;
char *p2;
char *p, *p2;
f = fopen("/proc/self/mountinfo", "r");
if (!f)
......@@ -1248,17 +1241,31 @@ int detect_shared_rootfs(void)
continue;
*p2 = '\0';
if (strcmp(p + 1, "/") == 0) {
/* This is '/'. Is it shared? */
if (strcmp(p + 1, path) == 0) {
/* This is the path. Is it shared? */
p = strchr(p2 + 1, ' ');
if (p && strstr(p, "shared:")) {
fclose(f);
return 1;
return true;
}
}
}
fclose(f);
return false;
}
/*
* Detect whether / is mounted MS_SHARED. The only way I know of to
* check that is through /proc/self/mountinfo.
* I'm only checking for /. If the container rootfs or mount location
* is MS_SHARED, but not '/', then you're out of luck - figuring that
* out would be too much work to be worth it.
*/
int detect_shared_rootfs(void)
{
if (is_shared_mountpoint("/"))
return 1;
return 0;
}
......
......@@ -502,6 +502,7 @@ extern bool dir_exists(const char *path);
#define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
extern uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval);
extern bool is_shared_mountpoint(const char *path);
extern int detect_shared_rootfs(void);
extern bool detect_ramfs_rootfs(void);
extern char *on_path(const char *cmd, const char *rootfs);
......
......@@ -33,6 +33,7 @@ lxc_test_state_server_SOURCES = state_server.c lxctest.h
lxc_test_share_ns_SOURCES = share_ns.c lxctest.h
lxc_test_criu_check_feature_SOURCES = criu_check_feature.c lxctest.h
lxc_test_raw_clone_SOURCES = lxc_raw_clone.c lxctest.h
lxc_test_mount_injection_SOURCES = mount_injection.c
AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DLXCPATH=\"$(LXCPATH)\" \
......@@ -64,7 +65,8 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \
lxc-test-config-jump-table lxc-test-shortlived \
lxc-test-api-reboot lxc-test-state-server lxc-test-share-ns \
lxc-test-criu-check-feature lxc-test-raw-clone
lxc-test-criu-check-feature lxc-test-raw-clone \
lxc-test-mount-injection
bin_SCRIPTS =
if ENABLE_TOOLS
......@@ -121,6 +123,7 @@ EXTRA_DIST = \
lxc-test-unpriv \
lxc-test-utils.c \
may_control.c \
mount_injection.c \
parse_config_file.c \
saveconfig.c \
shortlived.c \
......
/* mount_injection
*
* Copyright © 2018 Elizaveta Tretiakova <elizabet.tretyakova@gmail.com>.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lxc/lxccontainer.h>
#include <lxc/list.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lxctest.h"
#include "utils.h"
#define NAME "mount_injection_test-"
#define TEMPLATE P_tmpdir"/mount_injection_XXXXXX"
struct mountinfo_data {
const char *mount_root;
const char *mount_point;
const char *fstype;
const char *mount_source;
const char *message;
bool should_be_present;
};
static int comp_field(char *line, const char *str, int nfields)
{
char *p, *p2;
int i, ret;
if(!line)
return -1;
if (!str)
return 0;
for (p = line, i = 0; p && i < nfields; i++)
p = strchr(p + 1, ' ');
if (!p)
return -1;
p2 = strchr(p + 1, ' ');
if (p2)
*p2 = '\0';
ret = strcmp(p + 1, str);
if (p2)
*p2 = ' ';
return ret;
}
static int find_in_proc_mounts(void *data)
{
char buf[LXC_LINELEN];
FILE *f;
struct mountinfo_data *mdata = (struct mountinfo_data *)data;
fprintf(stderr, "%s", mdata->message);
f = fopen("/proc/self/mountinfo", "r");
if (!f)
return 0;
while (fgets(buf, LXC_LINELEN, f)) {
char *buf2;
/* checking mount_root is tricky since it will be prefixed with
* whatever path was the source of the mount in the original
* mount namespace. So only verify it when we know that root is
* in fact "/".
*/
if (mdata->mount_root && comp_field(buf, mdata->mount_root, 3) != 0)
continue;
if (comp_field(buf, mdata->mount_point, 4) != 0)
continue;
if (!mdata->fstype || !mdata->mount_source)
goto on_success;
buf2 = strchr(buf, '-');
if (comp_field(buf2, mdata->fstype, 1) != 0 ||
comp_field(buf2, mdata->mount_source, 2) != 0)
continue;
on_success:
fclose(f);
fprintf(stderr, "PRESENT\n");
if (mdata->should_be_present)
_exit(EXIT_SUCCESS);
_exit(EXIT_FAILURE);
}
fclose(f);
fprintf(stderr, "MISSING\n");
if (!mdata->should_be_present)
_exit(EXIT_SUCCESS);
_exit(EXIT_FAILURE);
}
static int check_containers_mountinfo(struct lxc_container *c, struct mountinfo_data *d)
{
pid_t pid;
int ret = -1;
lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
ret = c->attach(c, find_in_proc_mounts, d, &attach_options, &pid);
if (ret < 0) {
fprintf(stderr, "Check of the container's mountinfo failed\n");
return ret;
}
ret = wait_for_pid(pid);
if (ret < 0)
fprintf(stderr, "Attached function failed\n");
return ret;
}
/* config_items: NULL-terminated array of config pairs */
static int perform_container_test(const char *name, const char *config_items[])
{
int i;
char *sret;
char template_log[sizeof(TEMPLATE)], template_dir[sizeof(TEMPLATE)],
device_message[sizeof("Check urandom device injected into "" - ") - 1 + strlen(name) + 1],
dir_message[sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE) - 1 + strlen(name) + 1],
fs_message[sizeof("Check devtmpfs injected into "" - ") - 1 + strlen(name) + 1];
struct lxc_container *c;
struct lxc_mount mnt;
struct lxc_log log;
int ret = -1, dev_msg_size = sizeof("Check urandom device injected into "" - ") - 1 + strlen(name) + 1,
dir_msg_size = sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE) - 1 + strlen(name) + 1,
fs_msg_size = sizeof("Check devtmpfs injected into "" - ") - 1 + strlen(name) + 1;
struct mountinfo_data device = {
.mount_root = "/urandom",
.mount_point = "/mnt/mount_injection_test_urandom",
.fstype = NULL,
.mount_source = "/dev/urandom",
.message = "",
.should_be_present = true
}, dir = {
.mount_root = NULL,
.mount_point = template_dir,
.fstype = NULL,
.mount_source = NULL,
.message = "",
.should_be_present = true
}, fs = {
.mount_root = "/",
.mount_point = "/mnt/mount_injection_test_devtmpfs",
.fstype = "devtmpfs",
.mount_source = NULL,
.message = "",
.should_be_present = true
};
/* Temp paths and messages setup */
strcpy(template_dir, TEMPLATE);
sret = mkdtemp(template_dir);
if (!sret) {
lxc_error("Failed to create temporary src file for container %s\n", name);
exit(EXIT_FAILURE);
}
ret = snprintf(device_message, dev_msg_size, "Check urandom device injected into %s - ", name);
if (ret < 0 || ret >= dev_msg_size) {
fprintf(stderr, "Failed to create message for dev\n");
exit(EXIT_FAILURE);
}
device.message = &device_message[0];
ret = snprintf(dir_message, dir_msg_size, "Check dir %s injected into %s - ", template_dir, name);
if (ret < 0 || ret >= dir_msg_size) {
fprintf(stderr, "Failed to create message for dir\n");
exit(EXIT_FAILURE);
}
dir.message = &dir_message[0];
ret = snprintf(fs_message, fs_msg_size, "Check devtmpfs injected into %s - ", name);
if (ret < 0 || ret >= fs_msg_size) {
fprintf(stderr, "Failed to create message for fs\n");
exit(EXIT_FAILURE);
}
fs.message = &fs_message[0];
/* Setup logging*/
strcpy(template_log, TEMPLATE);
i = lxc_make_tmpfile(template_log, false);
if (i < 0) {
lxc_error("Failed to create temporary log file for container %s\n", name);
exit(EXIT_FAILURE);
} else {
lxc_debug("Using \"%s\" as temporary log file for container %s\n", template_log, name);
close(i);
}
log.name = name;
log.file = template_log;
log.level = "TRACE";
log.prefix = "mount-injection";
log.quiet = false;
log.lxcpath = NULL;
if (lxc_log_init(&log))
exit(EXIT_FAILURE);
/* Container setup */
c = lxc_container_new(name, NULL);
if (!c) {
fprintf(stderr, "Unable to instantiate container (%s)...\n", name);
goto out;
}
if (c->is_defined(c)) {
fprintf(stderr, "Container (%s) already exists\n", name);
goto out;
}
for (i = 0; config_items[i]; i += 2) {
if (!c->set_config_item(c, config_items[i], config_items[i + 1])) {
fprintf(stderr, "Failed to set \"%s\" config option to \"%s\"\n", config_items[i], config_items[i + 1]);
goto out;
}
}
if (!c->create(c, "busybox", NULL, NULL, 1, NULL)) {
fprintf(stderr, "Creating the container (%s) failed...\n", name);
goto out;
}
c->want_daemonize(c, true);
if (!c->start(c, false, NULL)) {
fprintf(stderr, "Starting the container (%s) failed...\n", name);
goto out;
}
mnt.version = LXC_MOUNT_API_V1;
/* Check device mounted */
ret = c->mount(c, "/dev/urandom", "/mnt/mount_injection_test_urandom", NULL, MS_BIND, NULL, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to mount \"/dev/urandom\"\n");
goto out;
}
ret = check_containers_mountinfo(c, &device);
if (ret < 0)
goto out;
/* Check device unmounted */
/* TODO: what about other umount flags? */
ret = c->umount(c, "/mnt/mount_injection_test_urandom", MNT_DETACH, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to umount2 \"/dev/urandom\"\n");
goto out;
}
device.message = "Unmounted \"/mnt/mount_injection_test_urandom\" -- should be missing now: ";
device.should_be_present = false;
ret = check_containers_mountinfo(c, &device);
if (ret < 0)
goto out;
/* Check dir mounted */
ret = c->mount(c, template_dir, template_dir, NULL, MS_BIND, NULL, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to mount \"%s\"\n", template_dir);
goto out;
}
ret = check_containers_mountinfo(c, &dir);
if (ret < 0)
goto out;
/* Check dir unmounted */
/* TODO: what about other umount flags? */
ret = c->umount(c, template_dir, MNT_DETACH, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to umount2 \"%s\"\n", template_dir);
goto out;
}
dir.message = "Unmounted dir -- should be missing now: ";
dir.should_be_present = false;
ret = check_containers_mountinfo(c, &dir);
if (ret < 0)
goto out;
/* Check fs mounted */
ret = c->mount(c, NULL, "/mnt/mount_injection_test_devtmpfs", "devtmpfs", 0, NULL, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to mount devtmpfs\n");
goto out;
}
ret = check_containers_mountinfo(c, &fs);
if (ret < 0)
goto out;
/* Check fs unmounted */
/* TODO: what about other umount flags? */
ret = c->umount(c, "/mnt/mount_injection_test_devtmpfs", MNT_DETACH, &mnt);
if (ret < 0) {
fprintf(stderr, "Failed to umount2 devtmpfs\n");
goto out;
}
fs.message = "Unmounted \"/mnt/mount_injection_test_devtmpfs\" -- should be missing now: ";
fs.should_be_present = false;
ret = check_containers_mountinfo(c, &fs);
if (ret < 0)
goto out;
/* Finalize the container */
if (!c->stop(c)) {
fprintf(stderr, "Stopping the container (%s) failed...\n", name);
goto out;
}
if (!c->destroy(c)) {
fprintf(stderr, "Destroying the container (%s) failed...\n", name);
goto out;
}
ret = 0;
out:
lxc_container_put(c);
if (ret != 0) {
int fd;
fd = open(template_log, O_RDONLY);
if (fd >= 0) {
char buf[4096];
ssize_t buflen;
while ((buflen = read(fd, buf, 1024)) > 0) {
buflen = write(STDERR_FILENO, buf, buflen);
if (buflen <= 0)
break;
}
close(fd);
}
}
unlink(template_log);
unlink(template_dir);
return ret;
}
static int do_priv_container_test()
{
const char *config_items[] = {"lxc.mount.auto", "shmounts:/tmp/mount_injection_test", NULL};
return perform_container_test(NAME"privileged", config_items);
}
static int do_unpriv_container_test()
{
const char *config_items[] = {
"lxc.mount.auto", "shmounts:/tmp/mount_injection_test",
NULL
};
return perform_container_test(NAME"unprivileged", config_items);
}
int main(int argc, char *argv[])
{
if (do_priv_container_test()) {
fprintf(stderr, "Privileged mount injection test failed\n");
return -1;
}
if(do_unpriv_container_test()) {
fprintf(stderr, "Unprivileged mount injection test failed\n");
return -1;
}
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment