[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