From 08366a8597d9bd1d3cf26c91d8b156630bac21f4 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sat, 8 Aug 2015 12:30:55 -0400 Subject: [PATCH] Enter existing user namespace if present When executing an additional process in a container, all namespaces are entered but the user namespace. As a result, the process may be executed as the host's root user. This has both functionality and security implications. Fix this by adding the missing user namespace to the array of namespaces. Since joining a user namespace in which the caller is already a member yields an error, skip namespaces we're already in. Last, remove a needless and buggy AT_SYMLINK_NOFOLLOW in the code. Signed-off-by: Ido Yariv --- libcontainer/nsenter/nsexec.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c index cd02d00a..69b077bf 100644 --- a/libcontainer/nsenter/nsexec.c +++ b/libcontainer/nsenter/nsexec.c @@ -65,11 +65,11 @@ static int clone_parent(jmp_buf * env) void nsexec() { - char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" }; + char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt", "user" }; const int num = sizeof(namespaces) / sizeof(char *); jmp_buf env; char buf[PATH_MAX], *val; - int i, tfd, child, len, pipenum, consolefd = -1; + int i, tfd, self_tfd, child, len, pipenum, consolefd = -1; pid_t pid; char *console; @@ -114,17 +114,30 @@ void nsexec() exit(1); } + self_tfd = open("/proc/self/ns", O_DIRECTORY | O_RDONLY); + if (self_tfd == -1) { + pr_perror("Failed to open /proc/self/ns"); + exit(1); + } + for (i = 0; i < num; i++) { struct stat st; + struct stat self_st; int fd; /* Symlinks on all namespaces exist for dead processes, but they can't be opened */ - if (fstatat(tfd, namespaces[i], &st, AT_SYMLINK_NOFOLLOW) == -1) { + if (fstatat(tfd, namespaces[i], &st, 0) == -1) { // Ignore nonexistent namespaces. if (errno == ENOENT) continue; } + /* Skip namespaces we're already part of */ + if (fstatat(self_tfd, namespaces[i], &self_st, 0) != -1 && + st.st_ino == self_st.st_ino) { + continue; + } + fd = openat(tfd, namespaces[i], O_RDONLY); if (fd == -1) { pr_perror("Failed to open ns file %s for ns %s", buf, @@ -139,6 +152,9 @@ void nsexec() close(fd); } + close(self_tfd); + close(tfd); + if (setjmp(env) == 1) { // Child