nosymfollow-test.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <limits.h>
  6. #include <sched.h>
  7. #include <stdarg.h>
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/mount.h>
  13. #include <sys/stat.h>
  14. #include <sys/types.h>
  15. #include <sys/vfs.h>
  16. #include <unistd.h>
  17. #ifndef MS_NOSYMFOLLOW
  18. # define MS_NOSYMFOLLOW 256 /* Do not follow symlinks */
  19. #endif
  20. #ifndef ST_NOSYMFOLLOW
  21. # define ST_NOSYMFOLLOW 0x2000 /* Do not follow symlinks */
  22. #endif
  23. #define DATA "/tmp/data"
  24. #define LINK "/tmp/symlink"
  25. #define TMP "/tmp"
  26. static void die(char *fmt, ...)
  27. {
  28. va_list ap;
  29. va_start(ap, fmt);
  30. vfprintf(stderr, fmt, ap);
  31. va_end(ap);
  32. exit(EXIT_FAILURE);
  33. }
  34. static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt,
  35. va_list ap)
  36. {
  37. ssize_t written;
  38. char buf[4096];
  39. int buf_len;
  40. int fd;
  41. buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
  42. if (buf_len < 0)
  43. die("vsnprintf failed: %s\n", strerror(errno));
  44. if (buf_len >= sizeof(buf))
  45. die("vsnprintf output truncated\n");
  46. fd = open(filename, O_WRONLY);
  47. if (fd < 0) {
  48. if ((errno == ENOENT) && enoent_ok)
  49. return;
  50. die("open of %s failed: %s\n", filename, strerror(errno));
  51. }
  52. written = write(fd, buf, buf_len);
  53. if (written != buf_len) {
  54. if (written >= 0) {
  55. die("short write to %s\n", filename);
  56. } else {
  57. die("write to %s failed: %s\n",
  58. filename, strerror(errno));
  59. }
  60. }
  61. if (close(fd) != 0)
  62. die("close of %s failed: %s\n", filename, strerror(errno));
  63. }
  64. static void maybe_write_file(char *filename, char *fmt, ...)
  65. {
  66. va_list ap;
  67. va_start(ap, fmt);
  68. vmaybe_write_file(true, filename, fmt, ap);
  69. va_end(ap);
  70. }
  71. static void write_file(char *filename, char *fmt, ...)
  72. {
  73. va_list ap;
  74. va_start(ap, fmt);
  75. vmaybe_write_file(false, filename, fmt, ap);
  76. va_end(ap);
  77. }
  78. static void create_and_enter_ns(void)
  79. {
  80. uid_t uid = getuid();
  81. gid_t gid = getgid();
  82. if (unshare(CLONE_NEWUSER) != 0)
  83. die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno));
  84. maybe_write_file("/proc/self/setgroups", "deny");
  85. write_file("/proc/self/uid_map", "0 %d 1", uid);
  86. write_file("/proc/self/gid_map", "0 %d 1", gid);
  87. if (setgid(0) != 0)
  88. die("setgid(0) failed %s\n", strerror(errno));
  89. if (setuid(0) != 0)
  90. die("setuid(0) failed %s\n", strerror(errno));
  91. if (unshare(CLONE_NEWNS) != 0)
  92. die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno));
  93. }
  94. static void setup_symlink(void)
  95. {
  96. int data, err;
  97. data = creat(DATA, O_RDWR);
  98. if (data < 0)
  99. die("creat failed: %s\n", strerror(errno));
  100. err = symlink(DATA, LINK);
  101. if (err < 0)
  102. die("symlink failed: %s\n", strerror(errno));
  103. if (close(data) != 0)
  104. die("close of %s failed: %s\n", DATA, strerror(errno));
  105. }
  106. static void test_link_traversal(bool nosymfollow)
  107. {
  108. int link;
  109. link = open(LINK, 0, O_RDWR);
  110. if (nosymfollow) {
  111. if ((link != -1 || errno != ELOOP)) {
  112. die("link traversal unexpected result: %d, %s\n",
  113. link, strerror(errno));
  114. }
  115. } else {
  116. if (link < 0)
  117. die("link traversal failed: %s\n", strerror(errno));
  118. if (close(link) != 0)
  119. die("close of link failed: %s\n", strerror(errno));
  120. }
  121. }
  122. static void test_readlink(void)
  123. {
  124. char buf[4096];
  125. ssize_t ret;
  126. bzero(buf, sizeof(buf));
  127. ret = readlink(LINK, buf, sizeof(buf));
  128. if (ret < 0)
  129. die("readlink failed: %s\n", strerror(errno));
  130. if (strcmp(buf, DATA) != 0)
  131. die("readlink strcmp failed: '%s' '%s'\n", buf, DATA);
  132. }
  133. static void test_realpath(void)
  134. {
  135. char *path = realpath(LINK, NULL);
  136. if (!path)
  137. die("realpath failed: %s\n", strerror(errno));
  138. if (strcmp(path, DATA) != 0)
  139. die("realpath strcmp failed\n");
  140. free(path);
  141. }
  142. static void test_statfs(bool nosymfollow)
  143. {
  144. struct statfs buf;
  145. int ret;
  146. ret = statfs(TMP, &buf);
  147. if (ret)
  148. die("statfs failed: %s\n", strerror(errno));
  149. if (nosymfollow) {
  150. if ((buf.f_flags & ST_NOSYMFOLLOW) == 0)
  151. die("ST_NOSYMFOLLOW not set on %s\n", TMP);
  152. } else {
  153. if ((buf.f_flags & ST_NOSYMFOLLOW) != 0)
  154. die("ST_NOSYMFOLLOW set on %s\n", TMP);
  155. }
  156. }
  157. static void run_tests(bool nosymfollow)
  158. {
  159. test_link_traversal(nosymfollow);
  160. test_readlink();
  161. test_realpath();
  162. test_statfs(nosymfollow);
  163. }
  164. int main(int argc, char **argv)
  165. {
  166. create_and_enter_ns();
  167. if (mount("testing", TMP, "ramfs", 0, NULL) != 0)
  168. die("mount failed: %s\n", strerror(errno));
  169. setup_symlink();
  170. run_tests(false);
  171. if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0)
  172. die("remount failed: %s\n", strerror(errno));
  173. run_tests(true);
  174. return EXIT_SUCCESS;
  175. }