unprivileged-remount-test.c 8.2 KB

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