parent
4f7014cf0c
commit
7a57abc43f
@ -1,185 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
@ -1 +1 @@
|
|||||||
SHA512 (lxc-2.0.7.tar.gz) = 51aedd32ae108f31dac5e8e1e71c93fa5da7813a3569f4aeee79dc254fe4a8c27221de83f645e6b334048c44953a7a7dbcde64439b9161d0cb55d8207944bb7b
|
SHA512 (lxc-2.0.8.tar.gz) = ed9fd47e92007f433695cffea659180866a5ac2778712f4eb13b6629bb18292589f5b674b734853ca196ba1f6d38bdbf3cc8a2bb28e25d3540b06b945fcf9096
|
||||||
|
Loading…
Reference in new issue