diff options
author | Eivind Uggedal <eivind@uggedal.com> | 2015-09-30 17:47:51 +0000 |
---|---|---|
committer | Eivind Uggedal <eivind@uggedal.com> | 2015-09-30 17:50:31 +0000 |
commit | b9333a8dc06ac07169fc3199b93eee3aa48dcf93 (patch) | |
tree | 64fa462372e09f0d996bf00ef1ce35d5958a8313 | |
parent | 09295609534a7bcaff507fbf6199bbe95bdf285e (diff) | |
download | aports-b9333a8dc06ac07169fc3199b93eee3aa48dcf93.tar.bz2 aports-b9333a8dc06ac07169fc3199b93eee3aa48dcf93.tar.xz |
-rw-r--r-- | main/lxc/APKBUILD | 21 | ||||
-rw-r--r-- | main/lxc/CVE-2014-1334.patch | 183 | ||||
-rw-r--r-- | main/lxc/CVE-2015-1331.patch | 100 | ||||
-rw-r--r-- | main/lxc/CVE-2015-1335.patch | 475 |
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 + |