Unverified Commit 7e678d3d by Christian Brauner Committed by Stéphane Graber

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/1654676Reported-by: 's avatarJann Horn <jannh@google.com> Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent ce4f39c9
...@@ -50,6 +50,14 @@ ...@@ -50,6 +50,14 @@
#include "utils.h" #include "utils.h"
#include "network.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) static void usage(char *me, bool fail)
{ {
fprintf(stderr, "Usage: %s pid type bridge nicname\n", me); fprintf(stderr, "Usage: %s pid type bridge nicname\n", me);
...@@ -474,68 +482,122 @@ again: ...@@ -474,68 +482,122 @@ again:
static int rename_in_ns(int pid, char *oldname, char **newnamep) static int rename_in_ns(int pid, char *oldname, char **newnamep)
{ {
char nspath[MAXPATHLEN]; char nspath[MAXPATHLEN];
uid_t ruid, suid, euid;
int fret = -1;
int fd = -1, ofd = -1, ret, ifindex = -1; int fd = -1, ofd = -1, ret, ifindex = -1;
bool grab_newname = false; bool grab_newname = false;
ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", getpid()); ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", getpid());
if (ret < 0 || ret >= MAXPATHLEN) if (ret < 0 || ret >= MAXPATHLEN)
return -1; goto do_partial_cleanup;
if ((ofd = open(nspath, O_RDONLY)) < 0) { if ((ofd = open(nspath, O_RDONLY)) < 0) {
fprintf(stderr, "Opening %s\n", nspath); usernic_error("Failed opening network namespace path for '%d'.", getpid());
return -1; goto do_partial_cleanup;
} }
ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", pid); ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", pid);
if (ret < 0 || ret >= MAXPATHLEN) if (ret < 0 || ret >= MAXPATHLEN)
goto out_err; goto do_partial_cleanup;
if ((fd = open(nspath, O_RDONLY)) < 0) { if ((fd = open(nspath, O_RDONLY)) < 0) {
fprintf(stderr, "Opening %s\n", nspath); usernic_error("Failed opening network namespace path for '%d'.", pid);
goto out_err; goto do_partial_cleanup;
} }
if (setns(fd, 0) < 0) {
fprintf(stderr, "setns to container network namespace\n"); ret = getresuid(&ruid, &euid, &suid);
goto out_err; 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;
} }
close(fd); fd = -1;
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;
}
if (!*newnamep) { if (!*newnamep) {
grab_newname = true; grab_newname = true;
*newnamep = VETH_DEF_NAME; *newnamep = VETH_DEF_NAME;
if (!(ifindex = if_nametoindex(oldname))) {
fprintf(stderr, "failed to get netdev index\n"); ifindex = if_nametoindex(oldname);
goto out_err; 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); ret = lxc_netdev_rename_by_name(oldname, *newnamep);
goto out_err; 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) { if (grab_newname) {
char ifname[IFNAMSIZ], *namep = ifname; char ifname[IFNAMSIZ];
char *namep = ifname;
if (!if_indextoname(ifindex, namep)) { if (!if_indextoname(ifindex, namep)) {
fprintf(stderr, "Failed to get new netdev name\n"); usernic_error("Failed to get new netdev name: %s.\n",
goto out_err; strerror(errno));
goto do_full_cleanup;
} }
*newnamep = strdup(namep); *newnamep = strdup(namep);
if (!*newnamep) 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;
}
close(ofd);
return 0; fret = 0;
out_err: do_full_cleanup:
if (ofd >= 0) ret = setresuid(ruid, euid, suid);
close(ofd); if (ret < 0) {
if (setns(ofd, 0) < 0) usernic_error(
fprintf(stderr, "Error returning to original network namespace\n"); "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.
}
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;
}
do_partial_cleanup:
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
return -1; close(ofd);
return fret;
} }
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment