aboutsummaryrefslogtreecommitdiff
path: root/gnu/packages/patches/sudo-CVE-2015-5602.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/packages/patches/sudo-CVE-2015-5602.patch')
-rw-r--r--gnu/packages/patches/sudo-CVE-2015-5602.patch372
1 files changed, 372 insertions, 0 deletions
diff --git a/gnu/packages/patches/sudo-CVE-2015-5602.patch b/gnu/packages/patches/sudo-CVE-2015-5602.patch
new file mode 100644
index 0000000000..36c90fbee7
--- /dev/null
+++ b/gnu/packages/patches/sudo-CVE-2015-5602.patch
@@ -0,0 +1,372 @@
+Based on the patch from https://www.sudo.ws/repos/sudo/raw-rev/c2e36a80a279
+Backported to 1.8.15 by Mark H Weaver <mhw@netris.org>
+
+# HG changeset patch
+# User Todd C. Miller <Todd.Miller@courtesan.com>
+# Date 1452475889 25200
+# Node ID c2e36a80a27927c32cba55afae78b8dc830cddc3
+# Parent 94ffd6b18431fa4b9ed0a0c3f0b7b9582a4f6bde
+Rewritten sudoedit_checkdir support that checks all the dirs in the
+path and refuses to follow symlinks in writable directories.
+This is a better fix for CVE-2015-5602.
+Adapted from a diff by Ben Hutchings. Bug #707
+
+diff -r 94ffd6b18431 -r c2e36a80a279 doc/CONTRIBUTORS
+--- a/doc/CONTRIBUTORS Mon Jan 04 10:47:11 2016 -0700
++++ b/doc/CONTRIBUTORS Sun Jan 10 18:31:29 2016 -0700
+@@ -58,6 +58,7 @@
+ Holloway, Nick
+ Hoover, Adam
+ Hunter, Michael T.
++ Hutchings, Ben
+ Irrgang, Eric
+ Jackson, Brian
+ Jackson, John R.
+diff -r 94ffd6b18431 -r c2e36a80a279 doc/UPGRADE
+--- a/doc/UPGRADE Mon Jan 04 10:47:11 2016 -0700
++++ b/doc/UPGRADE Sun Jan 10 18:31:29 2016 -0700
+@@ -1,6 +1,15 @@
+ Notes on upgrading from an older release
+ ========================================
+
++o Upgrading from a version prior to the post-1.8.15 fix for CVE-2015-5602.
++
++ The meaning of the sudoedit_checkdir sudoers option has changed.
++ Previously, it would only check the parent directory
++ of the file to be edited. After the CVE fix, all directories
++ in the path to be edited are checked and sudoedit will refuse
++ to follow a symbolic link in a directory that is writable by
++ the invoking user.
++
+ o Upgrading from a version prior to 1.8.15:
+
+ Prior to version 1.8.15, when env_reset was enabled (the default)
+diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.cat
+--- a/doc/sudoers.cat Mon Jan 04 10:47:11 2016 -0700
++++ b/doc/sudoers.cat Sun Jan 10 18:31:29 2016 -0700
+@@ -1275,12 +1275,15 @@
+ system call. This flag is _o_f_f by default.
+
+ sudoedit_checkdir
+- If set, ssuuddooeeddiitt will refuse to edit files located in a
+- directory that is writable by the invoking user unless
+- it is run by root. On many systems, this option
+- requires that the parent directory of the file to be
+- edited be readable by the target user. This flag is
+- _o_f_f by default.
++ If set, ssuuddooeeddiitt will check directories in the path to
++ be edited for writability by the invoking user.
++ Symbolic links will not be followed in writable
++ directories and ssuuddooeeddiitt will also refuse to edit a
++ file located in a writable directory. Theses
++ restrictions are not enforced when ssuuddooeeddiitt is invoked
++ as root. On many systems, this option requires that
++ all directories in the path to be edited be readable by
++ the target user. This flag is _o_f_f by default.
+
+ sudoedit_follow By default, ssuuddooeeddiitt will not follow symbolic links
+ when opening files. The _s_u_d_o_e_d_i_t___f_o_l_l_o_w option can be
+diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.man.in
+--- a/doc/sudoers.man.in Mon Jan 04 10:47:11 2016 -0700
++++ b/doc/sudoers.man.in Sun Jan 10 18:31:29 2016 -0700
+@@ -2715,10 +2715,16 @@
+ .br
+ If set,
+ \fBsudoedit\fR
+-will refuse to edit files located in a directory that is writable
+-by the invoking user unless it is run by root.
+-On many systems, this option requires that the parent directory
+-of the file to be edited be readable by the target user.
++will check directories in the path to be edited for writability
++by the invoking user.
++Symbolic links will not be followed in writable directories and
++\fBsudoedit\fR
++will also refuse to edit a file located in a writable directory.
++Theses restrictions are not enforced when
++\fBsudoedit\fR
++is invoked as root.
++On many systems, this option requires that all directories
++in the path to be edited be readable by the target user.
+ This flag is
+ \fIoff\fR
+ by default.
+diff -r 94ffd6b18431 -r c2e36a80a279 doc/sudoers.mdoc.in
+--- a/doc/sudoers.mdoc.in Mon Jan 04 10:47:11 2016 -0700
++++ b/doc/sudoers.mdoc.in Sun Jan 10 18:31:29 2016 -0700
+@@ -2549,10 +2549,16 @@
+ .It sudoedit_checkdir
+ If set,
+ .Nm sudoedit
+-will refuse to edit files located in a directory that is writable
+-by the invoking user unless it is run by root.
+-On many systems, this option requires that the parent directory
+-of the file to be edited be readable by the target user.
++will check directories in the path to be edited for writability
++by the invoking user.
++Symbolic links will not be followed in writable directories and
++.Nm sudoedit
++will also refuse to edit a file located in a writable directory.
++Theses restrictions are not enforced when
++.Nm sudoedit
++is invoked as root.
++On many systems, this option requires that all directories
++in the path to be edited be readable by the target user.
+ This flag is
+ .Em off
+ by default.
+diff -r 94ffd6b18431 -r c2e36a80a279 include/sudo_compat.h
+--- a/include/sudo_compat.h Mon Jan 04 10:47:11 2016 -0700
++++ b/include/sudo_compat.h Sun Jan 10 18:31:29 2016 -0700
+@@ -182,6 +182,8 @@
+ # ifndef UTIME_NOW
+ # define UTIME_NOW -2L
+ # endif
++#endif
++#if !defined(HAVE_OPENAT) || (!defined(HAVE_FUTIMENS) && !defined(HAVE_UTIMENSAT))
+ # ifndef AT_FDCWD
+ # define AT_FDCWD -100
+ # endif
+diff -r 94ffd6b18431 -r c2e36a80a279 src/sudo_edit.c
+--- a/src/sudo_edit.c Mon Jan 04 10:47:11 2016 -0700
++++ b/src/sudo_edit.c Sun Jan 10 18:31:29 2016 -0700
+@@ -179,13 +179,15 @@
+ }
+
+ #ifndef HAVE_OPENAT
+-/* This does not support AT_FDCWD... */
+ static int
+ sudo_openat(int dfd, const char *path, int flags, mode_t mode)
+ {
+ int fd, odfd;
+ debug_decl(sudo_openat, SUDO_DEBUG_EDIT)
+
++ if (dfd == AT_FDCWD)
++ debug_return_int(open(path, flags, mode));
++
+ /* Save cwd */
+ if ((odfd = open(".", O_RDONLY)) == -1)
+ debug_return_int(-1);
+@@ -207,6 +209,64 @@
+ #define openat sudo_openat
+ #endif /* HAVE_OPENAT */
+
++#ifdef O_NOFOLLOW
++static int
++sudo_edit_openat_nofollow(int dfd, char *path, int oflags, mode_t mode)
++{
++ debug_decl(sudo_edit_open_nofollow, SUDO_DEBUG_EDIT)
++
++ debug_return_int(openat(dfd, path, oflags|O_NOFOLLOW, mode));
++}
++#else
++/*
++ * Returns true if fd and path don't match or path is a symlink.
++ * Used on older systems without O_NOFOLLOW.
++ */
++static bool
++sudo_edit_is_symlink(int fd, char *path)
++{
++ struct stat sb1, sb2;
++ debug_decl(sudo_edit_is_symlink, SUDO_DEBUG_EDIT)
++
++ /*
++ * Treat [fl]stat() failure like there was a symlink.
++ */
++ if (fstat(fd, &sb1) == -1 || lstat(path, &sb2) == -1)
++ debug_return_bool(true);
++
++ /*
++ * Make sure we did not open a link and that what we opened
++ * matches what is currently on the file system.
++ */
++ if (S_ISLNK(sb2.st_mode) ||
++ sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
++ debug_return_bool(true);
++ }
++
++ debug_return_bool(false);
++}
++
++static int
++sudo_edit_openat_nofollow(char *path, int oflags, mode_t mode)
++{
++ struct stat sb1, sb2;
++ int fd;
++ debug_decl(sudo_edit_openat_nofollow, SUDO_DEBUG_EDIT)
++
++ fd = openat(dfd, path, oflags, mode);
++ if (fd == -1)
++ debug_return_int(-1);
++
++ if (sudo_edit_is_symlink(fd, path)) {
++ close(fd);
++ fd = -1;
++ errno = ELOOP;
++ }
++
++ debug_return_int(fd);
++}
++#endif /* O_NOFOLLOW */
++
+ /*
+ * Returns true if the directory described by sb is writable
+ * by the user. We treat directories with the sticky bit as
+@@ -245,49 +305,94 @@
+ debug_return_bool(false);
+ }
+
++/*
++ * Directory open flags for use with openat(2) and fstat(2).
++ * Use O_PATH and O_DIRECTORY where possible.
++ */
++#if defined(O_PATH) && defined(O_DIRECTORY)
++# define DIR_OPEN_FLAGS (O_PATH|O_DIRECTORY)
++#elif defined(O_PATH) && !defined(O_DIRECTORY)
++# define DIR_OPEN_FLAGS O_PATH
++#elif !defined(O_PATH) && defined(O_DIRECTORY)
++# define DIR_OPEN_FLAGS (O_RDONLY|O_DIRECTORY)
++#else
++# define DIR_OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
++#endif
++
+ static int
+ sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode)
+ {
+- char *base, *dir;
++ int dfd, fd, dflags = DIR_OPEN_FLAGS;
++#if defined(__linux__) && defined(O_PATH)
++ char *opath = path;
++#endif
++ bool is_writable;
+ struct stat sb;
+- int dfd, fd;
+ debug_decl(sudo_edit_open_nonwritable, SUDO_DEBUG_EDIT)
+
+- base = strrchr(path, '/');
+- if (base != NULL) {
+- *base++ = '\0';
+- dir = path;
++#if defined(__linux__) && defined(O_PATH)
++restart:
++#endif
++ if (path[0] == '/') {
++ dfd = open("/", dflags);
++ path++;
+ } else {
+- base = path;
+- dir = ".";
++ dfd = open(".", dflags);
++ if (path[0] == '.' && path[1] == '/')
++ path += 2;
+ }
+-#ifdef O_PATH
+- if ((dfd = open(dir, O_PATH)) != -1) {
+- /* Linux kernels < 3.6 can't do fstat on O_PATH fds. */
+- if (fstat(dfd, &sb) == -1) {
+- close(dfd);
+- dfd = open(dir, O_RDONLY);
+- if (fstat(dfd, &sb) == -1) {
+- close(dfd);
+- dfd = -1;
+- }
+- }
+- }
+-#else
+- if ((dfd = open(dir, O_RDONLY)) != -1) {
+- if (fstat(dfd, &sb) == -1) {
+- close(dfd);
+- dfd = -1;
+- }
+- }
+-#endif
+- if (base != path)
+- base[-1] = '/'; /* restore path */
+ if (dfd == -1)
+ debug_return_int(-1);
+
+- if (dir_is_writable(&sb, user_details.uid, user_details.gid,
+- user_details.ngroups, user_details.groups)) {
++ for (;;) {
++ char *slash;
++ int subdfd;
++
++ /*
++ * Look up one component at a time, avoiding symbolic links in
++ * writable directories.
++ */
++ if (fstat(dfd, &sb) == -1) {
++ close(dfd);
++#if defined(__linux__) && defined(O_PATH)
++ /* Linux prior to 3.6 can't fstat an O_PATH fd */
++ if (ISSET(dflags, O_PATH)) {
++ CLR(dflags, O_PATH);
++ path = opath;
++ goto restart;
++ }
++#endif
++ debug_return_int(-1);
++ }
++#ifndef O_DIRECTORY
++ if (!S_ISDIR(sb.st_mode)) {
++ close(dfd);
++ errno = ENOTDIR;
++ debug_return_int(-1);
++ }
++#endif
++ is_writable = dir_is_writable(&sb, user_details.uid, user_details.gid,
++ user_details.ngroups, user_details.groups);
++
++ while (path[0] == '/')
++ path++;
++ slash = strchr(path, '/');
++ if (slash == NULL)
++ break;
++ *slash = '\0';
++ if (is_writable)
++ subdfd = sudo_edit_openat_nofollow(dfd, path, dflags, 0);
++ else
++ subdfd = openat(dfd, path, dflags, 0);
++ *slash = '/'; /* restore path */
++ close(dfd);
++ if (subdfd == -1)
++ debug_return_int(-1);
++ path = slash + 1;
++ dfd = subdfd;
++ }
++
++ if (is_writable) {
+ close(dfd);
+ errno = EISDIR;
+ debug_return_int(-1);
+@@ -332,27 +437,10 @@
+ if (!ISSET(oflags, O_NONBLOCK))
+ (void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
+
+- /*
+- * Treat [fl]stat() failure like an open() failure.
+- */
+- if (fstat(fd, &sb1) == -1 || lstat(path, &sb2) == -1) {
+- const int serrno = errno;
++ if (!ISSET(sflags, CD_SUDOEDIT_FOLLOW) && sudo_edit_is_symlink(fd, path)) {
+ close(fd);
+- errno = serrno;
+- debug_return_int(-1);
+- }
+-
+- /*
+- * Make sure we did not open a link and that what we opened
+- * matches what is currently on the file system.
+- */
+- if (!ISSET(sflags, CD_SUDOEDIT_FOLLOW)) {
+- if (S_ISLNK(sb2.st_mode) ||
+- sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
+- close(fd);
+- errno = ELOOP;
+- debug_return_int(-1);
+- }
++ fd = -1;
++ errno = ELOOP;
+ }
+
+ debug_return_int(fd);
+