unprivileged-remount-test.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <sched.h>
  4. #include <stdio.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <sys/types.h>
  8. #include <sys/mount.h>
  9. #include <sys/wait.h>
  10. #include <sys/vfs.h>
  11. #include <sys/statvfs.h>
  12. #include <stdlib.h>
  13. #include <unistd.h>
  14. #include <fcntl.h>
  15. #include <grp.h>
  16. #include <stdbool.h>
  17. #include <stdarg.h>
  18. #ifndef CLONE_NEWNS
  19. # define CLONE_NEWNS 0x00020000
  20. #endif
  21. #ifndef CLONE_NEWUTS
  22. # define CLONE_NEWUTS 0x04000000
  23. #endif
  24. #ifndef CLONE_NEWIPC
  25. # define CLONE_NEWIPC 0x08000000
  26. #endif
  27. #ifndef CLONE_NEWNET
  28. # define CLONE_NEWNET 0x40000000
  29. #endif
  30. #ifndef CLONE_NEWUSER
  31. # define CLONE_NEWUSER 0x10000000
  32. #endif
  33. #ifndef CLONE_NEWPID
  34. # define CLONE_NEWPID 0x20000000
  35. #endif
  36. #ifndef MS_REC
  37. # define MS_REC 16384
  38. #endif
  39. #ifndef MS_RELATIME
  40. # define MS_RELATIME (1 << 21)
  41. #endif
  42. #ifndef MS_STRICTATIME
  43. # define MS_STRICTATIME (1 << 24)
  44. #endif
  45. static void die(char *fmt, ...)
  46. {
  47. va_list ap;
  48. va_start(ap, fmt);
  49. vfprintf(stderr, fmt, ap);
  50. va_end(ap);
  51. exit(EXIT_FAILURE);
  52. }
  53. static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
  54. {
  55. char buf[4096];
  56. int fd;
  57. ssize_t written;
  58. int buf_len;
  59. buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
  60. if (buf_len < 0) {
  61. die("vsnprintf failed: %s\n",
  62. strerror(errno));
  63. }
  64. if (buf_len >= sizeof(buf)) {
  65. die("vsnprintf output truncated\n");
  66. }
  67. fd = open(filename, O_WRONLY);
  68. if (fd < 0) {
  69. if ((errno == ENOENT) && enoent_ok)
  70. return;
  71. die("open of %s failed: %s\n",
  72. filename, strerror(errno));
  73. }
  74. written = write(fd, buf, buf_len);
  75. if (written != buf_len) {
  76. if (written >= 0) {
  77. die("short write to %s\n", filename);
  78. } else {
  79. die("write to %s failed: %s\n",
  80. filename, strerror(errno));
  81. }
  82. }
  83. if (close(fd) != 0) {
  84. die("close of %s failed: %s\n",
  85. filename, strerror(errno));
  86. }
  87. }
  88. static void maybe_write_file(char *filename, char *fmt, ...)
  89. {
  90. va_list ap;
  91. va_start(ap, fmt);
  92. vmaybe_write_file(true, filename, fmt, ap);
  93. va_end(ap);
  94. }
  95. static void write_file(char *filename, char *fmt, ...)
  96. {
  97. va_list ap;
  98. va_start(ap, fmt);
  99. vmaybe_write_file(false, filename, fmt, ap);
  100. va_end(ap);
  101. }
  102. static int read_mnt_flags(const char *path)
  103. {
  104. int ret;
  105. struct statvfs stat;
  106. int mnt_flags;
  107. ret = statvfs(path, &stat);
  108. if (ret != 0) {
  109. die("statvfs of %s failed: %s\n",
  110. path, strerror(errno));
  111. }
  112. if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \
  113. ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \
  114. ST_SYNCHRONOUS | ST_MANDLOCK)) {
  115. die("Unrecognized mount flags\n");
  116. }
  117. mnt_flags = 0;
  118. if (stat.f_flag & ST_RDONLY)
  119. mnt_flags |= MS_RDONLY;
  120. if (stat.f_flag & ST_NOSUID)
  121. mnt_flags |= MS_NOSUID;
  122. if (stat.f_flag & ST_NODEV)
  123. mnt_flags |= MS_NODEV;
  124. if (stat.f_flag & ST_NOEXEC)
  125. mnt_flags |= MS_NOEXEC;
  126. if (stat.f_flag & ST_NOATIME)
  127. mnt_flags |= MS_NOATIME;
  128. if (stat.f_flag & ST_NODIRATIME)
  129. mnt_flags |= MS_NODIRATIME;
  130. if (stat.f_flag & ST_RELATIME)
  131. mnt_flags |= MS_RELATIME;
  132. if (stat.f_flag & ST_SYNCHRONOUS)
  133. mnt_flags |= MS_SYNCHRONOUS;
  134. if (stat.f_flag & ST_MANDLOCK)
  135. mnt_flags |= ST_MANDLOCK;
  136. return mnt_flags;
  137. }
  138. static void create_and_enter_userns(void)
  139. {
  140. uid_t uid;
  141. gid_t gid;
  142. uid = getuid();
  143. gid = getgid();
  144. if (unshare(CLONE_NEWUSER) !=0) {
  145. die("unshare(CLONE_NEWUSER) failed: %s\n",
  146. strerror(errno));
  147. }
  148. maybe_write_file("/proc/self/setgroups", "deny");
  149. write_file("/proc/self/uid_map", "0 %d 1", uid);
  150. write_file("/proc/self/gid_map", "0 %d 1", gid);
  151. if (setgid(0) != 0) {
  152. die ("setgid(0) failed %s\n",
  153. strerror(errno));
  154. }
  155. if (setuid(0) != 0) {
  156. die("setuid(0) failed %s\n",
  157. strerror(errno));
  158. }
  159. }
  160. static
  161. bool test_unpriv_remount(const char *fstype, const char *mount_options,
  162. int mount_flags, int remount_flags, int invalid_flags)
  163. {
  164. pid_t child;
  165. child = fork();
  166. if (child == -1) {
  167. die("fork failed: %s\n",
  168. strerror(errno));
  169. }
  170. if (child != 0) { /* parent */
  171. pid_t pid;
  172. int status;
  173. pid = waitpid(child, &status, 0);
  174. if (pid == -1) {
  175. die("waitpid failed: %s\n",
  176. strerror(errno));
  177. }
  178. if (pid != child) {
  179. die("waited for %d got %d\n",
  180. child, pid);
  181. }
  182. if (!WIFEXITED(status)) {
  183. die("child did not terminate cleanly\n");
  184. }
  185. return WEXITSTATUS(status) == EXIT_SUCCESS;
  186. }
  187. create_and_enter_userns();
  188. if (unshare(CLONE_NEWNS) != 0) {
  189. die("unshare(CLONE_NEWNS) failed: %s\n",
  190. strerror(errno));
  191. }
  192. if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) {
  193. die("mount of %s with options '%s' on /tmp failed: %s\n",
  194. fstype,
  195. mount_options? mount_options : "",
  196. strerror(errno));
  197. }
  198. create_and_enter_userns();
  199. if (unshare(CLONE_NEWNS) != 0) {
  200. die("unshare(CLONE_NEWNS) failed: %s\n",
  201. strerror(errno));
  202. }
  203. if (mount("/tmp", "/tmp", "none",
  204. MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) {
  205. /* system("cat /proc/self/mounts"); */
  206. die("remount of /tmp failed: %s\n",
  207. strerror(errno));
  208. }
  209. if (mount("/tmp", "/tmp", "none",
  210. MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) {
  211. /* system("cat /proc/self/mounts"); */
  212. die("remount of /tmp with invalid flags "
  213. "succeeded unexpectedly\n");
  214. }
  215. exit(EXIT_SUCCESS);
  216. }
  217. static bool test_unpriv_remount_simple(int mount_flags)
  218. {
  219. return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
  220. }
  221. static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags)
  222. {
  223. return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags,
  224. invalid_flags);
  225. }
  226. static bool test_priv_mount_unpriv_remount(void)
  227. {
  228. pid_t child;
  229. int ret;
  230. const char *orig_path = "/dev";
  231. const char *dest_path = "/tmp";
  232. int orig_mnt_flags, remount_mnt_flags;
  233. child = fork();
  234. if (child == -1) {
  235. die("fork failed: %s\n",
  236. strerror(errno));
  237. }
  238. if (child != 0) { /* parent */
  239. pid_t pid;
  240. int status;
  241. pid = waitpid(child, &status, 0);
  242. if (pid == -1) {
  243. die("waitpid failed: %s\n",
  244. strerror(errno));
  245. }
  246. if (pid != child) {
  247. die("waited for %d got %d\n",
  248. child, pid);
  249. }
  250. if (!WIFEXITED(status)) {
  251. die("child did not terminate cleanly\n");
  252. }
  253. return WEXITSTATUS(status) == EXIT_SUCCESS;
  254. }
  255. orig_mnt_flags = read_mnt_flags(orig_path);
  256. create_and_enter_userns();
  257. ret = unshare(CLONE_NEWNS);
  258. if (ret != 0) {
  259. die("unshare(CLONE_NEWNS) failed: %s\n",
  260. strerror(errno));
  261. }
  262. ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL);
  263. if (ret != 0) {
  264. die("recursive bind mount of %s onto %s failed: %s\n",
  265. orig_path, dest_path, strerror(errno));
  266. }
  267. ret = mount(dest_path, dest_path, "none",
  268. MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL);
  269. if (ret != 0) {
  270. /* system("cat /proc/self/mounts"); */
  271. die("remount of /tmp failed: %s\n",
  272. strerror(errno));
  273. }
  274. remount_mnt_flags = read_mnt_flags(dest_path);
  275. if (orig_mnt_flags != remount_mnt_flags) {
  276. die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n",
  277. dest_path, orig_path);
  278. }
  279. exit(EXIT_SUCCESS);
  280. }
  281. int main(int argc, char **argv)
  282. {
  283. if (!test_unpriv_remount_simple(MS_RDONLY)) {
  284. die("MS_RDONLY malfunctions\n");
  285. }
  286. if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) {
  287. die("MS_NODEV malfunctions\n");
  288. }
  289. if (!test_unpriv_remount_simple(MS_NOSUID)) {
  290. die("MS_NOSUID malfunctions\n");
  291. }
  292. if (!test_unpriv_remount_simple(MS_NOEXEC)) {
  293. die("MS_NOEXEC malfunctions\n");
  294. }
  295. if (!test_unpriv_remount_atime(MS_RELATIME,
  296. MS_NOATIME))
  297. {
  298. die("MS_RELATIME malfunctions\n");
  299. }
  300. if (!test_unpriv_remount_atime(MS_STRICTATIME,
  301. MS_NOATIME))
  302. {
  303. die("MS_STRICTATIME malfunctions\n");
  304. }
  305. if (!test_unpriv_remount_atime(MS_NOATIME,
  306. MS_STRICTATIME))
  307. {
  308. die("MS_NOATIME malfunctions\n");
  309. }
  310. if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME,
  311. MS_NOATIME))
  312. {
  313. die("MS_RELATIME|MS_NODIRATIME malfunctions\n");
  314. }
  315. if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME,
  316. MS_NOATIME))
  317. {
  318. die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n");
  319. }
  320. if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME,
  321. MS_STRICTATIME))
  322. {
  323. die("MS_NOATIME|MS_DIRATIME malfunctions\n");
  324. }
  325. if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME))
  326. {
  327. die("Default atime malfunctions\n");
  328. }
  329. if (!test_priv_mount_unpriv_remount()) {
  330. die("Mount flags unexpectedly changed after remount\n");
  331. }
  332. return EXIT_SUCCESS;
  333. }