[1/3] unshare: Unshare user namespace

Submitted by Pavel Emelianov on May 5, 2016, 8:30 p.m.

Details

Message ID 572BAD80.8020501@virtuozzo.com
State Rejected
Series "unshare: Userns support"
Headers show

Commit Message

Pavel Emelianov May 5, 2016, 8:30 p.m.
The biggest issue here is setting up uid/gid mappings. I see 3 options
here (more ideas are welcome).

1. Let user configure one. This is the easiest case that makes
   user's life harder and can be implemented later.
2. Set up maximal mapping. Works only for root.
3. Set up 1:1 mapping with current uid/gid. Works for non-root.

So in this patch the 2 or 3 mode is selected automatically.

Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
---
 criu/crtools.c            |  2 +-
 criu/include/namespaces.h |  3 ++
 criu/namespaces.c         | 75 +++++++++++++++++++++++++++++++++++++++++++++++
 criu/pstree.c             | 14 +++++++++
 test/zdtm.py              |  2 +-
 5 files changed, 94 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/crtools.c b/criu/crtools.c
index 7a0f977..d5f90a5 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -107,7 +107,7 @@  static int parse_unshare_arg(char *opt)
 	}
 
 	/* Only pid, mnt and user for now */
-	if (opts.unshare_flags & ~(CLONE_NEWNS | CLONE_NEWPID | UNSHARE_MOUNT_PROC)) {
+	if (opts.unshare_flags & ~(CLONE_NEWNS | CLONE_NEWPID | UNSHARE_MOUNT_PROC | CLONE_NEWUSER)) {
 		pr_err("Unsharing this namespace(s) is not supported yet\n");
 		return -1;
 	}
diff --git a/criu/include/namespaces.h b/criu/include/namespaces.h
index e2b529c..13d371b 100644
--- a/criu/include/namespaces.h
+++ b/criu/include/namespaces.h
@@ -41,6 +41,9 @@ 
 /* Special flag for additional --unshare feature */
 #define UNSHARE_MOUNT_PROC	0x1
 
+/* Service flag for userns unsharing */
+#define UNSHARE_UNPRIVILEDGED	0x2
+
 #define EXTRA_SIZE	20
 
 struct ns_desc {
diff --git a/criu/namespaces.c b/criu/namespaces.c
index db8f350..37c151f 100644
--- a/criu/namespaces.c
+++ b/criu/namespaces.c
@@ -24,6 +24,7 @@ 
 #include "namespaces.h"
 #include "net.h"
 #include "cgroup.h"
+#include "syscall-types.h"
 
 #include "protobuf.h"
 #include "images/ns.pb-c.h"
@@ -1455,12 +1456,79 @@  int stop_usernsd(void)
 	return ret;
 }
 
+static int try_generate_map(pid_t pid, char *type)
+{
+	UidGidExtent ext, *ep = &ext;
+
+	if (!(opts.unshare_flags & UNSHARE_UNPRIVILEDGED)) {
+		/*
+		 * Try the maximal mapping. This should suit
+		 * the case when we restore from root.
+		 */
+
+		ext.first = ext.lower_first = 0;
+		ext.count = -1;
+		if (write_id_map(pid, &ep, 1, type) == 0) {
+			pr_info("Created root %s\n", type);
+			return 0;
+		}
+	} else {
+		/*
+		 * Try the 1:1 mapping, for user restore, but
+		 * first disable setgroups for gid mapping.
+		 */
+		if (type[0] == 'g') {
+			int fd;
+
+			fd = open_proc_rw(pid, "setgroups");
+			if (fd < 0)
+				return -1;
+
+			if (write(fd, "deny", 5) != 5) {
+				pr_perror("Can't ban setgroups");
+				close(fd);
+				return -1;
+			}
+
+			close(fd);
+
+			ext.first = ext.lower_first = getegid();
+		} else
+			ext.first = ext.lower_first = getuid();
+
+		ext.count = 1;
+		if (write_id_map(pid, &ep, 1, type) == 0) {
+			pr_info("Created user %s\n", type);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int try_generate_xid_mappings(struct pstree_item *item)
+{
+	pid_t pid = item->pid.real;
+
+	if (try_generate_map(pid, "uid_map"))
+		return -1;
+
+
+	if (try_generate_map(pid, "gid_map"))
+		return -1;
+
+	return 0;
+}
+
 int prepare_userns(struct pstree_item *item)
 {
 	struct cr_img *img;
 	UsernsEntry *e;
 	int ret;
 
+	if (opts.unshare_flags & CLONE_NEWUSER)
+		return try_generate_xid_mappings(item);
+
 	img = open_image(CR_FD_USERNS, O_RSTR, item->ids->user_ns_id);
 	if (!img)
 		return -1;
@@ -1499,6 +1567,13 @@  int collect_namespaces(bool for_dump)
 
 static int prepare_userns_creds()
 {
+	if (opts.unshare_flags & UNSHARE_UNPRIVILEDGED)
+		/*
+		 * Mappings has been set up to 1:1, so we're
+		 * already in needed state.
+		 */
+		return 0;
+
 	/* UID and GID must be set after restoring /proc/PID/{uid,gid}_maps */
 	if (setuid(0) || setgid(0) || setgroups(0, NULL)) {
 		pr_perror("Unable to initialize id-s");
diff --git a/criu/pstree.c b/criu/pstree.c
index a7d2790..6bfe5c7 100644
--- a/criu/pstree.c
+++ b/criu/pstree.c
@@ -15,6 +15,7 @@ 
 #include "asm/dump.h"
 #include "setproctitle.h"
 #include "files.h"
+#include "syscall-types.h"
 
 #include "protobuf.h"
 #include "images/pstree.pb-c.h"
@@ -958,6 +959,19 @@  static int prepare_pstree_for_unshare(void)
 		opts.unshare_flags &= ~aux;
 	}
 
+	if (opts.unshare_flags & CLONE_NEWUSER) {
+		/*
+		 * Let's try to find out whether we have enough rights
+		 * to restore caps, creds, xids and full uns mappings.
+		 *
+		 * FIXME: this should probably be done on caps basis?
+		 */
+		if (geteuid() != 0 || getegid() != 0) {
+			pr_info("Will do unpriviledged unshare\n");
+			opts.unshare_flags |= UNSHARE_UNPRIVILEDGED;
+		}
+	}
+
 	root_ns_mask |= opts.unshare_flags;
 	return 0;
 }
diff --git a/test/zdtm.py b/test/zdtm.py
index 56f069b..a6762c8 100755
--- a/test/zdtm.py
+++ b/test/zdtm.py
@@ -757,7 +757,7 @@  class criu_cli:
 			r_opts.append("net:%s" % join_ns_file)
 		if self.__unshare:
 			r_opts.append("--unshare")
-			r_opts.append("pid,mnt,proc")
+			r_opts.append("pid,user,mnt,proc")
 
 		self.__prev_dump_iter = None
 		criu_dir = os.path.dirname(os.getcwd())