[CRIU] [PATCH 1/2] userns: set uid and gid before entering into userns

Andrey Vagin avagin at openvz.org
Wed Feb 17 15:07:13 PST 2016


From: Andrew Vagin <avagin at virtuozzo.com>

> 21.01.2016 02:56, Jann Horn writes:
> Call chain:
>
> cr_dump_tasks -> collect_namespaces(true) ->
> collect_user_namespaces(true) -> walk_namespaces -> collect_user_ns
> -> dump_user_ns -> check_user_ns
>
> This method enters a user namespace with unknown owner with
> euid==(kuid 0). Linux does not guarantee that this is safe; with
> the current upstream kernel, the namespace owner can attach to the
> CRIU process via ptrace and use it to write into /etc/shadow or
> whatever.

Cc: Jann Horn <jann at thejh.net>
Reported-by: Jann Horn <jann at thejh.net>
Signed-off-by: Andrew Vagin <avagin at virtuozzo.com>
---
 criu/namespaces.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 78 insertions(+), 3 deletions(-)

diff --git a/criu/namespaces.c b/criu/namespaces.c
index 2d24674..eadabdb 100644
--- a/criu/namespaces.c
+++ b/criu/namespaces.c
@@ -9,6 +9,7 @@
 #include <stdarg.h>
 #include <signal.h>
 #include <sched.h>
+#include <sys/capability.h>
 
 #include "util.h"
 #include "imgset.h"
@@ -510,6 +511,34 @@ static unsigned int userns_id(unsigned int id, UidGidExtent **map, int n)
 	return INVALID_ID;
 }
 
+static unsigned int host_id(unsigned int id, UidGidExtent **map, int n)
+{
+	int i;
+
+	if (!(root_ns_mask & CLONE_NEWUSER))
+		return id;
+
+	for (i = 0; i < n; i++) {
+		if (map[i]->first <= id &&
+		    map[i]->first + map[i]->count > id)
+			return map[i]->lower_first + (id - map[i]->first);
+	}
+
+	return INVALID_ID;
+}
+
+static uid_t host_uid(uid_t uid)
+{
+	UsernsEntry *e = &userns_entry;
+	return host_id(uid, e->uid_map, e->n_uid_map);
+}
+
+static gid_t host_gid(gid_t gid)
+{
+	UsernsEntry *e = &userns_entry;
+	return host_id(gid, e->gid_map, e->n_gid_map);
+}
+
 uid_t userns_uid(uid_t uid)
 {
 	UsernsEntry *e = &userns_entry;
@@ -622,6 +651,52 @@ static int check_user_ns(int pid)
 	}
 
 	if (chld == 0) {
+		struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
+		struct __user_cap_header_struct hdr;
+		uid_t uid;
+		gid_t gid;
+
+		uid = host_uid(0);
+		gid = host_gid(0);
+		if (uid == INVALID_ID || gid == INVALID_ID) {
+			pr_err("Unable to convert uid or gid\n");
+			return -1;
+		}
+
+		if (prctl(PR_SET_KEEPCAPS, 1)) {
+			pr_perror("Unable to set PR_SET_KEEPCAPS");
+			return -1;
+		}
+
+		if (setresgid(gid, gid, gid)) {
+			pr_perror("Unable to set group ID");
+			return -1;
+		}
+
+		if (setgroups(0, NULL) < 0) {
+			pr_perror("Unable to drop supplementary groups\n");
+			return -1;
+		}
+
+		if (setresuid(uid, uid, uid)) {
+			pr_perror("Unable to set user ID");
+			return -1;
+		}
+
+		hdr.version = _LINUX_CAPABILITY_VERSION_3;
+		hdr.pid = 0;
+
+		if (capget(&hdr, data) < 0) {
+			pr_perror("capget");
+			return -1;
+		}
+		data[0].effective = data[0].permitted;
+		data[1].effective = data[1].permitted;
+		if (capset(&hdr, data) < 0) {
+			pr_perror("capset");
+			return -1;
+		}
+
 		/*
 		 * Check that we are able to enter into other namespaces
 		 * from the target userns namespace. This signs that these
@@ -665,9 +740,6 @@ int dump_user_ns(pid_t pid, int ns_id)
 	UsernsEntry *e = &userns_entry;
 	struct cr_img *img;
 
-	if (check_user_ns(pid))
-		return -1;
-
 	ret = parse_id_map(pid, "uid_map", &e->uid_map);
 	if (ret < 0)
 		goto err;
@@ -678,6 +750,9 @@ int dump_user_ns(pid_t pid, int ns_id)
 		goto err;
 	e->n_gid_map = ret;
 
+	if (check_user_ns(pid))
+		return -1;
+
 	img = open_image(CR_FD_USERNS, O_DUMP, ns_id);
 	if (!img)
 		goto err;
-- 
2.5.0



More information about the CRIU mailing list