From 16aa6491177c1f88ccc20597a9b649ce5eebea67 Mon Sep 17 00:00:00 2001 From: Mark H Weaver Date: Tue, 12 Jan 2016 11:21:51 -0500 Subject: gnu: sudo: Add fix for CVE-2015-5602. * gnu/packages/patches/sudo-CVE-2015-5602.patch: New file. * gnu-system.am (dist_patch_DATA): Add it. * gnu/packages/admin.scm (sudo)[source]: Add patch. --- gnu-system.am | 1 + gnu/packages/admin.scm | 3 +- gnu/packages/patches/sudo-CVE-2015-5602.patch | 372 ++++++++++++++++++++++++++ 3 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 gnu/packages/patches/sudo-CVE-2015-5602.patch diff --git a/gnu-system.am b/gnu-system.am index 7105e13219..45487a466a 100644 --- a/gnu-system.am +++ b/gnu-system.am @@ -663,6 +663,7 @@ dist_patch_DATA = \ gnu/packages/patches/slim-config.patch \ gnu/packages/patches/slim-sigusr1.patch \ gnu/packages/patches/soprano-find-clucene.patch \ + gnu/packages/patches/sudo-CVE-2015-5602.patch \ gnu/packages/patches/superlu-dist-scotchmetis.patch \ gnu/packages/patches/synfig-build-fix.patch \ gnu/packages/patches/tar-d_ino_in_dirent-fix.patch \ diff --git a/gnu/packages/admin.scm b/gnu/packages/admin.scm index fbdc26d9ee..4d6bc70f82 100644 --- a/gnu/packages/admin.scm +++ b/gnu/packages/admin.scm @@ -700,7 +700,8 @@ system administrator.") version ".tar.gz"))) (sha256 (base32 - "0263gi6i19fyzzc488n0qw3m518i39f6a7qmrfvahk9j10bkh5j3")))) + "0263gi6i19fyzzc488n0qw3m518i39f6a7qmrfvahk9j10bkh5j3")) + (patches (list (search-patch "sudo-CVE-2015-5602.patch"))))) (build-system gnu-build-system) (arguments `(#:configure-flags 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 + +# HG changeset patch +# User Todd C. Miller +# 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); + -- cgit v1.2.3