From 4f7014cf0c3611eb5c01024d31bbb2b673338d35 Mon Sep 17 00:00:00 2001
From: Thomas Moschny <thm@fedoraproject.org>
Date: Thu, 9 Mar 2017 18:53:07 +0100
Subject: [PATCH] Add fix for CVE-2017-5985.

---
 lxc-2.0.7-ensure-netns-is-caller-owned.patch | 185 +++++++++++++++++++
 lxc.spec                                     |   7 +-
 2 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 lxc-2.0.7-ensure-netns-is-caller-owned.patch

diff --git a/lxc-2.0.7-ensure-netns-is-caller-owned.patch b/lxc-2.0.7-ensure-netns-is-caller-owned.patch
new file mode 100644
index 0000000..3b8a0e3
--- /dev/null
+++ b/lxc-2.0.7-ensure-netns-is-caller-owned.patch
@@ -0,0 +1,185 @@
+From d512bd5efb0e407eba350c4e649c464a65b712a3 Mon Sep 17 00:00:00 2001
+From: Christian Brauner <christian.brauner@ubuntu.com>
+Date: Sat, 28 Jan 2017 13:02:34 +0100
+Subject: [PATCH] CVE-2017-5985: Ensure target netns is caller-owned
+
+Before this commit, lxc-user-nic could potentially have been tricked into
+operating on a network namespace over which the caller did not hold privilege.
+
+This commit ensures that the caller is privileged over the network namespace by
+temporarily dropping privilege.
+
+Launchpad: https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1654676
+Reported-by: Jann Horn <jannh@google.com>
+Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
+---
+ src/lxc/lxc_user_nic.c | 119 ++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 87 insertions(+), 32 deletions(-)
+
+diff --git a/src/lxc/lxc_user_nic.c b/src/lxc/lxc_user_nic.c
+index 409a53a..96dc398 100644
+--- a/src/lxc/lxc_user_nic.c
++++ b/src/lxc/lxc_user_nic.c
+@@ -50,6 +50,14 @@
+ #include "utils.h"
+ #include "network.h"
+ 
++#define usernic_debug_stream(stream, format, ...)                              \
++	do {                                                                   \
++		fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__,     \
++			__func__, __VA_ARGS__);                                \
++	} while (false)
++
++#define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__)
++
+ static void usage(char *me, bool fail)
+ {
+ 	fprintf(stderr, "Usage: %s lxcpath name pid type bridge nicname\n", me);
+@@ -670,68 +678,115 @@ static bool create_db_dir(char *fnam)
+ }
+ 
+ #define VETH_DEF_NAME "eth%d"
+-
+ static int rename_in_ns(int pid, char *oldname, char **newnamep)
+ {
+-	int fd = -1, ofd = -1, ret, ifindex = -1;
++	uid_t ruid, suid, euid;
++	int fret = -1;
++	int fd = -1, ifindex = -1, ofd = -1, ret;
+ 	bool grab_newname = false;
+ 
+ 	ofd = lxc_preserve_ns(getpid(), "net");
+ 	if (ofd < 0) {
+-		fprintf(stderr, "Failed opening network namespace path for '%d'.", getpid());
+-		return -1;
++		usernic_error("Failed opening network namespace path for '%d'.", getpid());
++		return fret;
+ 	}
+ 
+ 	fd = lxc_preserve_ns(pid, "net");
+ 	if (fd < 0) {
+-		fprintf(stderr, "Failed opening network namespace path for '%d'.", pid);
+-		return -1;
++		usernic_error("Failed opening network namespace path for '%d'.", pid);
++		goto do_partial_cleanup;
++	}
++
++	ret = getresuid(&ruid, &euid, &suid);
++	if (ret < 0) {
++		usernic_error("Failed to retrieve real, effective, and saved "
++			      "user IDs: %s\n",
++			      strerror(errno));
++		goto do_partial_cleanup;
++	}
++
++	ret = setns(fd, CLONE_NEWNET);
++	close(fd);
++	fd = -1;
++	if (ret < 0) {
++		usernic_error("Failed to setns() to the network namespace of "
++			      "the container with PID %d: %s.\n",
++			      pid, strerror(errno));
++		goto do_partial_cleanup;
+ 	}
+ 
+-	if (setns(fd, 0) < 0) {
+-		fprintf(stderr, "setns to container network namespace\n");
+-		goto out_err;
++	ret = setresuid(ruid, ruid, 0);
++	if (ret < 0) {
++		usernic_error("Failed to drop privilege by setting effective "
++			      "user id and real user id to %d, and saved user "
++			      "ID to 0: %s.\n",
++			      ruid, strerror(errno));
++		// COMMENT(brauner): It's ok to jump to do_full_cleanup here
++		// since setresuid() will succeed when trying to set real,
++		// effective, and saved to values they currently have.
++		goto do_full_cleanup;
+ 	}
+-	close(fd); fd = -1;
++
+ 	if (!*newnamep) {
+ 		grab_newname = true;
+ 		*newnamep = VETH_DEF_NAME;
+-		if (!(ifindex = if_nametoindex(oldname))) {
+-			fprintf(stderr, "failed to get netdev index\n");
+-			goto out_err;
++
++		ifindex = if_nametoindex(oldname);
++		if (!ifindex) {
++			usernic_error("Failed to get netdev index: %s.\n", strerror(errno));
++			goto do_full_cleanup;
+ 		}
+ 	}
+-	if ((ret = lxc_netdev_rename_by_name(oldname, *newnamep)) < 0) {
+-		fprintf(stderr, "Error %d renaming netdev %s to %s in container\n", ret, oldname, *newnamep);
+-		goto out_err;
++
++	ret = lxc_netdev_rename_by_name(oldname, *newnamep);
++	if (ret < 0) {
++		usernic_error("Error %d renaming netdev %s to %s in container.\n", ret, oldname, *newnamep);
++		goto do_full_cleanup;
+ 	}
++
+ 	if (grab_newname) {
+-		char ifname[IFNAMSIZ], *namep = ifname;
++		char ifname[IFNAMSIZ];
++		char *namep = ifname;
++
+ 		if (!if_indextoname(ifindex, namep)) {
+-			fprintf(stderr, "Failed to get new netdev name\n");
+-			goto out_err;
++			usernic_error("Failed to get new netdev name: %s.\n", strerror(errno));
++			goto do_full_cleanup;
+ 		}
++
+ 		*newnamep = strdup(namep);
+ 		if (!*newnamep)
+-			goto out_err;
++			goto do_full_cleanup;
+ 	}
+-	if (setns(ofd, 0) < 0) {
+-		fprintf(stderr, "Error returning to original netns\n");
+-		close(ofd);
+-		return -1;
++
++	fret = 0;
++
++do_full_cleanup:
++	ret = setresuid(ruid, euid, suid);
++	if (ret < 0) {
++		usernic_error("Failed to restore privilege by setting effective "
++			      "user id to %d, real user id to %d, and saved user "
++			      "ID to %d: %s.\n",
++			      ruid, euid, suid, strerror(errno));
++		fret = -1;
++		// COMMENT(brauner): setns() should fail if setresuid() doesn't
++		// succeed but there's no harm in falling through; keeps the
++		// code cleaner.
+ 	}
+-	close(ofd);
+ 
+-	return 0;
++	ret = setns(ofd, CLONE_NEWNET);
++	if (ret < 0) {
++		usernic_error("Failed to setns() to original network namespace "
++			      "of PID %d: %s.\n",
++			      ofd, strerror(errno));
++		fret = -1;
++	}
+ 
+-out_err:
+-	if (ofd >= 0)
+-		close(ofd);
+-	if (setns(ofd, 0) < 0)
+-		fprintf(stderr, "Error returning to original network namespace\n");
++do_partial_cleanup:
+ 	if (fd >= 0)
+ 		close(fd);
+-	return -1;
++	close(ofd);
++
++	return fret;
+ }
+ 
+ /*
diff --git a/lxc.spec b/lxc.spec
index eb08816..75fb8f9 100644
--- a/lxc.spec
+++ b/lxc.spec
@@ -31,7 +31,7 @@
 
 Name:           lxc
 Version:        2.0.7
-Release:        %{?prerel:0.}1%{?prerel:.%{prerel}}%{?dist}.2
+Release:        %{?prerel:0.}2%{?prerel:.%{prerel}}%{?dist}
 Summary:        Linux Resource Containers
 Group:          Applications/System
 License:        LGPLv2+ and GPLv2
@@ -43,6 +43,7 @@ Source0:        http://linuxcontainers.org/downloads/%{name}-%{version}.tar.gz
 %endif
 Patch0:         lxc-2.0.7-fix-init.patch
 Patch1:         lxc-2.0.6-fix-lxc-net.patch
+Patch2:         lxc-2.0.7-ensure-netns-is-caller-owned.patch
 BuildRequires:  docbook2X
 BuildRequires:  doxygen
 BuildRequires:  kernel-headers
@@ -177,6 +178,7 @@ This package contains documentation for %{name}.
 %setup -q -n %{name}-%{?!prerel:%{version}}%{?prerel:%{commit}}
 %patch0 -p1
 %patch1 -p1
+%patch2 -p1
 
 
 %build
@@ -376,6 +378,9 @@ fi
 
 
 %changelog
+* Thu Mar  9 2017 Thomas Moschny <thomas.moschny@gmx.de> - 2.0.7-2
+- Add fix for CVE-2017-5985.
+
 * Wed Feb 15 2017 Igor Gnatenko <ignatenko@redhat.com> - 2.0.7-1.2
 - Rebuild for brp-python-bytecompile