xref: /freebsd/tests/sys/kern/jail_lookup_root.c (revision a5dac34f6e98c47bd7cb1946e39cc45432e167a8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
5  */
6 
7 #include <sys/param.h>
8 #include <sys/jail.h>
9 #include <sys/mount.h>
10 #include <sys/stat.h>
11 
12 #include <err.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <jail.h>
16 #include <mntopts.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 
20 #include <atf-c.h>
21 
22 static void
mkdir_checked(const char * dir,mode_t mode)23 mkdir_checked(const char *dir, mode_t mode)
24 {
25 	int error;
26 
27 	error = mkdir(dir, mode);
28 	ATF_REQUIRE_MSG(error == 0 || errno == EEXIST,
29 	    "mkdir %s: %s", dir, strerror(errno));
30 }
31 
32 static void __unused
mount_nullfs(const char * dir,const char * target)33 mount_nullfs(const char *dir, const char *target)
34 {
35 	struct iovec *iov;
36 	char errmsg[1024];
37 	int error, iovlen;
38 
39 	iov = NULL;
40 	iovlen = 0;
41 
42 	build_iovec(&iov, &iovlen, __DECONST(char *, "fstype"),
43 	    __DECONST(char *, "nullfs"), (size_t)-1);
44 	build_iovec(&iov, &iovlen, __DECONST(char *, "fspath"),
45 	    __DECONST(char *, target), (size_t)-1);
46 	build_iovec(&iov, &iovlen, __DECONST(char *, "from"),
47 	    __DECONST(char *, dir), (size_t)-1);
48 	build_iovec(&iov, &iovlen, __DECONST(char *, "errmsg"),
49 	    errmsg, sizeof(errmsg));
50 
51 	errmsg[0] = '\0';
52 	error = nmount(iov, iovlen, 0);
53 	ATF_REQUIRE_MSG(error == 0, "nmount: %s",
54 	    errmsg[0] != '\0' ? errmsg : strerror(errno));
55 
56 	free_iovec(&iov, &iovlen);
57 }
58 
59 ATF_TC_WITH_CLEANUP(jail_root);
ATF_TC_HEAD(jail_root,tc)60 ATF_TC_HEAD(jail_root, tc)
61 {
62 	atf_tc_set_md_var(tc, "require.user", "root");
63 }
ATF_TC_BODY(jail_root,tc)64 ATF_TC_BODY(jail_root, tc)
65 {
66 	int error, fd, jid;
67 
68 	mkdir_checked("./root", 0755);
69 	mkdir_checked("./root/a", 0755);
70 	mkdir_checked("./root/b", 0755);
71 	mkdir_checked("./root/a/c", 0755);
72 
73 	jid = jail_setv(JAIL_CREATE | JAIL_ATTACH,
74 	    "name", "nullfs_jail_root_test",
75 	    "allow.mount", "true",
76 	    "allow.mount.nullfs", "true",
77 	    "enforce_statfs", "1",
78 	    "path", "./root",
79 	    "persist", NULL,
80 	    NULL);
81 	ATF_REQUIRE_MSG(jid >= 0, "jail_setv: %s", jail_errmsg);
82 
83 	mount_nullfs("/a", "/b");
84 
85 	error = chdir("/b/c");
86 	ATF_REQUIRE(error == 0);
87 
88 	error = rename("/a/c", "/c");
89 	ATF_REQUIRE(error == 0);
90 
91 	/* Descending to the jail root should be ok. */
92 	error = chdir("..");
93 	ATF_REQUIRE(error == 0);
94 
95 	/* Going beyond the root will trigger an error. */
96 	error = chdir("..");
97 	ATF_REQUIRE_ERRNO(ENOENT, error != 0);
98 	fd = open("..", O_RDONLY | O_DIRECTORY);
99 	ATF_REQUIRE_ERRNO(ENOENT, fd < 0);
100 }
ATF_TC_CLEANUP(jail_root,tc)101 ATF_TC_CLEANUP(jail_root, tc)
102 {
103 	struct statfs fs;
104 	fsid_t fsid;
105 	int error, jid;
106 
107 	error = statfs("./root/b", &fs);
108 	if (error != 0)
109 		err(1, "statfs ./b");
110 	fsid = fs.f_fsid;
111 	error = statfs("./root", &fs);
112 	if (error != 0)
113 		err(1, "statfs ./root");
114 	if (fsid.val[0] != fs.f_fsid.val[0] ||
115 	    fsid.val[1] != fs.f_fsid.val[1]) {
116 		error = unmount("./root/b", 0);
117 		if (error != 0)
118 			err(1, "unmount ./root/b");
119 	}
120 
121 	jid = jail_getid("nullfs_jail_root_test");
122 	if (jid >= 0) {
123 		error = jail_remove(jid);
124 		if (error != 0)
125 			err(1, "jail_remove");
126 	}
127 }
128 
ATF_TP_ADD_TCS(tp)129 ATF_TP_ADD_TCS(tp)
130 {
131 	ATF_TP_ADD_TC(tp, jail_root);
132 	return (atf_no_error());
133 }
134