aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEivind Uggedal <eivind@uggedal.com>2015-09-30 17:47:51 +0000
committerEivind Uggedal <eivind@uggedal.com>2015-09-30 17:50:31 +0000
commitb9333a8dc06ac07169fc3199b93eee3aa48dcf93 (patch)
tree64fa462372e09f0d996bf00ef1ce35d5958a8313
parent09295609534a7bcaff507fbf6199bbe95bdf285e (diff)
downloadaports-b9333a8dc06ac07169fc3199b93eee3aa48dcf93.tar.bz2
aports-b9333a8dc06ac07169fc3199b93eee3aa48dcf93.tar.xz
main/lxc: security fix for CVE-2014-1334,CVE-2015-1331,CVE-2015-1335
-rw-r--r--main/lxc/APKBUILD21
-rw-r--r--main/lxc/CVE-2014-1334.patch183
-rw-r--r--main/lxc/CVE-2015-1331.patch100
-rw-r--r--main/lxc/CVE-2015-1335.patch475
4 files changed, 775 insertions, 4 deletions
diff --git a/main/lxc/APKBUILD b/main/lxc/APKBUILD
index e3d77d509f..68eeb7d12c 100644
--- a/main/lxc/APKBUILD
+++ b/main/lxc/APKBUILD
@@ -3,7 +3,7 @@
pkgname=lxc
pkgver=1.0.6
_mypkgver=${pkgver/_rc/.rc}
-pkgrel=0
+pkgrel=1
pkgdesc="linux containers - tools"
url="http://lxc.sourceforge.net/"
arch="all"
@@ -17,6 +17,10 @@ subpackages="$pkgname-dev $pkgname-doc $pkgname-lvm lua5.2-lxc:_lua52
source="https://github.com/lxc/lxc/archive/lxc-$_mypkgver.tar.gz
version.patch
lxc.initd
+
+ CVE-2014-1334.patch
+ CVE-2015-1331.patch
+ CVE-2015-1335.patch
"
_builddir="${srcdir}/lxc-lxc-${_mypkgver}"
@@ -86,10 +90,19 @@ dev() {
md5sums="3949cd916a120f40c2355c9a5b65cd51 lxc-1.0.6.tar.gz
79e90616b5049a472ccdcb5b1dcdd8b1 version.patch
-1268d4b3e6ed004a47216b8714d09bfd lxc.initd"
+1268d4b3e6ed004a47216b8714d09bfd lxc.initd
+33444cc34285e2cd6933fef01c0a7ca8 CVE-2014-1334.patch
+5d9e5fc3a4d02ea986d84ef7b29f359c CVE-2015-1331.patch
+47df9db0bbb78d9b822edc508885c718 CVE-2015-1335.patch"
sha256sums="2aea199a89e2cd946f93406af6c3f62844f36954b79a6991b36d2c33022cb11c lxc-1.0.6.tar.gz
b6d85fb23940d2511b3951de56b2532843c0e03ec1613548366361cc0c1a46b9 version.patch
-bc108a722dc359a24c48837ef7012c776b1d20a533ae0e2231f75081dad4e2f5 lxc.initd"
+bc108a722dc359a24c48837ef7012c776b1d20a533ae0e2231f75081dad4e2f5 lxc.initd
+9d9f108e174e0a19789107f8ac8f25a7fe6d1732bef1ca62e334745f87d1ee07 CVE-2014-1334.patch
+f6974204ad7db4188df16d658ae332fc8a8317ff4ff66363028516f86996a4b1 CVE-2015-1331.patch
+ef7a48440a92404b4a799b91e16f10a38463d5031109f0dbdee0cc88826abeca CVE-2015-1335.patch"
sha512sums="fe85ccb57865d86704df6b4b79d60f31892785b07dc9dd2580cc6c384c89c29c23516e906b7a16bc03c6582c1fb2432bb8ff11bd17c09efa8f6a035fb41f46b1 lxc-1.0.6.tar.gz
e2ffcbf55447291a8434a4f37255c3a6a119bc4116c75d205006aa2b070bf6be28535cf6107bead14bbf64bf9fa415346ab544bd1c15e1add7d1c6380e6b2def version.patch
-6618ceb59f1927bb82ad1a0fe0a7d4c452ced7855d8f0953556fce9154f30a4c5afbd7a2ab07fb26e6e793b07d4c8f906f8dc27c1defe0580dcf1545c80d1d60 lxc.initd"
+6618ceb59f1927bb82ad1a0fe0a7d4c452ced7855d8f0953556fce9154f30a4c5afbd7a2ab07fb26e6e793b07d4c8f906f8dc27c1defe0580dcf1545c80d1d60 lxc.initd
+0c983c82ef9168a08ab0f1f9e27d4518d8ca08ec50ebe1110398f9145c82855efc3a9841239fe2097e6f333bd7cf4cefa4ee9bcd2e41cd83f81c39750a6a45c7 CVE-2014-1334.patch
+6d1884a08586ce6d8c5b297cd44844632b0cf0296defd81dd20e563be24c1711ec46bee97ff81e33d9639c91419f547f294d1abfc6d759ff6b6c415a21c4dca8 CVE-2015-1331.patch
+2ab3474d3b1b960d98e3ca12817b3deb829b47d6282c0576acd092103ff7e576fdd189435e0fc67344931eb09c9e18f6d4818b02c5b31d20f6f371658fc13cef CVE-2015-1335.patch"
diff --git a/main/lxc/CVE-2014-1334.patch b/main/lxc/CVE-2014-1334.patch
new file mode 100644
index 0000000000..e40492fa6a
--- /dev/null
+++ b/main/lxc/CVE-2014-1334.patch
@@ -0,0 +1,183 @@
+From 94bdaef2e636351cc2966387d02e4412bda1d613 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber@ubuntu.com>
+Date: Thu, 16 Jul 2015 16:37:51 -0400
+Subject: [PATCH] Don't use the container's /proc during attach
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+A user could otherwise over-mount /proc and prevent the apparmor profile
+or selinux label from being written which combined with a modified
+/bin/sh or other commonly used binary would lead to unconfined code
+execution.
+
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1475050
+Reported-by: Roman Fiedler
+Signed-off-by: Stéphane Graber <stgraber@ubuntu.com>
+---
+ src/lxc/attach.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 93 insertions(+), 4 deletions(-)
+
+diff --git a/src/lxc/attach.c b/src/lxc/attach.c
+index ed6ea8d..16d942c 100644
+--- a/src/lxc/attach.c
++++ b/src/lxc/attach.c
+@@ -76,6 +76,82 @@
+
+ lxc_log_define(lxc_attach, lxc);
+
++int lsm_set_label_at(int procfd, int on_exec, char* lsm_label) {
++ int labelfd = -1;
++ int ret = 0;
++ const char* name;
++ char* command = NULL;
++
++ name = lsm_name();
++
++ if (strcmp(name, "nop") == 0)
++ goto out;
++
++ if (strcmp(name, "none") == 0)
++ goto out;
++
++ /* We don't support on-exec with AppArmor */
++ if (strcmp(name, "AppArmor") == 0)
++ on_exec = 0;
++
++ if (on_exec) {
++ labelfd = openat(procfd, "self/attr/exec", O_RDWR);
++ }
++ else {
++ labelfd = openat(procfd, "self/attr/current", O_RDWR);
++ }
++
++ if (labelfd < 0) {
++ SYSERROR("Unable to open LSM label");
++ ret = -1;
++ goto out;
++ }
++
++ if (strcmp(name, "AppArmor") == 0) {
++ int size;
++
++ command = malloc(strlen(lsm_label) + strlen("changeprofile ") + 1);
++ if (!command) {
++ SYSERROR("Failed to write apparmor profile");
++ ret = -1;
++ goto out;
++ }
++
++ size = sprintf(command, "changeprofile %s", lsm_label);
++ if (size < 0) {
++ SYSERROR("Failed to write apparmor profile");
++ ret = -1;
++ goto out;
++ }
++
++ if (write(labelfd, command, size + 1) < 0) {
++ SYSERROR("Unable to set LSM label");
++ ret = -1;
++ goto out;
++ }
++ }
++ else if (strcmp(name, "SELinux") == 0) {
++ if (write(labelfd, lsm_label, strlen(lsm_label) + 1) < 0) {
++ SYSERROR("Unable to set LSM label");
++ ret = -1;
++ goto out;
++ }
++ }
++ else {
++ ERROR("Unable to restore label for unknown LSM: %s", name);
++ ret = -1;
++ goto out;
++ }
++
++out:
++ free(command);
++
++ if (labelfd != -1)
++ close(labelfd);
++
++ return ret;
++}
++
+ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
+ {
+ struct lxc_proc_context_info *info = calloc(1, sizeof(*info));
+@@ -573,6 +649,7 @@ struct attach_clone_payload {
+ struct lxc_proc_context_info* init_ctx;
+ lxc_attach_exec_t exec_function;
+ void* exec_payload;
++ int procfd;
+ };
+
+ static int attach_child_main(void* data);
+@@ -625,6 +702,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
+ char* cwd;
+ char* new_cwd;
+ int ipc_sockets[2];
++ int procfd;
+ signed long personality;
+
+ if (!options)
+@@ -836,6 +914,13 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
+ rexit(-1);
+ }
+
++ procfd = open("/proc", O_DIRECTORY | O_RDONLY);
++ if (procfd < 0) {
++ SYSERROR("Unable to open /proc");
++ shutdown(ipc_sockets[1], SHUT_RDWR);
++ rexit(-1);
++ }
++
+ /* attach now, create another subprocess later, since pid namespaces
+ * only really affect the children of the current process
+ */
+@@ -863,7 +948,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
+ .options = options,
+ .init_ctx = init_ctx,
+ .exec_function = exec_function,
+- .exec_payload = exec_payload
++ .exec_payload = exec_payload,
++ .procfd = procfd
+ };
+ /* We use clone_parent here to make this subprocess a direct child of
+ * the initial process. Then this intermediate process can exit and
+@@ -901,6 +987,7 @@ static int attach_child_main(void* data)
+ {
+ struct attach_clone_payload* payload = (struct attach_clone_payload*)data;
+ int ipc_socket = payload->ipc_socket;
++ int procfd = payload->procfd;
+ lxc_attach_options_t* options = payload->options;
+ struct lxc_proc_context_info* init_ctx = payload->init_ctx;
+ #if HAVE_SYS_PERSONALITY_H
+@@ -1026,12 +1113,11 @@ static int attach_child_main(void* data)
+ close(ipc_socket);
+
+ /* set new apparmor profile/selinux context */
+- if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM)) {
++ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
+ int on_exec;
+
+ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
+- ret = lsm_process_label_set(init_ctx->lsm_label, 0, on_exec);
+- if (ret < 0) {
++ if (lsm_set_label_at(procfd, on_exec, init_ctx->lsm_label) < 0) {
+ rexit(-1);
+ }
+ }
+@@ -1082,6 +1168,9 @@ static int attach_child_main(void* data)
+ }
+ }
+
++ /* we don't need proc anymore */
++ close(procfd);
++
+ /* we're done, so we can now do whatever the user intended us to do */
+ rexit(payload->exec_function(payload->exec_payload));
+ }
+--
+1.9.1
+
diff --git a/main/lxc/CVE-2015-1331.patch b/main/lxc/CVE-2015-1331.patch
new file mode 100644
index 0000000000..d8ed818cdd
--- /dev/null
+++ b/main/lxc/CVE-2015-1331.patch
@@ -0,0 +1,100 @@
+From 9db431b17f023ec776e10c59383783f94eb18821 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn <serge.hallyn@ubuntu.com>
+Date: Fri, 3 Jul 2015 09:26:17 -0500
+Subject: [PATCH] lxclock: use /run/lxc/lock rather than /run/lock/lxc
+
+Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
+[tyhicks: Remove fallback path construction in /tmp]
+Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
+
+Origin: backport
+Bug-Ubuntu: https://launchpad.net/bugs/1470842
+---
+ src/lxc/lxclock.c | 47 ++++++++++-------------------------------------
+ src/tests/locktests.c | 2 +-
+ 2 files changed, 11 insertions(+), 38 deletions(-)
+
+diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c
+index fe13898..e9e95f7 100644
+--- a/src/lxc/lxclock.c
++++ b/src/lxc/lxclock.c
+@@ -103,13 +103,13 @@ static char *lxclock_name(const char *p, const char *n)
+ char *rundir;
+
+ /* lockfile will be:
+- * "/run" + "/lock/lxc/$lxcpath/$lxcname + '\0' if root
++ * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
+ * or
+- * $XDG_RUNTIME_DIR + "/lock/lxc/$lxcpath/$lxcname + '\0' if non-root
++ * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
+ */
+
+- /* length of "/lock/lxc/" + $lxcpath + "/" + $lxcname + '\0' */
+- len = strlen("/lock/lxc/") + strlen(n) + strlen(p) + 2;
++ /* length of "/lxc/lock/" + $lxcpath + "/" + $lxcname + '\0' */
++ len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 2;
+ rundir = get_rundir();
+ if (!rundir)
+ return NULL;
+@@ -120,7 +120,7 @@ static char *lxclock_name(const char *p, const char *n)
+ return NULL;
+ }
+
+- ret = snprintf(dest, len, "%s/lock/lxc/%s", rundir, p);
++ ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
+ if (ret < 0 || ret >= len) {
+ free(dest);
+ free(rundir);
+@@ -128,31 +128,13 @@ static char *lxclock_name(const char *p, const char *n)
+ }
+ ret = mkdir_p(dest, 0755);
+ if (ret < 0) {
+- /* fall back to "/tmp/" $(id -u) "/lxc/" $lxcpath / $lxcname + '\0' */
+- int l2 = 33 + strlen(n) + strlen(p);
+- if (l2 > len) {
+- char *d;
+- d = realloc(dest, l2);
+- if (!d) {
+- free(dest);
+- free(rundir);
+- return NULL;
+- }
+- len = l2;
+- dest = d;
+- }
+- ret = snprintf(dest, len, "/tmp/%d/lxc/%s", geteuid(), p);
+- if (ret < 0 || ret >= len) {
+- free(dest);
+- free(rundir);
+- return NULL;
+- }
+- ret = snprintf(dest, len, "/tmp/%d/lxc/%s/%s", geteuid(), p, n);
+- } else
+- ret = snprintf(dest, len, "%s/lock/lxc/%s/%s", rundir, p, n);
++ free(dest);
++ free(rundir);
++ return NULL;
++ }
+
++ ret = snprintf(dest, len, "%s/lxc/lock/%s/%s", rundir, p, n);
+ free(rundir);
+-
+ if (ret < 0 || ret >= len) {
+ free(dest);
+ return NULL;
+diff --git a/src/tests/locktests.c b/src/tests/locktests.c
+index dd3393a..233ca12 100644
+--- a/src/tests/locktests.c
++++ b/src/tests/locktests.c
+@@ -122,7 +122,7 @@ int main(int argc, char *argv[])
+ exit(1);
+ }
+ struct stat sb;
+- char *pathname = RUNTIME_PATH "/lock/lxc/var/lib/lxc/";
++ char *pathname = RUNTIME_PATH "/lxc/lock/var/lib/lxc/";
+ ret = stat(pathname, &sb);
+ if (ret != 0) {
+ fprintf(stderr, "%d: filename %s not created\n", __LINE__,
+--
+2.1.4
+
diff --git a/main/lxc/CVE-2015-1335.patch b/main/lxc/CVE-2015-1335.patch
new file mode 100644
index 0000000000..6e97e12d58
--- /dev/null
+++ b/main/lxc/CVE-2015-1335.patch
@@ -0,0 +1,475 @@
+From 3c62cae308e8e66dcc616c5bd34671e1d2eea5a6 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn <serge.hallyn@ubuntu.com>
+Date: Mon, 31 Aug 2015 12:57:20 -0500
+Subject: [PATCH 1/1] Protect container mounts against symlinks
+
+When a container starts up, lxc sets up the container's inital fstree
+by doing a bunch of mounting, guided by the container configuration
+file. The container config is owned by the admin or user on the host,
+so we do not try to guard against bad entries. However, since the
+mount target is in the container, it's possible that the container admin
+could divert the mount with symbolic links. This could bypass proper
+container startup (i.e. confinement of a root-owned container by the
+restrictive apparmor policy, by diverting the required write to
+/proc/self/attr/current), or bypass the (path-based) apparmor policy
+by diverting, say, /proc to /mnt in the container.
+
+To prevent this,
+
+1. do not allow mounts to paths containing symbolic links
+
+2. do not allow bind mounts from relative paths containing symbolic
+links.
+
+Details:
+
+This patch causes lxc to check /proc/self/mountinfo after each
+mount into a container rootfs (that is, where we are not chrooted
+into the container), making sure that the mount target wasn't a
+symlink.
+
+Use safe_mount() in mount_entry(), when mounting container proc,
+and when needed. In particular, safe_mount() need not be used in
+any case where:
+
+1. the mount is done in the container's namespace
+2. the mount is for the container's rootfs
+3. the mount is relative to a tmpfs or proc/sysfs which we have
+ just safe_mount()ed ourselves
+
+Since we were using proc/net as a temporary placeholder for /proc/sys/net
+during container startup, and proc/net is a symbolic link, use proc/tty
+instead.
+
+Update the lxc.container.conf manpage with details about the new
+restrictions.
+
+Finally, add a testcase to test some symbolic link possibilities.
+
+lxc-test-symlink: background lxc-start
+
+CVE-2015-1335
+
+Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
+---
+ doc/lxc.container.conf.sgml.in | 12 ++++++
+ src/lxc/cgfs.c | 5 ++-
+ src/lxc/cgmanager.c | 4 +-
+ src/lxc/conf.c | 30 +++++++-------
+ src/lxc/utils.c | 90 ++++++++++++++++++++++++++++++++++++++++++
+ src/lxc/utils.h | 2 +
+ src/tests/Makefile.am | 3 +-
+ src/tests/lxc-test-symlink | 88 +++++++++++++++++++++++++++++++++++++++++
+ 8 files changed, 216 insertions(+), 18 deletions(-)
+ create mode 100644 src/tests/lxc-test-symlink
+
+diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
+index 9cd0c57..6f06762 100644
+--- a/doc/lxc.container.conf.sgml.in
++++ b/doc/lxc.container.conf.sgml.in
+@@ -676,6 +676,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ container. This is useful to mount /etc, /var or /home for
+ examples.
+ </para>
++ <para>
++ NOTE - LXC will generally ensure that mount targets and relative
++ bind-mount sources are properly confined under the container
++ root, to avoid attacks involving over-mounting host directories
++ and files. (Symbolic links in absolute mount sources are ignored)
++ However, if the container configuration first mounts a directory which
++ is under the control of the container user, such as /home/joe, into
++ the container at some <filename>path</filename>, and then mounts
++ under <filename>path</filename>, then a TOCTTOU attack would be
++ possible where the container user modifies a symbolic link under
++ his home directory at just the right time.
++ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+diff --git a/src/lxc/cgfs.c b/src/lxc/cgfs.c
+index 15346dc..df4ad46 100644
+--- a/src/lxc/cgfs.c
++++ b/src/lxc/cgfs.c
+@@ -1363,7 +1363,10 @@ static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type)
+ if (!path)
+ return false;
+ snprintf(path, bufsz, "%s/sys/fs/cgroup", root);
+- r = mount("cgroup_root", path, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME, "size=10240k,mode=755");
++ r = safe_mount("cgroup_root", path, "tmpfs",
++ MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
++ "size=10240k,mode=755",
++ root);
+ if (r < 0) {
+ SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container");
+ return false;
+diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
+index 1872f03..79af751 100644
+--- a/src/lxc/cgmanager.c
++++ b/src/lxc/cgmanager.c
+@@ -1332,7 +1332,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname)
+ }
+
+ /* mount a tmpfs there so we can create subdirs */
+- if (mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755")) {
++ if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) {
+ SYSERROR("Failed to mount tmpfs at %s", cgpath);
+ return false;
+ }
+@@ -1343,7 +1343,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname)
+ return false;
+ }
+
+- if (mount(dirname, cgpath, "none", MS_BIND, 0)) {
++ if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) {
+ SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
+ return false;
+ }
+diff --git a/src/lxc/conf.c b/src/lxc/conf.c
+index 320b6c9..8acee5a 100644
+--- a/src/lxc/conf.c
++++ b/src/lxc/conf.c
+@@ -795,7 +795,7 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
+ }
+ mflags = add_required_remount_flags(source, destination,
+ default_mounts[i].flags);
+- r = mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options);
++ r = safe_mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options, conf->rootfs.path ? conf->rootfs.mount : NULL);
+ saved_errno = errno;
+ if (r < 0)
+ SYSERROR("error mounting %s on %s flags %lu", source, destination, mflags);
+@@ -989,7 +989,8 @@ static int setup_tty(const struct lxc_rootfs *rootfs,
+ return -1;
+ }
+
+- if (mount(pty_info->name, lxcpath, "none", MS_BIND, 0)) {
++ if (safe_mount(pty_info->name, lxcpath, "none", MS_BIND, 0,
++ rootfs->mount)) {
+ WARN("failed to mount '%s'->'%s'",
+ pty_info->name, path);
+ continue;
+@@ -1016,7 +1017,8 @@ static int setup_tty(const struct lxc_rootfs *rootfs,
+ close(ret);
+ }
+ }
+- if (mount(pty_info->name, path, "none", MS_BIND, 0)) {
++ if (safe_mount(pty_info->name, path, "none", MS_BIND, 0,
++ rootfs->mount)) {
+ WARN("failed to mount '%s'->'%s'",
+ pty_info->name, path);
+ continue;
+@@ -1442,16 +1444,16 @@ static int mount_autodev(const char *name, char *root, const char *lxcpath)
+ SYSERROR("WARNING: Failed to create symlink '%s'->'%s'", host_path, devtmpfs_path);
+ }
+ DEBUG("Bind mounting %s to %s", devtmpfs_path , path );
+- ret = mount(devtmpfs_path, path, NULL, MS_BIND, 0 );
++ ret = safe_mount(devtmpfs_path, path, NULL, MS_BIND, 0, root );
+ } else {
+ /* Only mount a tmpfs on here if we don't already a mount */
+ if ( ! mount_check_fs( host_path, NULL ) ) {
+ DEBUG("Mounting tmpfs to %s", host_path );
+- ret = mount("none", path, "tmpfs", 0, "size=100000,mode=755");
++ ret = safe_mount("none", path, "tmpfs", 0, "size=100000,mode=755", root);
+ } else {
+ /* This allows someone to manually set up a mount */
+ DEBUG("Bind mounting %s to %s", host_path, path );
+- ret = mount(host_path , path, NULL, MS_BIND, 0 );
++ ret = safe_mount(host_path , path, NULL, MS_BIND, 0, root );
+ }
+ }
+ if (ret) {
+@@ -1828,7 +1830,7 @@ static int setup_dev_console(const struct lxc_rootfs *rootfs,
+ return -1;
+ }
+
+- if (mount(console->name, path, "none", MS_BIND, 0)) {
++ if (safe_mount(console->name, path, "none", MS_BIND, 0, rootfs->mount)) {
+ ERROR("failed to mount '%s' on '%s'", console->name, path);
+ return -1;
+ }
+@@ -1883,7 +1885,7 @@ static int setup_ttydir_console(const struct lxc_rootfs *rootfs,
+ return 0;
+ }
+
+- if (mount(console->name, lxcpath, "none", MS_BIND, 0)) {
++ if (safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs->mount)) {
+ ERROR("failed to mount '%s' on '%s'", console->name, lxcpath);
+ return -1;
+ }
+@@ -2033,13 +2035,13 @@ static char *get_field(char *src, int nfields)
+
+ static int mount_entry(const char *fsname, const char *target,
+ const char *fstype, unsigned long mountflags,
+- const char *data, int optional)
++ const char *data, int optional, const char *rootfs)
+ {
+ #ifdef HAVE_STATVFS
+ struct statvfs sb;
+ #endif
+
+- if (mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data)) {
++ if (safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs)) {
+ if (optional) {
+ INFO("failed to mount '%s' on '%s' (optional): %s", fsname,
+ target, strerror(errno));
+@@ -2172,7 +2174,7 @@ static inline int mount_entry_on_systemfs(struct mntent *mntent)
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, mntent->mnt_dir,
+- mntent->mnt_type, mntflags, mntdata, optional);
++ mntent->mnt_type, mntflags, mntdata, optional, NULL);
+
+ free(pathdirname);
+ free(mntdata);
+@@ -2259,7 +2261,7 @@ skipabs:
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type,
+- mntflags, mntdata, optional);
++ mntflags, mntdata, optional, rootfs->mount);
+
+ free(mntdata);
+
+@@ -2315,7 +2317,7 @@ static int mount_entry_on_relative_rootfs(struct mntent *mntent,
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type,
+- mntflags, mntdata, optional);
++ mntflags, mntdata, optional, rootfs);
+
+ free(pathdirname);
+ free(mntdata);
+@@ -3981,7 +3983,7 @@ static int do_tmp_proc_mount(const char *rootfs)
+ return 0;
+
+ domount:
+- if (mount("proc", path, "proc", 0, NULL))
++ if (safe_mount("proc", path, "proc", 0, NULL, rootfs))
+ return -1;
+ INFO("Mounted /proc in container for security transition");
+ return 1;
+diff --git a/src/lxc/utils.c b/src/lxc/utils.c
+index 5ef04fc..f84af33 100644
+--- a/src/lxc/utils.c
++++ b/src/lxc/utils.c
+@@ -1322,3 +1322,93 @@ next_loop:
+ free(path);
+ return NULL;
+ }
++
++/*
++ * ws points into an array of \0-separate path elements.
++ * ws should be pointing to one of the path elements or
++ * the next \0. It will return the first character of the
++ * next path element.
++ */
++static char *next_word(char *ws) {
++ while (*ws && *ws != ' ') ws++;
++ while (*ws && *ws == ' ') ws++;
++ return ws;
++}
++
++/*
++ * This is only used during container startup. So we know we won't race
++ * with anyone else mounting. Check the last line in /proc/self/mountinfo
++ * to make sure the target is under the container root.
++ */
++static bool ensure_not_symlink(const char *target, const char *croot)
++{
++ FILE *f = fopen("/proc/self/mountinfo", "r");
++ char *line = NULL, *ws = NULL, *we = NULL;
++ size_t len = 0, i;
++ bool ret = false;
++
++ if (!croot || croot[0] == '\0')
++ return true;
++
++ if (!f) {
++ ERROR("Cannot open /proc/self/mountinfo");
++ return false;
++ }
++
++ while (getline(&line, &len, f) != -1) {
++ }
++ fclose(f);
++
++ if (!line)
++ return false;
++ ws = line;
++ for (i = 0; i < 4; i++)
++ ws = next_word(ws);
++ if (!*ws)
++ goto out;
++ we = ws;
++ while (*we && *we != ' ')
++ we++;
++ if (!*we)
++ goto out;
++ *we = '\0';
++
++ /* now make sure that ws starts with croot and ends with rest of target */
++ if (croot && strncmp(ws, croot, strlen(croot)) != 0) {
++ ERROR("Mount onto %s resulted in %s\n", target, ws);
++ goto out;
++ }
++
++ size_t start = croot ? strlen(croot) : 0;
++ if (strcmp(ws + start, target + start) != 0) {
++ ERROR("Mount onto %s resulted in %s\n", target, ws);
++ goto out;
++ }
++
++ ret = true;
++
++out:
++ free(line);
++ return ret;
++}
++/*
++ * Safely mount a path into a container, ensuring that the mount target
++ * is under the container's @rootfs. (If @rootfs is NULL, then the container
++ * uses the host's /)
++ */
++int safe_mount(const char *src, const char *dest, const char *fstype,
++ unsigned long flags, const void *data, const char *rootfs)
++{
++ int ret;
++ ret = mount(src, dest, fstype, flags, data);
++ if (ret < 0) {
++ SYSERROR("Mount of '%s' onto '%s' failed", src, dest);
++ return ret;
++ }
++ if (!ensure_not_symlink(dest, rootfs)) {
++ ERROR("Mount of '%s' onto '%s' was onto a symlink!", src, dest);
++ umount(dest);
++ return -1;
++ }
++ return 0;
++}
+diff --git a/src/lxc/utils.h b/src/lxc/utils.h
+index f48f403..30e8a98 100644
+--- a/src/lxc/utils.h
++++ b/src/lxc/utils.h
+@@ -280,3 +280,5 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval);
+ int detect_shared_rootfs(void);
+ int detect_ramfs_rootfs(void);
+ char *on_path(char *cmd);
++int safe_mount(const char *src, const char *dest, const char *fstype,
++ unsigned long flags, const void *data, const char *rootfs);
+diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
+index 19a4205..edb62d6 100644
+--- a/src/tests/Makefile.am
++++ b/src/tests/Makefile.am
+@@ -48,7 +48,7 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
+ lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
+ lxc-test-apparmor
+
+-bin_SCRIPTS = lxc-test-autostart
++bin_SCRIPTS = lxc-test-autostart lxc-test-symlink
+
+ if DISTRO_UBUNTU
+ bin_SCRIPTS += lxc-test-usernic lxc-test-ubuntu lxc-test-unpriv
+@@ -71,6 +71,7 @@ EXTRA_DIST = \
+ locktests.c \
+ lxcpath.c \
+ lxc-test-autostart \
++ lxc-test-symlink \
+ lxc-test-ubuntu \
+ lxc-test-unpriv \
+ may_control.c \
+diff --git a/src/tests/lxc-test-symlink b/src/tests/lxc-test-symlink
+new file mode 100644
+index 0000000..b014a66
+--- /dev/null
++++ b/src/tests/lxc-test-symlink
+@@ -0,0 +1,88 @@
++#!/bin/bash
++
++set -ex
++
++# lxc: linux Container library
++
++# Authors:
++# Serge Hallyn <serge.hallyn@ubuntu.com>
++#
++# This is a regression test for symbolic links
++
++dirname=`mktemp -d`
++fname=`mktemp`
++fname2=`mktemp`
++
++lxcpath=/var/lib/lxcsym1
++
++cleanup() {
++ lxc-destroy -P $lxcpath -f -n symtest1 || true
++ rm -f $lxcpath
++ rmdir $dirname || true
++ rm -f $fname || true
++ rm -f $fname2 || true
++}
++
++trap cleanup EXIT SIGHUP SIGINT SIGTERM
++
++testrun() {
++ expected=$1
++ run=$2
++ pass="pass"
++ lxc-start -d -P $lxcpath -n symtest1 -l trace -o $lxcpath/log || pass="fail"
++ [ $pass = "pass" ] && lxc-wait -P $lxcpath -n symtest1 -t 10 -s RUNNING || pass="fail"
++ if [ "$pass" != "$expected" ]; then
++ echo "Test $run: expected $expected but container did not. Start log:"
++ cat $lxcpath/log
++ echo "FAIL: Test $run: expected $expected but container did not."
++ false
++ fi
++ lxc-stop -P $lxcpath -n symtest1 -k || true
++}
++
++# make lxcpath a symlink - this should NOT cause failure
++ln -s /var/lib/lxc $lxcpath
++
++lxc-destroy -P $lxcpath -f -n symtest1 || true
++lxc-create -P $lxcpath -t busybox -n symtest1
++
++cat >> /var/lib/lxc/symtest1/config << EOF
++lxc.mount.entry = $dirname opt/xxx/dir none bind,create=dir
++lxc.mount.entry = $fname opt/xxx/file none bind,create=file
++lxc.mount.entry = $fname2 opt/xxx/file2 none bind
++EOF
++
++# Regular - should succeed
++mkdir -p /var/lib/lxc/symtest1/rootfs/opt/xxx
++touch /var/lib/lxc/symtest1/rootfs/opt/xxx/file2
++testrun pass 1
++
++# symlink - should fail
++rm -rf /var/lib/lxc/symtest1/rootfs/opt/xxx
++mkdir -p /var/lib/lxc/symtest1/rootfs/opt/xxx2
++ln -s /var/lib/lxc/symtest1/rootfs/opt/xxx2 /var/lib/lxc/symtest1/rootfs/opt/xxx
++touch /var/lib/lxc/symtest1/rootfs/opt/xxx/file2
++testrun fail 2
++
++# final final symlink - should fail
++rm -rf $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx/dir
++touch $lxcpath/symtest1/rootfs/opt/xxx/file
++touch $lxcpath/symtest1/rootfs/opt/xxx/file2src
++ln -s $lxcpath/symtest1/rootfs/opt/xxx/file2src $lxcpath/symtest1/rootfs/opt/xxx/file2
++testrun fail 3
++
++# Ideally we'd also try a loop device, but that won't work in nested containers
++# anyway - TODO
++
++# what about /proc itself
++
++rm -rf $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx
++touch $lxcpath/symtest1/rootfs/opt/xxx/file2
++mv $lxcpath/symtest1/rootfs/proc $lxcpath/symtest1/rootfs/proc1
++ln -s $lxcpath/symtest1/rootfs/proc1 $lxcpath/symtest1/rootfs/proc
++testrun fail 4
++
++echo "all tests passed"
+--
+2.5.0
+