[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