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 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 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); 60 ATF_TC_HEAD(jail_root, tc) 61 { 62 atf_tc_set_md_var(tc, "require.user", "root"); 63 } 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 } 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 129 ATF_TP_ADD_TCS(tp) 130 { 131 ATF_TP_ADD_TC(tp, jail_root); 132 return (atf_no_error()); 133 } 134