[CRIU] [PATCH 1/4] files: create links with uid and gid which are known in userns

Andrei Vagin avagin at openvz.org
Tue Dec 13 15:58:11 PST 2016


From: Andrei Vagin <avagin at virtuozzo.com>

Starting with 4.8 the kernel doesn't allow to create inodes
with a uid or gid unknown to an user namespace where a file
system was mounted.
036d523641c66 ("vfs: Don't create inodes with a uid or gid unknown to the vfs")

Signed-off-by: Andrei Vagin <avagin at virtuozzo.com>
---
 criu/files-reg.c         | 66 ++++++++++++++++++++++++++++++++++++++----------
 criu/include/files-reg.h |  3 ++-
 2 files changed, 54 insertions(+), 15 deletions(-)

diff --git a/criu/files-reg.c b/criu/files-reg.c
index 3834f38..b8339f2 100644
--- a/criu/files-reg.c
+++ b/criu/files-reg.c
@@ -10,6 +10,7 @@
 #include <sys/prctl.h>
 #include <ctype.h>
 #include <sched.h>
+#include <sys/capability.h>
 
 /* Stolen from kernel/fs/nfs/unlink.c */
 #define SILLYNAME_PREF ".nfs"
@@ -38,6 +39,7 @@
 #include "plugin.h"
 
 int setfsuid(uid_t fsuid);
+int setfsgid(gid_t fsuid);
 
 /*
  * Ghost files are those not visible from the FS. Dumping them is
@@ -293,7 +295,8 @@ static int open_remap_ghost(struct reg_file_info *rfi,
 	gf->id = rfe->remap_id;
 	gf->remap.users = 0;
 	gf->remap.is_dir = S_ISDIR(gfe->mode);
-	gf->remap.owner = gfe->uid;
+	gf->remap.uid = gfe->uid;
+	gf->remap.gid = gfe->gid;
 	list_add_tail(&gf->list, &ghost_files);
 gf_found:
 	rfi->is_dir = gf->remap.is_dir;
@@ -316,7 +319,8 @@ static int open_remap_linked(struct reg_file_info *rfi,
 	struct file_remap *rm;
 	struct file_desc *rdesc;
 	struct reg_file_info *rrfi;
-	uid_t owner = -1;
+	uid_t uid = -1;
+	gid_t gid = -1;
 
 	rdesc = find_file_desc_raw(FD_TYPES__REG, rfe->remap_id);
 	if (!rdesc) {
@@ -342,13 +346,15 @@ static int open_remap_linked(struct reg_file_info *rfi,
 			return -1;
 		}
 
-		owner = st.st_uid;
+		uid = st.st_uid;
+		gid = st.st_gid;
 	}
 
 	rm->rpath = rrfi->path;
 	rm->users = 0;
 	rm->is_dir = false;
-	rm->owner = owner;
+	rm->uid = uid;
+	rm->gid = gid;
 	rm->rmnt_id = rfi->rfe->mnt_id;
 	rfi->remap = rm;
 	return 0;
@@ -709,9 +715,11 @@ static void __rollback_link_remaps(bool do_unlink)
 
 void delete_link_remaps(void) { __rollback_link_remaps(true); }
 void free_link_remaps(void) { __rollback_link_remaps(false); }
+static int linkat_hard(int odir, char *opath, int ndir, char *npath, uid_t uid, gid_t gid, int flags);
 
 static int create_link_remap(char *path, int len, int lfd,
-				u32 *idp, struct ns_id *nsid)
+				u32 *idp, struct ns_id *nsid,
+				const struct stat *st)
 {
 	char link_name[PATH_MAX], *tmp;
 	RegFileEntry rfe = REG_FILE_ENTRY__INIT;
@@ -755,7 +763,8 @@ static int create_link_remap(char *path, int len, int lfd,
 	mntns_root = mntns_get_root_fd(nsid);
 
 again:
-	ret = linkat(lfd, "", mntns_root, link_name, AT_EMPTY_PATH);
+	ret = linkat_hard(lfd, "", mntns_root, link_name,
+				st->st_uid, st->st_gid, AT_EMPTY_PATH);
 	if (ret < 0 && errno == ENOENT) {
 		/* Use grand parent, if parent directory does not exist. */
 		if (trim_last_parent(link_name) < 0) {
@@ -780,7 +789,7 @@ static int dump_linked_remap(char *path, int len, const struct stat *ost,
 	u32 lid;
 	RemapFilePathEntry rpe = REMAP_FILE_PATH_ENTRY__INIT;
 
-	if (create_link_remap(path, len, lfd, &lid, nsid))
+	if (create_link_remap(path, len, lfd, &lid, nsid, ost))
 		return -1;
 
 	rpe.orig_id = id;
@@ -1190,16 +1199,18 @@ static void convert_path_from_another_mp(char *src, char *dst, int dlen,
 				src + off);
 }
 
-static int linkat_hard(int odir, char *opath, int ndir, char *npath, uid_t owner)
+static int linkat_hard(int odir, char *opath, int ndir, char *npath, uid_t uid, gid_t gid, int flags)
 {
-	int ret, old_fsuid = -1;
+	struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
+	struct __user_cap_header_struct hdr;
+	int ret, old_fsuid = -1, old_fsgid = -1;
 	int errno_save;
 
-	ret = linkat(odir, opath, ndir, npath, 0);
+	ret = linkat(odir, opath, ndir, npath, flags);
 	if (ret == 0)
 		return 0;
 
-	if (!( (errno == EPERM) && (root_ns_mask & CLONE_NEWUSER) )) {
+	if (!( (errno == EPERM || errno == EOVERFLOW) && (root_ns_mask & CLONE_NEWUSER) )) {
 		errno_save = errno;
 		pr_perror("Can't link %s -> %s", opath, npath);
 		errno = errno_save;
@@ -1220,16 +1231,42 @@ static int linkat_hard(int odir, char *opath, int ndir, char *npath, uid_t owner
 	 *
 	 * Fortunately, the setfsuid() requires ns-level
 	 * CAP_SETUID which we have.
+	 *
+	 * Starting with 4.8 the kernel doesn't allow to create inodes
+	 * with a uid or gid unknown to an user namespace.
+	 * 036d523641c66 ("vfs: Don't create inodes with a uid or gid unknown to the vfs")
 	 */
 
-	old_fsuid = setfsuid(owner);
+	old_fsuid = setfsuid(uid);
+	old_fsgid = setfsgid(gid);
+
+	/* AT_EMPTY_PATH requires CAP_DAC_READ_SEARCH */
+	if (flags & AT_EMPTY_PATH) {
+		hdr.version = _LINUX_CAPABILITY_VERSION_3;
+		hdr.pid = 0;
 
-	ret = linkat(odir, opath, ndir, npath, 0);
+		if (capget(&hdr, data) < 0) {
+			errno_save = errno;
+			pr_perror("capget");
+			goto out;
+		}
+		data[0].effective = data[0].permitted;
+		data[1].effective = data[1].permitted;
+		if (capset(&hdr, data) < 0) {
+			errno_save = errno;
+			pr_perror("capset");
+			goto out;
+		}
+	}
+
+	ret = linkat(odir, opath, ndir, npath, flags);
 	errno_save = errno;
 	if (ret < 0)
 		pr_perror("Can't link %s -> %s", opath, npath);
 
+out:
 	setfsuid(old_fsuid);
+	setfsgid(old_fsgid);
 	if (setfsuid(-1) != old_fsuid) {
 		pr_warn("Failed to restore old fsuid!\n");
 		/*
@@ -1380,7 +1417,8 @@ out_root:
 	if (*level < 0)
 		return -1;
 
-	if (linkat_hard(mntns_root, rpath, mntns_root, path, rfi->remap->owner) < 0) {
+	if (linkat_hard(mntns_root, rpath, mntns_root, path,
+			rfi->remap->uid, rfi->remap->gid, 0) < 0) {
 		rm_parent_dirs(mntns_root, path, *level);
 		return -1;
 	}
diff --git a/criu/include/files-reg.h b/criu/include/files-reg.h
index 7023fff..13a6fbc 100644
--- a/criu/include/files-reg.h
+++ b/criu/include/files-reg.h
@@ -14,7 +14,8 @@ struct file_remap {
 	bool is_dir;
 	int  rmnt_id;
 	unsigned int users;
-	uid_t owner;
+	uid_t uid;
+	gid_t gid;
 };
 
 struct reg_file_info {
-- 
2.7.4



More information about the CRIU mailing list