xref: /linux/tools/testing/selftests/filesystems/overlayfs/idmapped_mounts.c (revision aaed66fadba2d2de8fe0daa0aa3eac827d2076b9)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <sched.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <sys/stat.h>
10 #include <sys/syscall.h>
11 
12 #include <linux/mount.h>
13 #include <linux/types.h>
14 
15 #include "kselftest_harness.h"
16 #include "../wrappers.h"
17 #include "../utils.h"
18 
19 /*
20  * An idmapping that maps the mount-visible id range [0, ID_RANGE) onto the
21  * host/overlay-final id range [ID_HOST, ID_HOST + ID_RANGE).  Through such an
22  * idmapped overlay mount, an overlay-final id of ID_HOST + n is reported as n,
23  * and an id of n requested through the mount is stored as ID_HOST + n.
24  */
25 #define ID_NS	 0
26 #define ID_HOST	 10000
27 #define ID_RANGE 10000
28 
29 /*
30  * For the composition test the lower layer's on-disk ids live in a
31  * separate range and are mapped by an idmapped lower layer onto the
32  * overlay-final range [ID_HOST, ID_HOST + ID_RANGE).
33  */
34 #define LAYER_HOST 20000
35 
36 #ifndef MOUNT_ATTR_IDMAP
37 #define MOUNT_ATTR_IDMAP 0x00100000
38 #endif
39 
40 #ifndef __NR_mount_setattr
41 #define __NR_mount_setattr 442
42 #endif
43 
44 static inline int sys_mount_setattr(int dfd, const char *path,
45 				    unsigned int flags,
46 				    struct mount_attr *attr, size_t size)
47 {
48 	return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
49 }
50 
51 static bool ovl_supported(void)
52 {
53 	int fd = sys_fsopen("overlay", 0);
54 
55 	if (fd < 0)
56 		return false;
57 	close(fd);
58 	return true;
59 }
60 
61 /* base/{l,u,w} owned by ID_HOST so they map to ID_NS through the idmap. */
62 static int setup_layers(const char *base)
63 {
64 	static const char *sub[] = { "", "/l", "/u", "/w" };
65 	char path[PATH_MAX];
66 
67 	for (size_t i = 0; i < ARRAY_SIZE(sub); i++) {
68 		snprintf(path, sizeof(path), "%s%s", base, sub[i]);
69 		if (mkdir(path, 0755) && errno != EEXIST)
70 			return -1;
71 		if (i && chown(path, ID_HOST, ID_HOST))
72 			return -1;
73 	}
74 	return 0;
75 }
76 
77 static int ovl_mount(const char *base, bool nfs_export)
78 {
79 	char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX];
80 	int fsfd, ovl;
81 
82 	snprintf(lower, sizeof(lower), "%s/l", base);
83 	snprintf(upper, sizeof(upper), "%s/u", base);
84 	snprintf(work, sizeof(work), "%s/w", base);
85 
86 	fsfd = sys_fsopen("overlay", 0);
87 	if (fsfd < 0)
88 		return -1;
89 
90 	if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) ||
91 	    sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", lower, 0) ||
92 	    sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) ||
93 	    sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0))
94 		goto err;
95 	if (nfs_export &&
96 	    (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "index", "on", 0) ||
97 	     sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "nfs_export", "on", 0)))
98 		goto err;
99 	if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0))
100 		goto err;
101 
102 	ovl = sys_fsmount(fsfd, 0, 0);
103 	close(fsfd);
104 	return ovl;
105 err:
106 	close(fsfd);
107 	return -1;
108 }
109 
110 /* Idmap the (still detached, not yet visible) overlay mount @mfd. */
111 static int ovl_idmap(int mfd)
112 {
113 	struct mount_attr attr = {
114 		.attr_set = MOUNT_ATTR_IDMAP,
115 	};
116 	int ret, userns_fd;
117 
118 	/*
119 	 * get_userns_fd(fs_id, mount_id, range): a file whose filesystem id
120 	 * is fs_id + n is shown through the idmapped mount as mount_id + n.
121 	 * Here the overlay-final (fs side) range is [ID_HOST, ..) and the
122 	 * caller-visible (mount side) range is [ID_NS, ..).
123 	 */
124 	userns_fd = get_userns_fd(ID_HOST, ID_NS, ID_RANGE);
125 	if (userns_fd < 0)
126 		return -1;
127 
128 	attr.userns_fd = userns_fd;
129 	ret = sys_mount_setattr(mfd, "", AT_EMPTY_PATH, &attr, sizeof(attr));
130 	close(userns_fd);
131 	return ret;
132 }
133 
134 /* Clone @path into a detached, idmapped mount usable as an overlay layer. */
135 static int idmapped_layer_fd(const char *path, int nsid, int hostid, int range)
136 {
137 	struct mount_attr attr = {
138 		.attr_set = MOUNT_ATTR_IDMAP,
139 	};
140 	int fd_tree, userns_fd;
141 
142 	fd_tree = sys_open_tree(AT_FDCWD, path,
143 			       OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
144 	if (fd_tree < 0)
145 		return -1;
146 	userns_fd = get_userns_fd(nsid, hostid, range);
147 	if (userns_fd < 0) {
148 		close(fd_tree);
149 		return -1;
150 	}
151 	attr.userns_fd = userns_fd;
152 	if (sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr,
153 			      sizeof(attr))) {
154 		close(userns_fd);
155 		close(fd_tree);
156 		return -1;
157 	}
158 	close(userns_fd);
159 	return fd_tree;
160 }
161 
162 /* Overlay with a layer passed by fd (idmapped) plus a plain upper/work. */
163 static int ovl_mount_lower_fd(const char *upper, const char *work, int fd_lower)
164 {
165 	int fsfd, ovl;
166 
167 	fsfd = sys_fsopen("overlay", 0);
168 	if (fsfd < 0)
169 		return -1;
170 
171 	if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) ||
172 	    sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) ||
173 	    sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0) ||
174 	    sys_fsconfig(fsfd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower) ||
175 	    sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0))
176 		goto err;
177 
178 	ovl = sys_fsmount(fsfd, 0, 0);
179 	close(fsfd);
180 	return ovl;
181 err:
182 	close(fsfd);
183 	return -1;
184 }
185 
186 /*
187  * Mount an overlay inside user namespace @u1 (so the overlay sb's s_user_ns is
188  * not the initial namespace) and idmap that overlay mount with @u2.  Runs in a
189  * child that joins @u1; returns 0 on success.
190  */
191 static int userns_overlay_child(int u1)
192 {
193 	struct mount_attr attr = {
194 		.attr_set = MOUNT_ATTR_IDMAP,
195 	};
196 	struct stat st;
197 	int ovl, u2;
198 
199 	/* Become root in the overlay sb's user namespace u1. */
200 	if (!switch_userns(u1, 0, 0, false))
201 		return fprintf(stderr, "userns: switch_userns: %m\n"), -1;
202 	if (unshare(CLONE_NEWNS) ||
203 	    sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL))
204 		return fprintf(stderr, "userns: unshare/slave: %m\n"), -1;
205 	if (sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL))
206 		return fprintf(stderr, "userns: mount tmpfs: %m\n"), -1;
207 	if (setup_layers("/tmp/ovl"))
208 		return fprintf(stderr, "userns: setup_layers: %m\n"), -1;
209 	if (mknod("/tmp/ovl/l/file", S_IFREG | 0644, 0) ||
210 	    chown("/tmp/ovl/l/file", ID_HOST + 5, ID_HOST + 5))
211 		return fprintf(stderr, "userns: lower file: %m\n"), -1;
212 
213 	ovl = ovl_mount("/tmp/ovl", false);
214 	if (ovl < 0)
215 		return fprintf(stderr, "userns: ovl_mount: %m\n"), -1;
216 
217 	/*
218 	 * mount_setattr() requires CAP_SYS_ADMIN over the idmap user
219 	 * namespace, so it must be a child of u1.  Create it now, from
220 	 * inside u1.
221 	 */
222 	u2 = get_userns_fd(ID_HOST, ID_NS, ID_RANGE);
223 	if (u2 < 0)
224 		return fprintf(stderr, "userns: get_userns_fd: %m\n"), -1;
225 	attr.userns_fd = u2;
226 	if (sys_mount_setattr(ovl, "", AT_EMPTY_PATH, &attr, sizeof(attr)))
227 		return fprintf(stderr, "userns: mount_setattr: %m\n"), -1;
228 	close(u2);
229 
230 	if (fstatat(ovl, "file", &st, 0))
231 		return fprintf(stderr, "userns: fstatat: %m\n"), -1;
232 	if (st.st_uid != ID_NS + 5 || st.st_gid != ID_NS + 5) {
233 		fprintf(stderr, "userns: got %u:%u expected %u:%u\n",
234 			st.st_uid, st.st_gid, ID_NS + 5, ID_NS + 5);
235 		return -1;
236 	}
237 	return 0;
238 }
239 
240 FIXTURE(idmapped_overlay) {
241 	char base[64];
242 };
243 
244 FIXTURE_SETUP(idmapped_overlay)
245 {
246 	/* Private mount namespace so test mounts need no cleanup. */
247 	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
248 	ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
249 
250 	/* tmpfs for the layers so we can chown them to arbitrary ids. */
251 	ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0);
252 
253 	snprintf(self->base, sizeof(self->base), "/tmp/ovl");
254 	ASSERT_EQ(setup_layers(self->base), 0);
255 }
256 
257 FIXTURE_TEARDOWN(idmapped_overlay)
258 {
259 }
260 
261 /* A file owned by ID_HOST + 5 is reported as ID_NS + 5 through the idmap. */
262 TEST_F(idmapped_overlay, getattr)
263 {
264 	char path[PATH_MAX];
265 	struct stat st;
266 	int ovl;
267 
268 	if (!ovl_supported())
269 		SKIP(return, "overlayfs not supported");
270 
271 	snprintf(path, sizeof(path), "%s/l/file", self->base);
272 	ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0);
273 	ASSERT_EQ(chown(path, ID_HOST + 5, ID_HOST + 5), 0);
274 
275 	ovl = ovl_mount(self->base, false);
276 	ASSERT_GE(ovl, 0);
277 	ASSERT_EQ(ovl_idmap(ovl), 0);
278 
279 	ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0);
280 	EXPECT_EQ(st.st_uid, ID_NS + 5);
281 	EXPECT_EQ(st.st_gid, ID_NS + 5);
282 
283 	EXPECT_EQ(close(ovl), 0);
284 }
285 
286 /*
287  * Every creation path initializes the new owner through the mount idmap:
288  * created as caller id ID_NS, stored on the upper layer as overlay-final
289  * ID_HOST.  Covers ovl_create() (regular file), ovl_mkdir(), ovl_mknod()
290  * and ovl_symlink() (which share ovl_create_object()), plus the separate
291  * ovl_tmpfile() path.
292  */
293 TEST_F(idmapped_overlay, create)
294 {
295 	static const char *names[] = { "reg", "dir", "fifo", "lnk" };
296 	char path[PATH_MAX];
297 	struct stat st;
298 	int ovl, fd;
299 
300 	if (!ovl_supported())
301 		SKIP(return, "overlayfs not supported");
302 
303 	ovl = ovl_mount(self->base, false);
304 	ASSERT_GE(ovl, 0);
305 	ASSERT_EQ(ovl_idmap(ovl), 0);
306 
307 	/* One object per creation operation, all as caller id ID_NS. */
308 	fd = openat(ovl, "reg", O_CREAT | O_WRONLY | O_EXCL, 0644);
309 	ASSERT_GE(fd, 0);
310 	EXPECT_EQ(close(fd), 0);
311 	ASSERT_EQ(mkdirat(ovl, "dir", 0755), 0);
312 	ASSERT_EQ(mknodat(ovl, "fifo", S_IFIFO | 0644, 0), 0);
313 	ASSERT_EQ(symlinkat("target", ovl, "lnk"), 0);
314 
315 	for (size_t i = 0; i < ARRAY_SIZE(names); i++) {
316 		/* Reported as ID_NS through the idmapped mount ... */
317 		ASSERT_EQ(fstatat(ovl, names[i], &st, AT_SYMLINK_NOFOLLOW), 0);
318 		EXPECT_EQ(st.st_uid, ID_NS);
319 		EXPECT_EQ(st.st_gid, ID_NS);
320 		/* ... and stored as ID_HOST on the upper layer. */
321 		snprintf(path, sizeof(path), "%s/u/%s", self->base, names[i]);
322 		ASSERT_EQ(lstat(path, &st), 0);
323 		EXPECT_EQ(st.st_uid, ID_HOST);
324 		EXPECT_EQ(st.st_gid, ID_HOST);
325 	}
326 
327 	/* O_TMPFILE goes through the separate ovl_tmpfile() path. */
328 	fd = openat(ovl, ".", O_TMPFILE | O_WRONLY, 0644);
329 	ASSERT_GE(fd, 0);
330 	/* Inside the mount: caller id ID_NS. */
331 	ASSERT_EQ(fstat(fd, &st), 0);
332 	EXPECT_EQ(st.st_uid, ID_NS);
333 	EXPECT_EQ(st.st_gid, ID_NS);
334 	/* Link it in so the upper backing file can be inspected too. */
335 	ASSERT_EQ(linkat(fd, "", ovl, "tmp", AT_EMPTY_PATH), 0);
336 	EXPECT_EQ(close(fd), 0);
337 	snprintf(path, sizeof(path), "%s/u/tmp", self->base);
338 	ASSERT_EQ(lstat(path, &st), 0);
339 	EXPECT_EQ(st.st_uid, ID_HOST);
340 	EXPECT_EQ(st.st_gid, ID_HOST);
341 
342 	EXPECT_EQ(close(ovl), 0);
343 }
344 
345 /* chown through the idmapped mount round-trips: ID_NS + 5 <-> ID_HOST + 5. */
346 TEST_F(idmapped_overlay, chown)
347 {
348 	char path[PATH_MAX];
349 	struct stat st;
350 	int ovl, fd;
351 
352 	if (!ovl_supported())
353 		SKIP(return, "overlayfs not supported");
354 
355 	ovl = ovl_mount(self->base, false);
356 	ASSERT_GE(ovl, 0);
357 	ASSERT_EQ(ovl_idmap(ovl), 0);
358 
359 	fd = openat(ovl, "f", O_CREAT | O_WRONLY | O_EXCL, 0644);
360 	ASSERT_GE(fd, 0);
361 	EXPECT_EQ(close(fd), 0);
362 
363 	ASSERT_EQ(fchownat(ovl, "f", ID_NS + 5, ID_NS + 5, 0), 0);
364 
365 	ASSERT_EQ(fstatat(ovl, "f", &st, 0), 0);
366 	EXPECT_EQ(st.st_uid, ID_NS + 5);
367 	EXPECT_EQ(st.st_gid, ID_NS + 5);
368 
369 	snprintf(path, sizeof(path), "%s/u/f", self->base);
370 	ASSERT_EQ(stat(path, &st), 0);
371 	EXPECT_EQ(st.st_uid, ID_HOST + 5);
372 	EXPECT_EQ(st.st_gid, ID_HOST + 5);
373 
374 	EXPECT_EQ(close(ovl), 0);
375 }
376 
377 /*
378  * Composition: an idmapped lower layer underneath an idmapped overlay mount.
379  * An on-disk id is mapped by the layer idmap into the overlay-final range and
380  * then by the mount idmap into the caller's range:
381  *
382  *   on-disk LAYER_HOST+7  --layer-->  ID_HOST+7  --mount-->  ID_NS+7
383  */
384 TEST_F(idmapped_overlay, composition)
385 {
386 	char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX], path[PATH_MAX];
387 	struct stat st;
388 	int ovl, fd_lower;
389 
390 	if (!ovl_supported())
391 		SKIP(return, "overlayfs not supported");
392 
393 	snprintf(lower, sizeof(lower), "%s/l", self->base);
394 	snprintf(upper, sizeof(upper), "%s/u", self->base);
395 	snprintf(work, sizeof(work), "%s/w", self->base);
396 
397 	/* Put the lower layer's ids in the on-disk [LAYER_HOST, ..) range. */
398 	ASSERT_EQ(chown(lower, LAYER_HOST, LAYER_HOST), 0);
399 	snprintf(path, sizeof(path), "%s/l/file", self->base);
400 	ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0);
401 	ASSERT_EQ(chown(path, LAYER_HOST + 7, LAYER_HOST + 7), 0);
402 
403 	/* Idmapped lower: on-disk LAYER_HOST <-> overlay-final ID_HOST. */
404 	fd_lower = idmapped_layer_fd(lower, LAYER_HOST, ID_HOST, ID_RANGE);
405 	ASSERT_GE(fd_lower, 0);
406 
407 	ovl = ovl_mount_lower_fd(upper, work, fd_lower);
408 	ASSERT_GE(ovl, 0);
409 	EXPECT_EQ(close(fd_lower), 0);
410 
411 	/* Idmap the overlay mount: overlay-final ID_HOST <-> caller ID_NS. */
412 	ASSERT_EQ(ovl_idmap(ovl), 0);
413 
414 	ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0);
415 	EXPECT_EQ(st.st_uid, ID_NS + 7);
416 	EXPECT_EQ(st.st_gid, ID_NS + 7);
417 
418 	EXPECT_EQ(close(ovl), 0);
419 }
420 
421 /* An idmapped overlay mount whose sb lives inside a user namespace. */
422 TEST_F(idmapped_overlay, userns)
423 {
424 	int u1;
425 	pid_t pid;
426 
427 	if (!ovl_supported())
428 		SKIP(return, "overlayfs not supported");
429 
430 	/* u1 backs the overlay sb: identity-mapped, but not the init ns. */
431 	u1 = get_userns_fd(0, 0, 65536);
432 	if (u1 < 0)
433 		SKIP(return, "user namespaces not available");
434 
435 	pid = fork();
436 	ASSERT_GE(pid, 0);
437 	if (pid == 0) {
438 		int ret = userns_overlay_child(u1);
439 
440 		_exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
441 	}
442 	EXPECT_EQ(wait_for_pid(pid), 0);
443 
444 	EXPECT_EQ(close(u1), 0);
445 }
446 
447 /*
448  * An nfs_export overlay can be idmapped, and decodable file handles round-trip
449  * through the idmapped mount with correctly mapped ownership.  Overlay file
450  * handles encode object identity, not ownership, so the mount idmap does not
451  * affect them; it only maps the owner reported once a handle is reopened.
452  */
453 TEST_F(idmapped_overlay, nfs_export_handles)
454 {
455 	char path[PATH_MAX], mnt[128];
456 	union {
457 		struct file_handle fh;
458 		char buf[sizeof(struct file_handle) + MAX_HANDLE_SZ];
459 	} fhu;
460 	struct file_handle *fh = &fhu.fh;
461 	struct stat st;
462 	int ovl, mfd, fd, mount_id;
463 
464 	if (!ovl_supported())
465 		SKIP(return, "overlayfs not supported");
466 
467 	snprintf(path, sizeof(path), "%s/l/file", self->base);
468 	ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0);
469 	ASSERT_EQ(chown(path, ID_HOST + 7, ID_HOST + 7), 0);
470 
471 	/* nfs_export=on gives decodable overlay file handles. */
472 	ovl = ovl_mount(self->base, true);
473 	if (ovl < 0)
474 		SKIP(return, "overlayfs nfs_export not supported");
475 	ASSERT_EQ(ovl_idmap(ovl), 0);
476 
477 	/* Attach the idmapped mount so handles can be resolved against it. */
478 	snprintf(mnt, sizeof(mnt), "%s/mnt", self->base);
479 	ASSERT_EQ(mkdir(mnt, 0755), 0);
480 	ASSERT_EQ(sys_move_mount(ovl, "", AT_FDCWD, mnt,
481 				 MOVE_MOUNT_F_EMPTY_PATH), 0);
482 
483 	snprintf(path, sizeof(path), "%s/file", mnt);
484 	fh->handle_bytes = MAX_HANDLE_SZ;
485 	ASSERT_EQ(name_to_handle_at(AT_FDCWD, path, fh, &mount_id, 0), 0);
486 
487 	mfd = open(mnt, O_RDONLY | O_DIRECTORY);
488 	ASSERT_GE(mfd, 0);
489 	fd = open_by_handle_at(mfd, fh, O_RDONLY);
490 	EXPECT_EQ(close(mfd), 0);
491 	ASSERT_GE(fd, 0);
492 
493 	ASSERT_EQ(fstat(fd, &st), 0);
494 	EXPECT_EQ(st.st_uid, ID_NS + 7);
495 	EXPECT_EQ(st.st_gid, ID_NS + 7);
496 
497 	EXPECT_EQ(close(fd), 0);
498 	EXPECT_EQ(close(ovl), 0);
499 }
500 
501 TEST_HARNESS_MAIN
502