[lxc-devel] [lxd/master] nsexec: simplify attach_userns()
brauner on Github
lxc-bot at linuxcontainers.org
Mon Jun 4 22:10:49 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 421 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180604/51a7c7d1/attachment.bin>
-------------- next part --------------
From f44e73f458be9cdbdd12238f78d66ad2a9362cca Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 5 Jun 2018 00:08:41 +0200
Subject: [PATCH] nsexec: simplify attach_userns()
Avoid the costly string-parsing and readlink() calls.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
lxd/main_nsexec.go | 152 ++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 109 insertions(+), 43 deletions(-)
diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go
index 1bf446eef..a04658c56 100644
--- a/lxd/main_nsexec.go
+++ b/lxd/main_nsexec.go
@@ -29,6 +29,8 @@ package main
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
// External functions
@@ -94,50 +96,114 @@ int dosetns(int pid, char *nstype) {
return 0;
}
+static int preserve_ns(const int pid, const char *ns)
+{
+ int ret;
+// 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0
+#define __NS_PATH_LEN 50
+ char path[__NS_PATH_LEN];
+
+ // This way we can use this function to also check whether namespaces
+ // are supported by the kernel by passing in the NULL or the empty
+ // string.
+ ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
+ !ns || strcmp(ns, "") == 0 ? "" : "/",
+ !ns || strcmp(ns, "") == 0 ? "" : ns);
+ errno = EFBIG;
+ if (ret < 0 || (size_t)ret >= __NS_PATH_LEN)
+ return -EFBIG;
+
+ return open(path, O_RDONLY | O_CLOEXEC);
+}
+
+// in_same_namespace - Check whether two processes are in the same namespace.
+// @pid1 - PID of the first process.
+// @pid2 - PID of the second process.
+// @ns - Name of the namespace to check. Must correspond to one of the names
+// for the namespaces as shown in /proc/<pid/ns/
+//
+// If the two processes are not in the same namespace returns an fd to the
+// namespace of the second process identified by @pid2. If the two processes are
+// in the same namespace returns -EINVAL, -1 if an error occurred.
+static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
+{
+ int ns_fd1 = -1, ns_fd2 = -1, ret = -1;
+ struct stat ns_st1, ns_st2;
+
+ ns_fd1 = preserve_ns(pid1, ns);
+ if (ns_fd1 < 0) {
+ // The kernel does not support this namespace. This is not an
+ // error.
+ if (errno == ENOENT)
+ return -EINVAL;
+
+ goto out;
+ }
+
+ ns_fd2 = preserve_ns(pid2, ns);
+ if (ns_fd2 < 0)
+ goto out;
+
+ ret = fstat(ns_fd1, &ns_st1);
+ if (ret < 0)
+ goto out;
+
+ ret = fstat(ns_fd2, &ns_st2);
+ if (ret < 0)
+ goto out;
+
+ // processes are in the same namespace
+ ret = -EINVAL;
+ if ((ns_st1.st_dev == ns_st2.st_dev ) && (ns_st1.st_ino == ns_st2.st_ino))
+ goto out;
+
+ // processes are in different namespaces
+ ret = ns_fd2;
+ ns_fd2 = -1;
+
+out:
+
+ if (ns_fd1 >= 0)
+ close(ns_fd1);
+ if (ns_fd2 >= 0)
+ close(ns_fd2);
+
+ return ret;
+}
+
void attach_userns(int pid) {
- char nspath[PATH_MAX];
- char userns_source[22];
- char userns_target[22];
- ssize_t len = 0;
-
- sprintf(nspath, "/proc/%d/ns/user", pid);
- if (access(nspath, F_OK) == 0) {
- len = readlink("/proc/self/ns/user", userns_source, 21);
- if (len < 0) {
- fprintf(stderr, "Failed readlink of source namespace: %s\n", strerror(errno));
- _exit(1);
- }
- userns_source[len] = '\0';
-
- len = readlink(nspath, userns_target, 21);
- if (len < 0) {
- fprintf(stderr, "Failed readlink of target namespace: %s\n", strerror(errno));
- _exit(1);
- }
- userns_target[len] = '\0';
-
- if (strcmp(userns_source, userns_target) != 0) {
- if (dosetns(pid, "user") < 0) {
- fprintf(stderr, "Failed setns to container user namespace: %s\n", strerror(errno));
- _exit(1);
- }
-
- if (setgroups(0, NULL) < 0) {
- fprintf(stderr, "Failed setgroups to container root groups: %s\n", strerror(errno));
- _exit(1);
- }
-
- if (setgid(0) < 0) {
- fprintf(stderr, "Failed setgid to container root group: %s\n", strerror(errno));
- _exit(1);
- }
-
- if (setuid(0) < 0) {
- fprintf(stderr, "Failed setuid to container root user: %s\n", strerror(errno));
- _exit(1);
- }
-
- }
+ int ret, userns_fd;
+
+ userns_fd = in_same_namespace(getpid(), pid, "user");
+ if (userns_fd < 0) {
+ if (userns_fd == -EINVAL)
+ _exit(EXIT_SUCCESS);
+
+ _exit(EXIT_FAILURE);
+ }
+
+ ret = setns(userns_fd, CLONE_NEWUSER);
+ if (ret < 0) {
+ fprintf(stderr, "Failed setns to container user namespace: %s\n", strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+
+ ret = setuid(0);
+ if (ret < 0) {
+ fprintf(stderr, "Failed setuid to container root user: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ ret = setgid(0);
+ if (ret < 0) {
+ fprintf(stderr, "Failed setgid to container root group: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ ret = setgroups(0, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed setgroups to container root groups: %s\n", strerror(errno));
+ _exit(1);
}
}
More information about the lxc-devel
mailing list