From cf3c9ccc091c41af48139b7586993cc4e45c1b61 Mon Sep 17 00:00:00 2001 From: Thomas Moschny Date: Sun, 2 Aug 2015 15:11:36 +0200 Subject: [PATCH] Add security fixes, see rhbz#1245939 and rhbz#1245941. --- lxc-1.1.2-fix-CVE-2015-1331.patch | 113 ++++++++++++++++++ lxc-1.1.2-fix-CVE-2015-1334.patch | 184 ++++++++++++++++++++++++++++++ lxc.spec | 11 +- 3 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 lxc-1.1.2-fix-CVE-2015-1331.patch create mode 100644 lxc-1.1.2-fix-CVE-2015-1334.patch diff --git a/lxc-1.1.2-fix-CVE-2015-1331.patch b/lxc-1.1.2-fix-CVE-2015-1331.patch new file mode 100644 index 0000000..a963b64 --- /dev/null +++ b/lxc-1.1.2-fix-CVE-2015-1331.patch @@ -0,0 +1,113 @@ +From f50da74a71f2c33f869e6da15f131bf5c9174c12 Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Fri, 3 Jul 2015 09:26:17 -0500 +Subject: [PATCH 1/2] CVE-2015-1331: lxclock: use /run/lxc/lock rather than + /run/lock/lxc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This prevents an unprivileged user to use LXC to create arbitrary file +on the filesystem. + +Signed-off-by: Serge Hallyn +Signed-off-by: Tyler Hicks +Acked-by: Stéphane Graber +--- + 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) + 3; ++ /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */ ++ len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 3; + 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,40 +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' +- * * maximum length of $(id -u) is 10 calculated by (log (2 ** (sizeof(uid_t) * 8) - 1) / log 10 + 1) +- * * lxcpath always starts with '/' +- */ +- int l2 = 22 + 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 = mkdir_p(dest, 0755); +- if (ret < 0) { +- 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.4.3 + diff --git a/lxc-1.1.2-fix-CVE-2015-1334.patch b/lxc-1.1.2-fix-CVE-2015-1334.patch new file mode 100644 index 0000000..0f69d87 --- /dev/null +++ b/lxc-1.1.2-fix-CVE-2015-1334.patch @@ -0,0 +1,184 @@ +From ef62305193a5bb7ec00ccf00451be4ff0efac3ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Thu, 16 Jul 2015 16:37:51 -0400 +Subject: [PATCH 2/2] CVE-2015-1334: 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. + +Reported-by: Roman Fiedler +Signed-off-by: Stéphane Graber +--- + src/lxc/attach.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 93 insertions(+), 5 deletions(-) + +diff --git a/src/lxc/attach.c b/src/lxc/attach.c +index 69dafd4..436ae7a 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)); +@@ -570,6 +646,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); +@@ -622,6 +699,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) +@@ -833,6 +911,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 + */ +@@ -860,7 +945,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 +@@ -898,6 +984,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 +@@ -1038,13 +1125,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, +- init_ctx->container->lxc_conf, 0, on_exec); +- if (ret < 0) { ++ if (lsm_set_label_at(procfd, on_exec, init_ctx->lsm_label) < 0) { + rexit(-1); + } + } +@@ -1095,6 +1180,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)); + } +-- +2.4.3 + diff --git a/lxc.spec b/lxc.spec index 97e5856..5df3757 100644 --- a/lxc.spec +++ b/lxc.spec @@ -31,7 +31,7 @@ Name: lxc Version: 1.1.2 -Release: %{?prerel:0.}1%{?prerel:.%{prerel}}%{?dist}.1 +Release: %{?prerel:0.}2%{?prerel:.%{prerel}}%{?dist} Summary: Linux Resource Containers Group: Applications/System License: LGPLv2+ and GPLv2 @@ -43,6 +43,10 @@ Source0: http://linuxcontainers.org/downloads/%{name}-%{version}.tar.gz %endif Patch0: lxc-1.1.0-fix-init.patch Patch1: lxc-1.1.2-fix-lua-compat.patch +# upstream commit 61ecf69 +Patch2: lxc-1.1.2-fix-CVE-2015-1331.patch +# based on upstream commit 659e807 +Patch3: lxc-1.1.2-fix-CVE-2015-1334.patch BuildRequires: docbook-utils BuildRequires: docbook2X BuildRequires: doxygen @@ -183,6 +187,8 @@ This package contains documentation for %{name}. %setup -q -n %{name}-%{?!prerel:%{version}}%{?prerel:%{commit}} %patch0 -p1 %patch1 -p1 +%patch2 -p1 +%patch3 -p1 %build @@ -383,6 +389,9 @@ fi %changelog +* Sun Aug 2 2015 Thomas Moschny - 1.1.2-2 +- Add security fixes, see rhbz#1245939 and rhbz#1245941. + * Wed Jun 17 2015 Fedora Release Engineering - 1.1.2-1.1 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild