[lxc-devel] [PATCH v2 7/7] added the unmount-namespace hook

Wolfgang Bumiller w.bumiller at proxmox.com
Fri Oct 2 12:26:08 UTC 2015


As mentioned in the <LXC security issue> thread, I use O_PATH
in this patch which only exists since Linux 2.6.39, but it should be
optional and can be removed (it's just an optimization).
Will wait for the security thread to be resolved to mirror the changes
here.

> On September 30, 2015 at 12:18 PM Wolfgang Bumiller <w.bumiller at proxmox.com>
> wrote:
> 
> 
> Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
> ---
>  hooks/Makefile.am         |   6 ++
>  hooks/unmount-namespace.c | 180
> ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 186 insertions(+)
>  create mode 100644 hooks/unmount-namespace.c
> 
> diff --git a/hooks/Makefile.am b/hooks/Makefile.am
> index be55601..ef82083 100644
> --- a/hooks/Makefile.am
> +++ b/hooks/Makefile.am
> @@ -6,4 +6,10 @@ hooks_SCRIPTS = \
>  	ubuntu-cloud-prep \
>  	squid-deb-proxy-client
>  
> +hooks_PROGRAMS = \
> +	unmount-namespace
> +
> +unmount_namespace_SOURCES = \
> +	unmount-namespace.c
> +
>  EXTRA_DIST=$(hooks_SCRIPTS)
> diff --git a/hooks/unmount-namespace.c b/hooks/unmount-namespace.c
> new file mode 100644
> index 0000000..aa64985
> --- /dev/null
> +++ b/hooks/unmount-namespace.c
> @@ -0,0 +1,180 @@
> +/*
> + * Copyright © 2015 Wolfgang Bumiller <w.bumiller at proxmox.com>.
> + * Copyright © 2015 Proxmox Server Solutions GmbH
> + *
> + * 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    /* setns */
> +#include <stdio.h>     /* fdopen, getmntent, endmntent */
> +#include <stdlib.h>    /* malloc, qsort */
> +#include <unistd.h>    /* close */
> +#include <string.h>    /* strcmp, strncmp, strdup, strerror */
> +#include <sched.h>     /* setns */
> +#include <sys/mount.h> /* umount2 */
> +#include <sys/types.h> /* openat, open */
> +#include <sys/stat.h>  /* openat, open */
> +#include <fcntl.h>     /* openat, open */
> +#include <mntent.h>    /* getmntent, endmntent */
> +#include <errno.h>     /* errno */
> +
> +struct mount {
> +	char *src; /* currently not used */
> +	char *dst;
> +	char *fs; /* currently not used */
> +};
> +
> +static void mount_free(struct mount *mnt) {
> +	free(mnt->src);
> +	free(mnt->dst);
> +	free(mnt->fs);
> +}
> +
> +static int mount_cmp_dst(const void *a_, const void *b_) {
> +	struct mount *a = (struct mount*)a_;
> +	struct mount *b = (struct mount*)b_;
> +	return strcmp(b->dst, a->dst); /* swapped order */
> +}
> +
> +/* Unmounting /dev/pts fails, and  so /dev also fails, but /dev is not what
> + * we're interested in.
> + */
> +static int mount_should_error(const struct mount *mnt) {
> +	const char *dst = mnt->dst;
> +	return !(strncmp(dst, "/dev", 4) == 0 && (dst[4] == 0 || dst[4] == '/'));
> +}
> +
> +/* Read mounts from 'self/mounts' relative to a directory filedescriptor.
> + * Before entering the container we open a handle to /proc on the host as we
> + * need to access /proc/self/mounts and the container's /proc doesn't contain
> + * our /self. We then use openat(2) to avoid having to mount a temporary
> /proc.
> + */
> +static int read_mounts(int procfd, struct mount **mp, size_t *countp) {
> +	int fd;
> +	struct mntent *ent;
> +	FILE *mf;
> +	size_t capacity = 32;
> +	size_t count = 0;
> +	struct mount *mounts = (struct mount*)malloc(capacity * sizeof(*mounts));
> +
> +	*mp = NULL;
> +	*countp = 0;
> +
> +	fd = openat(procfd, "self/mounts", O_RDONLY);
> +	if (fd < 0)
> +		return 0;
> +
> +	mf = fdopen(fd, "r");
> +	if (!mf) {
> +		int error = errno;
> +		close(fd);
> +		errno = error;
> +		return 0;
> +	}
> +	while ((ent = getmntent(mf))) {
> +		if (count == capacity) {
> +			capacity *= 2;
> +			mounts = (struct mount*)realloc(mounts, capacity * sizeof(*mounts));
> +		}
> +		mounts[count].src = strdup(ent->mnt_fsname);
> +		mounts[count].dst = strdup(ent->mnt_dir);
> +		mounts[count].fs  = strdup(ent->mnt_type);
> +		++count;
> +	}
> +	endmntent(mf);
> +
> +	*mp = mounts;
> +	*countp = count;
> +
> +	return 1;
> +}
> +
> +int main(int argc, char **argv) {
> +	int i, procfd, ctmntfd;
> +	struct mount *mounts;
> +	size_t zi, count = 0;
> +	const char *mntns = NULL;
> +
> +	if (argc < 4 || strcmp(argv[2], "lxc") != 0) {
> +		fprintf(stderr, "%s: usage error, expected LXC hook arguments\n", argv[0]);
> +		return 2;
> +	}
> +
> +	if (strcmp(argv[3], "stop") != 0)
> +		return 0;
> +
> +	for (i = 4; i != argc; ++i) {
> +		if (!strncmp(argv[i], "mnt:", 4))
> +			mntns = argv[i] + 4;
> +	}
> +
> +	if (!mntns) {
> +		fprintf(stderr, "%s: no mount namespace provided\n", argv[0]);
> +		return 3;
> +	}
> +
> +	/* Open a handle to /proc on the host as we need to access /proc/self/mounts
> +	 * and the container's /proc doesn't contain our /self. See read_mounts().
> +	 */
> +	procfd = open("/proc", O_RDONLY | O_DIRECTORY | O_PATH);
> +	if (procfd < 0) {
> +		fprintf(stderr, "%s: failed to open /proc: %s\n", argv[0],
> strerror(errno));
> +		return 4;
> +	}
> +
> +	/* Open the mount namespace and enter it. */
> +	ctmntfd = open(mntns, O_RDONLY);
> +	if (ctmntfd < 0) {
> +		fprintf(stderr, "%s: failed to open mount namespace: %s\n",
> +			argv[0], strerror(errno));
> +		close(procfd);
> +		return 5;
> +	}
> +
> +	if (setns(ctmntfd, CLONE_NEWNS) != 0) {
> +		fprintf(stderr, "%s: failed to attach to namespace: %s\n",
> +			argv[0], strerror(errno));
> +		close(ctmntfd);
> +		close(procfd);
> +		return 6;
> +	}
> +	close(ctmntfd);
> +
> +	/* Now read [[procfd]]/self/mounts */
> +	if (!read_mounts(procfd, &mounts, &count)) {
> +		fprintf(stderr, "%s: failed to read mountpoints: %s\n",
> +			argv[0], strerror(errno));
> +		close(procfd);
> +		return 7;
> +	}
> +	close(procfd);
> +
> +	/* Just sort to get a sane unmount-order... */
> +	qsort(mounts, count, sizeof(*mounts), &mount_cmp_dst);
> +
> +	for (zi = 0; zi != count; ++zi) {
> +		/* fprintf(stderr, "Unmount: %s\n", mounts[zi].dst); */
> +		if (umount2(mounts[zi].dst, 0) != 0) {
> +			int error = errno;
> +			if (mount_should_error(&mounts[zi])) {
> +				fprintf(stderr, "%s: failed to unmount %s: %s\n",
> +					argv[0], mounts[zi].dst, strerror(error));
> +			}
> +		}
> +		mount_free(&mounts[zi]);
> +	}
> +	free(mounts);
> +
> +	return 0;
> +}
> -- 
> 2.1.4
> 
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel



More information about the lxc-devel mailing list