Mirror of GNU Guix
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
11 KiB

  1. Based on the patch from https://www.sudo.ws/repos/sudo/raw-rev/c2e36a80a279
  2. Backported to 1.8.15 by Mark H Weaver <mhw@netris.org>
  3. # HG changeset patch
  4. # User Todd C. Miller <Todd.Miller@courtesan.com>
  5. # Date 1452475889 25200
  6. # Node ID c2e36a80a27927c32cba55afae78b8dc830cddc3
  7. # Parent 94ffd6b18431fa4b9ed0a0c3f0b7b9582a4f6bde
  8. Rewritten sudoedit_checkdir support that checks all the dirs in the
  9. path and refuses to follow symlinks in writable directories.
  10. This is a better fix for CVE-2015-5602.
  11. Adapted from a diff by Ben Hutchings. Bug #707
  12. diff -r 94ffd6b18431 -r c2e36a80a279 doc/CONTRIBUTORS
  13. --- a/doc/CONTRIBUTORS Mon Jan 04 10:47:11 2016 -0700
  14. +++ b/doc/CONTRIBUTORS Sun Jan 10 18:31:29 2016 -0700
  15. @@ -58,6 +58,7 @@
  16. Holloway, Nick
  17. Hoover, Adam
  18. Hunter, Michael T.
  19. + Hutchings, Ben
  20. Irrgang, Eric
  21. Jackson, Brian
  22. Jackson, John R.
  23. diff -r 94ffd6b18431 -r c2e36a80a279 doc/UPGRADE
  24. --- a/doc/UPGRADE Mon Jan 04 10:47:11 2016 -0700
  25. +++ b/doc/UPGRADE Sun Jan 10 18:31:29 2016 -0700
  26. @@ -1,6 +1,15 @@
  27. Notes on upgrading from an older release
  28. ========================================
  29. +o Upgrading from a version prior to the post-1.8.15 fix for CVE-2015-5602.
  30. +
  31. + The meaning of the sudoedit_checkdir sudoers option has changed.
  32. + Previously, it would only check the parent directory
  33. + of the file to be edited. After the CVE fix, all directories
  34. + in the path to be edited are checked and sudoedit will refuse
  35. + to follow a symbolic link in a directory that is writable by
  36. + the invoking user.
  37. +
  38. o Upgrading from a version prior to 1.8.15:
  39. Prior to version 1.8.15, when env_reset was enabled (the default)
  40. diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.cat
  41. --- a/doc/sudoers.cat Mon Jan 04 10:47:11 2016 -0700
  42. +++ b/doc/sudoers.cat Sun Jan 10 18:31:29 2016 -0700
  43. @@ -1275,12 +1275,15 @@
  44. system call. This flag is _o_f_f by default.
  45. sudoedit_checkdir
  46. - If set, ssuuddooeeddiitt will refuse to edit files located in a
  47. - directory that is writable by the invoking user unless
  48. - it is run by root. On many systems, this option
  49. - requires that the parent directory of the file to be
  50. - edited be readable by the target user. This flag is
  51. - _o_f_f by default.
  52. + If set, ssuuddooeeddiitt will check directories in the path to
  53. + be edited for writability by the invoking user.
  54. + Symbolic links will not be followed in writable
  55. + directories and ssuuddooeeddiitt will also refuse to edit a
  56. + file located in a writable directory. Theses
  57. + restrictions are not enforced when ssuuddooeeddiitt is invoked
  58. + as root. On many systems, this option requires that
  59. + all directories in the path to be edited be readable by
  60. + the target user. This flag is _o_f_f by default.
  61. sudoedit_follow By default, ssuuddooeeddiitt will not follow symbolic links
  62. when opening files. The _s_u_d_o_e_d_i_t___f_o_l_l_o_w option can be
  63. diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.man.in
  64. --- a/doc/sudoers.man.in Mon Jan 04 10:47:11 2016 -0700
  65. +++ b/doc/sudoers.man.in Sun Jan 10 18:31:29 2016 -0700
  66. @@ -2715,10 +2715,16 @@
  67. .br
  68. If set,
  69. \fBsudoedit\fR
  70. -will refuse to edit files located in a directory that is writable
  71. -by the invoking user unless it is run by root.
  72. -On many systems, this option requires that the parent directory
  73. -of the file to be edited be readable by the target user.
  74. +will check directories in the path to be edited for writability
  75. +by the invoking user.
  76. +Symbolic links will not be followed in writable directories and
  77. +\fBsudoedit\fR
  78. +will also refuse to edit a file located in a writable directory.
  79. +Theses restrictions are not enforced when
  80. +\fBsudoedit\fR
  81. +is invoked as root.
  82. +On many systems, this option requires that all directories
  83. +in the path to be edited be readable by the target user.
  84. This flag is
  85. \fIoff\fR
  86. by default.
  87. diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.mdoc.in
  88. --- a/doc/sudoers.mdoc.in Mon Jan 04 10:47:11 2016 -0700
  89. +++ b/doc/sudoers.mdoc.in Sun Jan 10 18:31:29 2016 -0700
  90. @@ -2549,10 +2549,16 @@
  91. .It sudoedit_checkdir
  92. If set,
  93. .Nm sudoedit
  94. -will refuse to edit files located in a directory that is writable
  95. -by the invoking user unless it is run by root.
  96. -On many systems, this option requires that the parent directory
  97. -of the file to be edited be readable by the target user.
  98. +will check directories in the path to be edited for writability
  99. +by the invoking user.
  100. +Symbolic links will not be followed in writable directories and
  101. +.Nm sudoedit
  102. +will also refuse to edit a file located in a writable directory.
  103. +Theses restrictions are not enforced when
  104. +.Nm sudoedit
  105. +is invoked as root.
  106. +On many systems, this option requires that all directories
  107. +in the path to be edited be readable by the target user.
  108. This flag is
  109. .Em off
  110. by default.
  111. diff -r 94ffd6b18431 -r c2e36a80a279 include/sudo_compat.h
  112. --- a/include/sudo_compat.h Mon Jan 04 10:47:11 2016 -0700
  113. +++ b/include/sudo_compat.h Sun Jan 10 18:31:29 2016 -0700
  114. @@ -182,6 +182,8 @@
  115. # ifndef UTIME_NOW
  116. # define UTIME_NOW -2L
  117. # endif
  118. +#endif
  119. +#if !defined(HAVE_OPENAT) || (!defined(HAVE_FUTIMENS) && !defined(HAVE_UTIMENSAT))
  120. # ifndef AT_FDCWD
  121. # define AT_FDCWD -100
  122. # endif
  123. diff -r 94ffd6b18431 -r c2e36a80a279 src/sudo_edit.c
  124. --- a/src/sudo_edit.c Mon Jan 04 10:47:11 2016 -0700
  125. +++ b/src/sudo_edit.c Sun Jan 10 18:31:29 2016 -0700
  126. @@ -179,13 +179,15 @@
  127. }
  128. #ifndef HAVE_OPENAT
  129. -/* This does not support AT_FDCWD... */
  130. static int
  131. sudo_openat(int dfd, const char *path, int flags, mode_t mode)
  132. {
  133. int fd, odfd;
  134. debug_decl(sudo_openat, SUDO_DEBUG_EDIT)
  135. + if (dfd == AT_FDCWD)
  136. + debug_return_int(open(path, flags, mode));
  137. +
  138. /* Save cwd */
  139. if ((odfd = open(".", O_RDONLY)) == -1)
  140. debug_return_int(-1);
  141. @@ -207,6 +209,64 @@
  142. #define openat sudo_openat
  143. #endif /* HAVE_OPENAT */
  144. +#ifdef O_NOFOLLOW
  145. +static int
  146. +sudo_edit_openat_nofollow(int dfd, char *path, int oflags, mode_t mode)
  147. +{
  148. + debug_decl(sudo_edit_open_nofollow, SUDO_DEBUG_EDIT)
  149. +
  150. + debug_return_int(openat(dfd, path, oflags|O_NOFOLLOW, mode));
  151. +}
  152. +#else
  153. +/*
  154. + * Returns true if fd and path don't match or path is a symlink.
  155. + * Used on older systems without O_NOFOLLOW.
  156. + */
  157. +static bool
  158. +sudo_edit_is_symlink(int fd, char *path)
  159. +{
  160. + struct stat sb1, sb2;
  161. + debug_decl(sudo_edit_is_symlink, SUDO_DEBUG_EDIT)
  162. +
  163. + /*
  164. + * Treat [fl]stat() failure like there was a symlink.
  165. + */
  166. + if (fstat(fd, &sb1) == -1 || lstat(path, &sb2) == -1)
  167. + debug_return_bool(true);
  168. +
  169. + /*
  170. + * Make sure we did not open a link and that what we opened
  171. + * matches what is currently on the file system.
  172. + */
  173. + if (S_ISLNK(sb2.st_mode) ||
  174. + sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
  175. + debug_return_bool(true);
  176. + }
  177. +
  178. + debug_return_bool(false);
  179. +}
  180. +
  181. +static int
  182. +sudo_edit_openat_nofollow(char *path, int oflags, mode_t mode)
  183. +{
  184. + struct stat sb1, sb2;
  185. + int fd;
  186. + debug_decl(sudo_edit_openat_nofollow, SUDO_DEBUG_EDIT)
  187. +
  188. + fd = openat(dfd, path, oflags, mode);
  189. + if (fd == -1)
  190. + debug_return_int(-1);
  191. +
  192. + if (sudo_edit_is_symlink(fd, path)) {
  193. + close(fd);
  194. + fd = -1;
  195. + errno = ELOOP;
  196. + }
  197. +
  198. + debug_return_int(fd);
  199. +}
  200. +#endif /* O_NOFOLLOW */
  201. +
  202. /*
  203. * Returns true if the directory described by sb is writable
  204. * by the user. We treat directories with the sticky bit as
  205. @@ -245,49 +305,94 @@
  206. debug_return_bool(false);
  207. }
  208. +/*
  209. + * Directory open flags for use with openat(2) and fstat(2).
  210. + * Use O_PATH and O_DIRECTORY where possible.
  211. + */
  212. +#if defined(O_PATH) && defined(O_DIRECTORY)
  213. +# define DIR_OPEN_FLAGS (O_PATH|O_DIRECTORY)
  214. +#elif defined(O_PATH) && !defined(O_DIRECTORY)
  215. +# define DIR_OPEN_FLAGS O_PATH
  216. +#elif !defined(O_PATH) && defined(O_DIRECTORY)
  217. +# define DIR_OPEN_FLAGS (O_RDONLY|O_DIRECTORY)
  218. +#else
  219. +# define DIR_OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
  220. +#endif
  221. +
  222. static int
  223. sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode)
  224. {
  225. - char *base, *dir;
  226. + int dfd, fd, dflags = DIR_OPEN_FLAGS;
  227. +#if defined(__linux__) && defined(O_PATH)
  228. + char *opath = path;
  229. +#endif
  230. + bool is_writable;
  231. struct stat sb;
  232. - int dfd, fd;
  233. debug_decl(sudo_edit_open_nonwritable, SUDO_DEBUG_EDIT)
  234. - base = strrchr(path, '/');
  235. - if (base != NULL) {
  236. - *base++ = '\0';
  237. - dir = path;
  238. +#if defined(__linux__) && defined(O_PATH)
  239. +restart:
  240. +#endif
  241. + if (path[0] == '/') {
  242. + dfd = open("/", dflags);
  243. + path++;
  244. } else {
  245. - base = path;
  246. - dir = ".";
  247. + dfd = open(".", dflags);
  248. + if (path[0] == '.' && path[1] == '/')
  249. + path += 2;
  250. }
  251. -#ifdef O_PATH
  252. - if ((dfd = open(dir, O_PATH)) != -1) {
  253. - /* Linux kernels < 3.6 can't do fstat on O_PATH fds. */
  254. - if (fstat(dfd, &sb) == -1) {
  255. - close(dfd);
  256. - dfd = open(dir, O_RDONLY);
  257. - if (fstat(dfd, &sb) == -1) {
  258. - close(dfd);
  259. - dfd = -1;
  260. - }
  261. - }
  262. - }
  263. -#else
  264. - if ((dfd = open(dir, O_RDONLY)) != -1) {
  265. - if (fstat(dfd, &sb) == -1) {
  266. - close(dfd);
  267. - dfd = -1;
  268. - }
  269. - }
  270. -#endif
  271. - if (base != path)
  272. - base[-1] = '/'; /* restore path */
  273. if (dfd == -1)
  274. debug_return_int(-1);
  275. - if (dir_is_writable(&sb, user_details.uid, user_details.gid,
  276. - user_details.ngroups, user_details.groups)) {
  277. + for (;;) {
  278. + char *slash;
  279. + int subdfd;
  280. +
  281. + /*
  282. + * Look up one component at a time, avoiding symbolic links in
  283. + * writable directories.
  284. + */
  285. + if (fstat(dfd, &sb) == -1) {
  286. + close(dfd);
  287. +#if defined(__linux__) && defined(O_PATH)
  288. + /* Linux prior to 3.6 can't fstat an O_PATH fd */
  289. + if (ISSET(dflags, O_PATH)) {
  290. + CLR(dflags, O_PATH);
  291. + path = opath;
  292. + goto restart;
  293. + }
  294. +#endif
  295. + debug_return_int(-1);
  296. + }
  297. +#ifndef O_DIRECTORY
  298. + if (!S_ISDIR(sb.st_mode)) {
  299. + close(dfd);
  300. + errno = ENOTDIR;
  301. + debug_return_int(-1);
  302. + }
  303. +#endif
  304. + is_writable = dir_is_writable(&sb, user_details.uid, user_details.gid,
  305. + user_details.ngroups, user_details.groups);
  306. +
  307. + while (path[0] == '/')
  308. + path++;
  309. + slash = strchr(path, '/');
  310. + if (slash == NULL)
  311. + break;
  312. + *slash = '\0';
  313. + if (is_writable)
  314. + subdfd = sudo_edit_openat_nofollow(dfd, path, dflags, 0);
  315. + else
  316. + subdfd = openat(dfd, path, dflags, 0);
  317. + *slash = '/'; /* restore path */
  318. + close(dfd);
  319. + if (subdfd == -1)
  320. + debug_return_int(-1);
  321. + path = slash + 1;
  322. + dfd = subdfd;
  323. + }
  324. +
  325. + if (is_writable) {
  326. close(dfd);
  327. errno = EISDIR;
  328. debug_return_int(-1);
  329. @@ -332,27 +437,10 @@
  330. if (!ISSET(oflags, O_NONBLOCK))
  331. (void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
  332. - /*
  333. - * Treat [fl]stat() failure like an open() failure.
  334. - */
  335. - if (fstat(fd, &sb1) == -1 || lstat(path, &sb2) == -1) {
  336. - const int serrno = errno;
  337. + if (!ISSET(sflags, CD_SUDOEDIT_FOLLOW) && sudo_edit_is_symlink(fd, path)) {
  338. close(fd);
  339. - errno = serrno;
  340. - debug_return_int(-1);
  341. - }
  342. -
  343. - /*
  344. - * Make sure we did not open a link and that what we opened
  345. - * matches what is currently on the file system.
  346. - */
  347. - if (!ISSET(sflags, CD_SUDOEDIT_FOLLOW)) {
  348. - if (S_ISLNK(sb2.st_mode) ||
  349. - sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
  350. - close(fd);
  351. - errno = ELOOP;
  352. - debug_return_int(-1);
  353. - }
  354. + fd = -1;
  355. + errno = ELOOP;
  356. }
  357. debug_return_int(fd);