diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23239.patch b/SOURCES/sudo-1.9.5-CVE-2021-23239.patch
new file mode 100644
index 0000000..2ff50fd
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23239.patch
@@ -0,0 +1,61 @@
+From db1f27c0350e9e437c93780ffe88648ae1984467 Mon Sep 17 00:00:00 2001
+From: "Todd C. Miller" <Todd.Miller@sudo.ws>
+Date: Wed, 6 Jan 2021 10:16:00 -0700
+Subject: [PATCH] Fix potential directory existing info leak in sudoedit. When
+ creating a new file, sudoedit checks to make sure the parent directory exists
+ so it can provide the user with a sensible error message.  However, this
+ could be used to test for the existence of directories not normally
+ accessible to the user by pointing to them with a symbolic link when the
+ parent directory is controlled by the user.  Problem reported by Matthias
+ Gerstner of SUSE.
+
+---
+ src/sudo_edit.c | 29 ++++++++++++++++++++++++-----
+ 1 file changed, 24 insertions(+), 5 deletions(-)
+
+diff --git a/src/sudo_edit.c b/src/sudo_edit.c
+index 82e04a71b..5502b7bd9 100644
+--- a/src/sudo_edit.c
++++ b/src/sudo_edit.c
+@@ -541,14 +541,33 @@ sudo_edit_create_tfiles(struct command_details *command_details,
+ 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
+ 	if (ofd != -1 || errno == ENOENT) {
+ 	    if (ofd == -1) {
+-		/* New file, verify parent dir exists unless in cwd. */
++		/*
++		 * New file, verify parent dir exists unless in cwd.
++		 * This fails early so the user knows ahead of time if the
++		 * edit won't succeed.  Additional checks are performed
++		 * when copying the temporary file back to the origin.
++		 */
+ 		char *slash = strrchr(files[i], '/');
+ 		if (slash != NULL && slash != files[i]) {
+-		    int serrno = errno;
++		    const int sflags = command_details->flags;
++		    const int serrno = errno;
++		    int dfd;
++
++		    /*
++		     * The parent directory is allowed to be a symbolic
++		     * link as long as *its* parent is not writable.
++		     */
+ 		    *slash = '\0';
+-		    if (stat(files[i], &sb) == 0 && S_ISDIR(sb.st_mode)) {
+-			memset(&sb, 0, sizeof(sb));
+-			rc = 0;
++		    SET(command_details->flags, CD_SUDOEDIT_FOLLOW);
++		    dfd = sudo_edit_open(files[i], DIR_OPEN_FLAGS,
++			S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
++		    command_details->flags = sflags;
++		    if (dfd != -1) {
++			if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) {
++			    memset(&sb, 0, sizeof(sb));
++			    rc = 0;
++			}
++			close(dfd);
+ 		    }
+ 		    *slash = '/';
+ 		    errno = serrno;
+-- 
+2.26.2
+
diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23240-1.patch b/SOURCES/sudo-1.9.5-CVE-2021-23240-1.patch
new file mode 100644
index 0000000..02efa86
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23240-1.patch
@@ -0,0 +1,158 @@
+From adb4360c40df99238c17c3ecedcb1d32d76e2b2e Mon Sep 17 00:00:00 2001
+From: "Todd C. Miller" <Todd.Miller@sudo.ws>
+Date: Fri, 17 Apr 2020 19:08:56 -0600
+Subject: [PATCH] Extend the original file before to the new size before
+ updating it. Instead of opening the original file for writing w/ tuncation,
+ we first extend the file with zeroes (by writing, not seeking), then
+ overwrite it.  This should allow sudo to fail early if the disk is out of
+ space before it overwrites the original file.
+
+---
+ src/sudo_edit.c | 93 ++++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 77 insertions(+), 16 deletions(-)
+
+diff --git a/src/sudo_edit.c b/src/sudo_edit.c
+index 28f6c6100..d99a5658a 100644
+--- a/src/sudo_edit.c
++++ b/src/sudo_edit.c
+@@ -1,7 +1,7 @@
+ /*
+  * SPDX-License-Identifier: ISC
+  *
+- * Copyright (c) 2004-2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
++ * Copyright (c) 2004-2008, 2010-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+  *
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -650,6 +650,51 @@ sudo_edit_create_tfiles(struct command_details *command_details,
+     debug_return_int(j);
+ }
+ 
++/*
++ * Extend the given fd to the specified size in bytes.
++ * We do this to allocate disk space up-front before overwriting
++ * the original file with the temporary.  Otherwise, we could
++ * we run out of disk space after truncating the original file.
++ */
++static int
++sudo_edit_extend_file(int fd, off_t new_size)
++{
++    off_t old_size, size;
++    ssize_t nwritten;
++    char zeroes[1024] = { '\0' };
++    debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
++
++    if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
++	sudo_warn("lseek");
++	debug_return_int(-1);
++    }
++    sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
++	__func__, (long long)old_size, (long long)new_size);
++
++    for (size = old_size; size < new_size; size += nwritten) {
++	size_t len = new_size - size;
++	if (len > sizeof(zeroes))
++	    len = sizeof(zeroes);
++	nwritten = write(fd, zeroes, len);
++	if (nwritten == -1) {
++	    int serrno = errno;
++	    if (ftruncate(fd, old_size) == -1) {
++		sudo_debug_printf(
++		    SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
++		    "unable to truncate to %lld", (long long)old_size);
++	    }
++	    errno = serrno;
++	    debug_return_int(-1);
++	}
++    }
++    if (lseek(fd, 0, SEEK_SET) == -1) {
++	sudo_warn("lseek");
++	debug_return_int(-1);
++    }
++
++    debug_return_int(0);
++}
++
+ /*
+  * Copy the temporary files specified in tf to the originals.
+  * Returns the number of copy errors or 0 if completely successful.
+@@ -708,38 +753,53 @@ sudo_edit_copy_tfiles(struct command_details *command_details,
+ 	switch_user(command_details->euid, command_details->egid,
+ 	    command_details->ngroups, command_details->groups);
+ 	oldmask = umask(command_details->umask);
+-	ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT,
++	ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_CREAT,
+ 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
+ 	umask(oldmask);
+ 	switch_user(ROOT_UID, user_details.egid,
+ 	    user_details.ngroups, user_details.groups);
+-	if (ofd == -1) {
+-	    sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+-	    sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+-	    close(tfd);
+-	    errors++;
+-	    continue;
++	if (ofd == -1)
++	    goto write_error;
++	/* Extend the file to the new size if larger before copying. */
++	if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
++	    if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
++		goto write_error;
+ 	}
++	/* Overwrite the old file with the new contents. */
+ 	while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
+-	    if ((nwritten = write(ofd, buf, nread)) != nread) {
++	    ssize_t off = 0;
++	    do {
++		nwritten = write(ofd, buf + off, nread - off);
+ 		if (nwritten == -1)
+-		    sudo_warn("%s", tf[i].ofile);
+-		else
+-		    sudo_warnx(U_("%s: short write"), tf[i].ofile);
+-		break;
+-	    }
++		    goto write_error;
++		off += nwritten;
++	    } while (nread > off);
+ 	}
+ 	if (nread == 0) {
+-	    /* success, got EOF */
++	    /* success, read to EOF */
++	    if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
++		/* We don't open with O_TRUNC so must truncate manually. */
++		if (ftruncate(ofd, sb.st_size)  == -1) {
++		    sudo_debug_printf(
++			SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
++			"unable to truncate %s to %lld", tf[i].ofile,
++			(long long)sb.st_size);
++		    goto write_error;
++		}
++	    }
+ 	    unlink(tf[i].tfile);
+ 	} else if (nread < 0) {
+ 	    sudo_warn(U_("unable to read temporary file"));
+ 	    sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
++	    errors++;
+ 	} else {
++write_error:
+ 	    sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+ 	    sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
++	    errors++;
+ 	}
+-	close(ofd);
++	if (ofd != -1)
++	    close(ofd);
+ 	close(tfd);
+     }
+     debug_return_int(errors);
+@@ -1065,6 +1125,7 @@ cleanup:
+ 	for (i = 0; i < nfiles; i++) {
+ 	    if (tf[i].tfile != NULL)
+ 		unlink(tf[i].tfile);
++	    free(tf[i].tfile);
+ 	}
+     }
+     free(tf);
+-- 
+2.26.2
+
diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23240-2.patch b/SOURCES/sudo-1.9.5-CVE-2021-23240-2.patch
new file mode 100644
index 0000000..1b40774
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23240-2.patch
@@ -0,0 +1,431 @@
+diff -up ./src/copy_file.c.symbolic-link-attack-2 ./src/copy_file.c
+--- ./src/copy_file.c.symbolic-link-attack-2	2021-02-02 15:31:20.555340446 +0100
++++ ./src/copy_file.c	2021-02-02 15:31:20.555340446 +0100
+@@ -0,0 +1,128 @@
++/*
++ * SPDX-License-Identifier: ISC
++ *
++ * Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/*
++ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
++ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
++ */
++
++#include <config.h>
++
++#include <sys/types.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++
++#include "sudo.h"
++
++/*
++ * Extend the given fd to the specified size in bytes.
++ * We do this to allocate disk space up-front before overwriting
++ * the original file with the temporary.  Otherwise, we could
++ * we run out of disk space after truncating the original file.
++ */
++static int
++sudo_extend_file(int fd, const char *name, off_t new_size)
++{
++    off_t old_size, size;
++    ssize_t nwritten;
++    char zeroes[BUFSIZ] = { '\0' };
++    debug_decl(sudo_extend_file, SUDO_DEBUG_UTIL);
++
++    if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
++	sudo_warn("lseek");
++	debug_return_int(-1);
++    }
++    sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending %s from %lld to %lld",
++	__func__, name, (long long)old_size, (long long)new_size);
++
++    for (size = old_size; size < new_size; size += nwritten) {
++	size_t len = new_size - size;
++	if (len > sizeof(zeroes))
++	    len = sizeof(zeroes);
++	nwritten = write(fd, zeroes, len);
++	if (nwritten == -1) {
++	    int serrno = errno;
++	    if (ftruncate(fd, old_size) == -1) {
++		sudo_debug_printf(
++		    SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
++		    "unable to truncate %s to %lld", name, (long long)old_size);
++	    }
++	    errno = serrno;
++	    debug_return_int(-1);
++	}
++    }
++    if (lseek(fd, 0, SEEK_SET) == -1) {
++	sudo_warn("lseek");
++	debug_return_int(-1);
++    }
++
++    debug_return_int(0);
++}
++
++/*
++ * Copy the contents of src_fd into dst_fd.
++ * Returns 0 on success or -1 on error.
++ */
++int
++sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst,
++    int dst_fd, off_t dst_len)
++{
++    char buf[BUFSIZ];
++    ssize_t nwritten, nread;
++    debug_decl(sudo_copy_file, SUDO_DEBUG_UTIL);
++
++    /* Extend the file to the new size if larger before copying. */
++    if (dst_len > 0 && src_len > dst_len) {
++	if (sudo_extend_file(dst_fd, dst, src_len) == -1)
++	    goto write_error;
++    }
++
++    /* Overwrite the old file with the new contents. */
++    while ((nread = read(src_fd, buf, sizeof(buf))) > 0) {
++	ssize_t off = 0;
++	do {
++	    nwritten = write(dst_fd, buf + off, nread - off);
++	    if (nwritten == -1)
++		goto write_error;
++	    off += nwritten;
++	} while (nread > off);
++    }
++    if (nread == 0) {
++	/* success, read to EOF */
++	if (src_len < dst_len) {
++	    /* We don't open with O_TRUNC so must truncate manually. */
++	    if (ftruncate(dst_fd, src_len) == -1) {
++		sudo_debug_printf(
++		    SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
++		    "unable to truncate %s to %lld", dst, (long long)src_len);
++		goto write_error;
++	    }
++	}
++	debug_return_int(0);
++    } else if (nread < 0) {
++	sudo_warn(U_("unable to read from %s"), src);
++	debug_return_int(-1);
++    } else {
++write_error:
++	sudo_warn(U_("unable to write to %s"), dst);
++	debug_return_int(-1);
++    }
++}
+diff -up ./src/Makefile.in.symbolic-link-attack-2 ./src/Makefile.in
+--- ./src/Makefile.in.symbolic-link-attack-2	2019-10-28 13:28:54.000000000 +0100
++++ ./src/Makefile.in	2021-02-02 15:31:20.555340446 +0100
+@@ -120,16 +120,17 @@ SHELL = @SHELL@
+ 
+ PROGS = @PROGS@
+ 
+-OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_monitor.o \
+-       exec_nopty.o exec_pty.o get_pty.o hooks.o limits.o load_plugins.o \
+-       net_ifs.o parse_args.o preserve_fds.o signal.o sudo.o sudo_edit.o \
+-       tcsetpgrp_nobg.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
++OBJS = conversation.o copy_file.o env_hooks.o exec.o exec_common.o \
++       exec_monitor.o exec_nopty.o exec_pty.o get_pty.o hooks.o \
++       limits.o load_plugins.o net_ifs.o parse_args.o preserve_fds.o \
++       signal.o sudo.o sudo_edit.o tcsetpgrp_nobg.o tgetpass.o \
++       ttyname.o utmp.o @SUDO_OBJS@
+ 
+ IOBJS = $(OBJS:.o=.i) sesh.i
+ 
+ POBJS = $(IOBJS:.i=.plog)
+ 
+-SESH_OBJS = sesh.o exec_common.o
++SESH_OBJS = copy_file.o exec_common.o sesh.o
+ 
+ CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o
+ 
+@@ -335,6 +336,22 @@ conversation.i: $(srcdir)/conversation.c
+ 	$(CC) -E -o $@ $(CPPFLAGS) $<
+ conversation.plog: conversation.i
+ 	rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/conversation.c --i-file $< --output-file $@
++copy_file.o: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
++             $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
++             $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
++             $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
++             $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
++             $(top_builddir)/config.h $(top_builddir)/pathnames.h
++	$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/copy_file.c
++copy_file.i: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
++             $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
++             $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
++             $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
++             $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
++             $(top_builddir)/config.h $(top_builddir)/pathnames.h
++	$(CC) -E -o $@ $(CPPFLAGS) $<
++copy_file.plog: copy_file.i
++	rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/copy_file.c --i-file $< --output-file $@
+ env_hooks.o: $(srcdir)/env_hooks.c $(incdir)/compat/stdbool.h \
+              $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+              $(incdir)/sudo_debug.h $(incdir)/sudo_dso.h \
+diff -up ./src/sesh.c.symbolic-link-attack-2 ./src/sesh.c
+--- ./src/sesh.c.symbolic-link-attack-2	2019-10-28 13:28:52.000000000 +0100
++++ ./src/sesh.c	2021-02-02 15:31:20.555340446 +0100
+@@ -1,7 +1,7 @@
+ /*
+  * SPDX-License-Identifier: ISC
+  *
+- * Copyright (c) 2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
++ * Copyright (c) 2008, 2010-2018, 2020 Todd C. Miller <Todd.Miller@sudo.ws>
+  *
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -182,7 +182,7 @@ sesh_sudoedit(int argc, char *argv[])
+      * so that it's ensured that the temporary files are
+      * created by us and that we are not opening any symlinks.
+      */
+-    oflags_dst = O_WRONLY|O_TRUNC|O_CREAT|(post ? follow : O_EXCL);
++    oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
+     for (i = 0; i < argc - 1; i += 2) {
+ 	const char *path_src = argv[i];
+ 	const char *path_dst = argv[i + 1];
+@@ -214,14 +214,29 @@ sesh_sudoedit(int argc, char *argv[])
+ 	}
+ 
+ 	if (fd_src != -1) {
+-	    while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
+-		if ((nwritten = write(fd_dst, buf, nread)) != nread) {
+-		    sudo_warn("%s", path_src);
+-		    if (post) {
+-			ret = SESH_ERR_SOME_FILES;
+-			goto nocleanup;
+-		    } else
+-			goto cleanup_0;
++	    off_t len_src = -1;
++	    off_t len_dst = -1;
++
++	    if (post) {
++		if (fstat(fd_src, &sb) != 0) {
++		    ret = SESH_ERR_SOME_FILES;
++		    goto nocleanup;
++		}
++		len_src = sb.st_size;
++		if (fstat(fd_dst, &sb) != 0) {
++		    ret = SESH_ERR_SOME_FILES;
++		    goto nocleanup;
++		}
++		len_dst = sb.st_size;
++	    }
++
++	    if (sudo_copy_file(path_src, fd_src, len_src, path_dst, fd_dst,
++		    len_dst) == -1) {
++		if (post) {
++		    ret = SESH_ERR_SOME_FILES;
++		    goto nocleanup;
++		} else {
++		    goto cleanup_0;
+ 		}
+ 	    }
+ 	}
+diff -up ./src/sudo_edit.c.symbolic-link-attack-2 ./src/sudo_edit.c
+--- ./src/sudo_edit.c.symbolic-link-attack-2	2021-02-02 15:31:20.554340459 +0100
++++ ./src/sudo_edit.c	2021-02-02 15:31:54.355884326 +0100
+@@ -42,7 +42,6 @@
+ #include <grp.h>
+ #include <pwd.h>
+ #include <signal.h>
+-#include <errno.h>
+ #include <fcntl.h>
+ 
+ #include "sudo.h"
+@@ -551,8 +550,6 @@ sudo_edit_create_tfiles(struct command_d
+     struct tempfile *tf, char *files[], int nfiles)
+ {
+     int i, j, tfd, ofd, rc;
+-    char buf[BUFSIZ];
+-    ssize_t nwritten, nread;
+     struct timespec times[2];
+     struct stat sb;
+     debug_decl(sudo_edit_create_tfiles, SUDO_DEBUG_EDIT)
+@@ -648,18 +645,7 @@ sudo_edit_create_tfiles(struct command_d
+ 	    debug_return_int(-1);
+ 	}
+ 	if (ofd != -1) {
+-	    while ((nread = read(ofd, buf, sizeof(buf))) > 0) {
+-		if ((nwritten = write(tfd, buf, nread)) != nread) {
+-		    if (nwritten == -1)
+-			sudo_warn("%s", tf[j].tfile);
+-		    else
+-			sudo_warnx(U_("%s: short write"), tf[j].tfile);
+-		    break;
+-		}
+-	    }
+-	    if (nread != 0) {
+-		if (nread < 0)
+-		    sudo_warn("%s", files[i]);
++	    if (sudo_copy_file(tf[j].ofile, ofd, tf[j].osize, tf[j].tfile, tfd, -1) == -1) {
+ 		close(ofd);
+ 		close(tfd);
+ 		debug_return_int(-1);
+@@ -689,51 +675,6 @@ sudo_edit_create_tfiles(struct command_d
+ }
+ 
+ /*
+- * Extend the given fd to the specified size in bytes.
+- * We do this to allocate disk space up-front before overwriting
+- * the original file with the temporary.  Otherwise, we could
+- * we run out of disk space after truncating the original file.
+- */
+-static int
+-sudo_edit_extend_file(int fd, off_t new_size)
+-{
+-    off_t old_size, size;
+-    ssize_t nwritten;
+-    char zeroes[1024] = { '\0' };
+-    debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
+-
+-    if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
+-	sudo_warn("lseek");
+-	debug_return_int(-1);
+-    }
+-    sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
+-	__func__, (long long)old_size, (long long)new_size);
+-
+-    for (size = old_size; size < new_size; size += nwritten) {
+-	size_t len = new_size - size;
+-	if (len > sizeof(zeroes))
+-	    len = sizeof(zeroes);
+-	nwritten = write(fd, zeroes, len);
+-	if (nwritten == -1) {
+-	    int serrno = errno;
+-	    if (ftruncate(fd, old_size) == -1) {
+-		sudo_debug_printf(
+-		    SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+-		    "unable to truncate to %lld", (long long)old_size);
+-	    }
+-	    errno = serrno;
+-	    debug_return_int(-1);
+-	}
+-    }
+-    if (lseek(fd, 0, SEEK_SET) == -1) {
+-	sudo_warn("lseek");
+-	debug_return_int(-1);
+-    }
+-
+-    debug_return_int(0);
+-}
+-
+-/*
+  * Copy the temporary files specified in tf to the originals.
+  * Returns the number of copy errors or 0 if completely successful.
+  */
+@@ -741,9 +682,7 @@ static int
+ sudo_edit_copy_tfiles(struct command_details *command_details,
+     struct tempfile *tf, int nfiles, struct timespec *times)
+ {
+-    int i, tfd, ofd, rc, errors = 0;
+-    char buf[BUFSIZ];
+-    ssize_t nwritten, nread;
++    int i, tfd, ofd, errors = 0;
+     struct timespec ts;
+     struct stat sb;
+     mode_t oldmask;
+@@ -751,7 +690,7 @@ sudo_edit_copy_tfiles(struct command_det
+ 
+     /* Copy contents of temp files to real ones. */
+     for (i = 0; i < nfiles; i++) {
+-	rc = -1;
++	int rc = -1;
+ 	sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ 	    "seteuid(%u)", (unsigned int)user_details.uid);
+ 	if (seteuid(user_details.uid) != 0)
+@@ -764,8 +703,8 @@ sudo_edit_copy_tfiles(struct command_det
+ 	    "seteuid(%u)", ROOT_UID);
+ 	if (seteuid(ROOT_UID) != 0)
+ 	    sudo_fatal("seteuid(ROOT_UID)");
+-	if (rc || !S_ISREG(sb.st_mode)) {
+-	    if (rc)
++	if (rc == -1 || !S_ISREG(sb.st_mode)) {
++	    if (rc == -1)
+ 		sudo_warn("%s", tf[i].tfile);
+ 	    else
+ 		sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
+@@ -796,46 +735,19 @@ sudo_edit_copy_tfiles(struct command_det
+ 	umask(oldmask);
+ 	switch_user(ROOT_UID, user_details.egid,
+ 	    user_details.ngroups, user_details.groups);
+-	if (ofd == -1)
+-	    goto write_error;
+-	/* Extend the file to the new size if larger before copying. */
+-	if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
+-	    if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
+-		goto write_error;
++	if (ofd == -1) {
++	    sudo_warn(U_("unable to write to %s"), tf[i].ofile);
++	    goto bad;
+ 	}
++
+ 	/* Overwrite the old file with the new contents. */
+-	while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
+-	    ssize_t off = 0;
+-	    do {
+-		nwritten = write(ofd, buf + off, nread - off);
+-		if (nwritten == -1)
+-		    goto write_error;
+-		off += nwritten;
+-	    } while (nread > off);
+-	}
+-	if (nread == 0) {
+-	    /* success, read to EOF */
+-	    if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
+-		/* We don't open with O_TRUNC so must truncate manually. */
+-		if (ftruncate(ofd, sb.st_size)  == -1) {
+-		    sudo_debug_printf(
+-			SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+-			"unable to truncate %s to %lld", tf[i].ofile,
+-			(long long)sb.st_size);
+-		    goto write_error;
+-		}
+-	    }
+-	    unlink(tf[i].tfile);
+-	} else if (nread < 0) {
+-	    sudo_warn(U_("unable to read temporary file"));
+-	    sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+-	    errors++;
+-	} else {
+-write_error:
+-	    sudo_warn(U_("unable to write to %s"), tf[i].ofile);
++	if (sudo_copy_file(tf[i].tfile, tfd, sb.st_size, tf[i].ofile, ofd,
++	    tf[i].osize) == -1) {
++bad:
+ 	    sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+ 	    errors++;
+ 	}
++
+ 	if (ofd != -1)
+ 	    close(ofd);
+ 	close(tfd);
+diff -up ./src/sudo_exec.h.symbolic-link-attack-2 ./src/sudo_exec.h
+--- ./src/sudo_exec.h.symbolic-link-attack-2	2019-10-28 13:27:39.000000000 +0100
++++ ./src/sudo_exec.h	2021-02-02 15:31:20.556340432 +0100
+@@ -84,6 +84,9 @@
+ struct command_details;
+ struct command_status;
+ 
++/* copy_file.c */
++int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
++
+ /* exec.c */
+ void exec_cmnd(struct command_details *details, int errfd);
+ void terminate_command(pid_t pid, bool use_pgrp);
diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23240-3.patch b/SOURCES/sudo-1.9.5-CVE-2021-23240-3.patch
new file mode 100644
index 0000000..8e4ae34
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23240-3.patch
@@ -0,0 +1,345 @@
+diff -up ./src/exec_monitor.c.symbolic-link-attack-3 ./src/exec_monitor.c
+--- ./src/exec_monitor.c.symbolic-link-attack-3	2019-10-28 13:27:39.000000000 +0100
++++ ./src/exec_monitor.c	2021-02-02 17:11:32.382020407 +0100
+@@ -613,7 +613,7 @@ exec_monitor(struct command_details *det
+ #ifdef HAVE_SELINUX
+     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
+         if (selinux_setup(details->selinux_role, details->selinux_type,
+-            details->tty, io_fds[SFD_SLAVE]) == -1)
++            details->tty, io_fds[SFD_SLAVE], true) == -1)
+             goto bad;
+     }
+ #endif
+diff -up ./src/exec_nopty.c.symbolic-link-attack-3 ./src/exec_nopty.c
+--- ./src/exec_nopty.c.symbolic-link-attack-3	2019-10-28 13:27:39.000000000 +0100
++++ ./src/exec_nopty.c	2021-02-02 17:11:32.382020407 +0100
+@@ -381,7 +381,7 @@ exec_nopty(struct command_details *detai
+ #ifdef HAVE_SELINUX
+     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
+         if (selinux_setup(details->selinux_role, details->selinux_type,
+-		details->tty, -1) == -1) {
++		details->tty, -1, true) == -1) {
+ 	    cstat->type = CMD_ERRNO;
+ 	    cstat->val = errno;
+ 	    debug_return;
+diff -up ./src/selinux.c.symbolic-link-attack-3 ./src/selinux.c
+--- ./src/selinux.c.symbolic-link-attack-3	2019-10-28 13:27:39.000000000 +0100
++++ ./src/selinux.c	2021-02-02 17:11:32.382020407 +0100
+@@ -363,7 +363,7 @@ bad:
+  */
+ int
+ selinux_setup(const char *role, const char *type, const char *ttyn,
+-    int ptyfd)
++    int ptyfd, bool label_tty)
+ {
+     int ret = -1;
+     debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)
+@@ -392,7 +392,7 @@ selinux_setup(const char *role, const ch
+     sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__,
+ 	se_state.new_context);
+     
+-    if (relabel_tty(ttyn, ptyfd) == -1) {
++    if (label_tty && relabel_tty(ttyn, ptyfd) == -1) {
+ 	sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
+ 	goto done;
+     }
+@@ -408,6 +408,28 @@ done:
+     debug_return_int(ret);
+ }
+ 
++int
++selinux_setcon(void)
++{
++    debug_decl(selinux_setcon, SUDO_DEBUG_SELINUX);
++
++    if (setexeccon(se_state.new_context)) {
++	sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
++	if (se_state.enforcing)
++	    debug_return_int(-1);
++    }
++
++#ifdef HAVE_SETKEYCREATECON
++    if (setkeycreatecon(se_state.new_context)) {
++	sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
++	if (se_state.enforcing)
++	    debug_return_int(-1);
++    }
++#endif /* HAVE_SETKEYCREATECON */
++
++    debug_return_int(0);
++}
++
+ void
+ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
+     bool noexec)
+@@ -424,19 +446,9 @@ selinux_execve(int fd, const char *path,
+ 	debug_return;
+     }
+ 
+-    if (setexeccon(se_state.new_context)) {
+-	sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
+-	if (se_state.enforcing)
+-	    debug_return;
+-    }
+-
+-#ifdef HAVE_SETKEYCREATECON
+-    if (setkeycreatecon(se_state.new_context)) {
+-	sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
+-	if (se_state.enforcing)
+-	    debug_return;
+-    }
+-#endif /* HAVE_SETKEYCREATECON */
++    /* Set SELinux exec and keycreate contexts. */
++    if (selinux_setcon() == -1)
++	debug_return;
+ 
+     /*
+      * Build new argv with sesh as argv[0].
+diff -up ./src/sudo.c.symbolic-link-attack-3 ./src/sudo.c
+--- ./src/sudo.c.symbolic-link-attack-3	2021-02-02 17:12:32.773182386 +0100
++++ ./src/sudo.c	2021-02-02 17:12:48.510964009 +0100
+@@ -971,10 +971,6 @@ run_command(struct command_details *deta
+     case CMD_WSTATUS:
+ 	/* Command ran, exited or was killed. */
+ 	status = cstat.val;
+-#ifdef HAVE_SELINUX
+-	if (ISSET(details->flags, CD_SUDOEDIT_COPY))
+-	    break;
+-#endif
+ 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
+ 	    "calling policy close with wait status %d", status);
+ 	policy_close(&policy_plugin, status, 0);
+diff -up ./src/sudo_edit.c.symbolic-link-attack-3 ./src/sudo_edit.c
+--- ./src/sudo_edit.c.symbolic-link-attack-3	2021-02-02 17:11:32.380020435 +0100
++++ ./src/sudo_edit.c	2021-02-02 17:11:32.382020407 +0100
+@@ -757,28 +757,54 @@ bad:
+ 
+ #ifdef HAVE_SELINUX
+ static int
++selinux_run_helper(char *argv[], char *envp[])
++{
++    int status, ret = SESH_ERR_FAILURE;
++    const char *sesh;
++    pid_t child, pid;
++    debug_decl(selinux_run_helper, SUDO_DEBUG_EDIT);
++
++    sesh = sudo_conf_sesh_path();
++    if (sesh == NULL) {
++	sudo_warnx("internal error: sesh path not set");
++	debug_return_int(-1);
++    }
++
++    child = sudo_debug_fork();
++    switch (child) {
++    case -1:
++	sudo_warn(U_("unable to fork"));
++	break;
++    case 0:
++	/* child runs sesh in new context */
++	if (selinux_setcon() == 0)
++	    execve(sesh, argv, envp);
++	_exit(SESH_ERR_FAILURE);
++    default:
++	/* parent waits */
++	do {
++	    pid = waitpid(child, &status, 0);
++	} while (pid == -1 && errno == EINTR);
++
++	ret = WIFSIGNALED(status) ? SESH_ERR_KILLED : WEXITSTATUS(status);
++    }
++
++    debug_return_int(ret);
++}
++
++static int
+ selinux_edit_create_tfiles(struct command_details *command_details,
+     struct tempfile *tf, char *files[], int nfiles)
+ {
+     char **sesh_args, **sesh_ap;
+     int i, rc, sesh_nargs;
+     struct stat sb;
+-    struct command_details saved_command_details;
+     debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
+-    
+-    /* Prepare selinux stuff (setexeccon) */
+-    if (selinux_setup(command_details->selinux_role,
+-	command_details->selinux_type, NULL, -1) != 0)
+-	debug_return_int(-1);
+ 
+     if (nfiles < 1)
+ 	debug_return_int(0);
+ 
+     /* Construct common args for sesh */
+-    memcpy(&saved_command_details, command_details, sizeof(struct command_details));
+-    command_details->command = _PATH_SUDO_SESH;
+-    command_details->flags |= CD_SUDOEDIT_COPY;
+-    
+     sesh_nargs = 4 + (nfiles * 2) + 1;
+     sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
+     if (sesh_args == NULL) {
+@@ -791,6 +817,7 @@ selinux_edit_create_tfiles(struct comman
+ 	*sesh_ap++ = "-h";
+     *sesh_ap++ = "0";
+ 
++    /* XXX - temp files should be created with user's context */
+     for (i = 0; i < nfiles; i++) {
+ 	char *tfile, *ofile = files[i];
+ 	int tfd;
+@@ -820,8 +847,7 @@ selinux_edit_create_tfiles(struct comman
+     *sesh_ap = NULL;
+ 
+     /* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
+-    command_details->argv = sesh_args;
+-    rc = run_command(command_details);
++    rc = selinux_run_helper(sesh_args, command_details->envp);
+     switch (rc) {
+     case SESH_SUCCESS:
+ 	break;
+@@ -829,15 +855,12 @@ selinux_edit_create_tfiles(struct comman
+ 	sudo_fatalx(U_("sesh: internal error: odd number of paths"));
+     case SESH_ERR_NO_FILES:
+ 	sudo_fatalx(U_("sesh: unable to create temporary files"));
++    case SESH_ERR_KILLED:
++	sudo_fatalx(U_("sesh: killed by a signal"));
+     default:
+ 	sudo_fatalx(U_("sesh: unknown error %d"), rc);
+     }
+ 
+-    /* Restore saved command_details. */
+-    command_details->command = saved_command_details.command;
+-    command_details->flags = saved_command_details.flags;
+-    command_details->argv = saved_command_details.argv;
+-    
+     /* Chown to user's UID so they can edit the temporary files. */
+     for (i = 0; i < nfiles; i++) {
+ 	if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
+@@ -858,24 +881,14 @@ selinux_edit_copy_tfiles(struct command_
+ {
+     char **sesh_args, **sesh_ap;
+     int i, rc, sesh_nargs, ret = 1;
+-    struct command_details saved_command_details;
+     struct timespec ts;
+     struct stat sb;
+     debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
+-    
+-    /* Prepare selinux stuff (setexeccon) */
+-    if (selinux_setup(command_details->selinux_role,
+-	command_details->selinux_type, NULL, -1) != 0)
+-	debug_return_int(1);
+ 
+     if (nfiles < 1)
+ 	debug_return_int(0);
+ 
+     /* Construct common args for sesh */
+-    memcpy(&saved_command_details, command_details, sizeof(struct command_details));
+-    command_details->command = _PATH_SUDO_SESH;
+-    command_details->flags |= CD_SUDOEDIT_COPY;
+-    
+     sesh_nargs = 3 + (nfiles * 2) + 1;
+     sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
+     if (sesh_args == NULL) {
+@@ -913,32 +926,29 @@ selinux_edit_copy_tfiles(struct command_
+ 
+     if (sesh_ap - sesh_args > 3) {
+ 	/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
+-	command_details->argv = sesh_args;
+-	rc = run_command(command_details);
++	rc = selinux_run_helper(sesh_args, command_details->envp);
+ 	switch (rc) {
+ 	case SESH_SUCCESS:
+ 	    ret = 0;
+ 	    break;
+ 	case SESH_ERR_NO_FILES:
+ 	    sudo_warnx(U_("unable to copy temporary files back to their original location"));
+-	    sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
+ 	    break;
+ 	case SESH_ERR_SOME_FILES:
+ 	    sudo_warnx(U_("unable to copy some of the temporary files back to their original location"));
+-	    sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
++	    break;
++	case SESH_ERR_KILLED:
++	    sudo_warnx(U_("sesh: killed by a signal"));
+ 	    break;
+ 	default:
+ 	    sudo_warnx(U_("sesh: unknown error %d"), rc);
+ 	    break;
+ 	}
++	if (ret != 0)
++	    sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
+     }
+     free(sesh_args);
+ 
+-    /* Restore saved command_details. */
+-    command_details->command = saved_command_details.command;
+-    command_details->flags = saved_command_details.flags;
+-    command_details->argv = saved_command_details.argv;
+-
+     debug_return_int(ret);
+ }
+ #endif /* HAVE_SELINUX */
+@@ -990,6 +1000,15 @@ sudo_edit(struct command_details *comman
+ 	goto cleanup;
+     }
+ 
++#ifdef HAVE_SELINUX
++    /* Compute new SELinux security context. */
++    if (ISSET(command_details->flags, CD_RBAC_ENABLED)) {
++	if (selinux_setup(command_details->selinux_role,
++		command_details->selinux_type, NULL, -1, false) != 0)
++	    goto cleanup;
++    }
++#endif
++
+     /* Copy editor files to temporaries. */
+     tf = calloc(nfiles, sizeof(*tf));
+     if (tf == NULL) {
+@@ -1025,6 +1044,7 @@ sudo_edit(struct command_details *comman
+     /*
+      * Run the editor with the invoking user's creds,
+      * keeping track of the time spent in the editor.
++     * XXX - should run editor with user's context
+      */
+     if (sudo_gettime_real(&times[0]) == -1) {
+ 	sudo_warn(U_("unable to read the clock"));
+diff -up ./src/sudo_exec.h.symbolic-link-attack-3 ./src/sudo_exec.h
+--- ./src/sudo_exec.h.symbolic-link-attack-3	2021-02-02 17:11:32.380020435 +0100
++++ ./src/sudo_exec.h	2021-02-02 17:11:32.382020407 +0100
+@@ -73,6 +73,7 @@
+  */
+ #define SESH_SUCCESS	    0		/* successful operation */
+ #define SESH_ERR_FAILURE    1		/* unspecified error */
++#define SESH_ERR_KILLED     2		/* killed by a signal */
+ #define SESH_ERR_INVALID    30		/* invalid -e arg value */
+ #define SESH_ERR_BAD_PATHS  31		/* odd number of paths */
+ #define SESH_ERR_NO_FILES   32		/* copy error, no files copied */
+diff -up ./src/sudo.h.symbolic-link-attack-3 ./src/sudo.h
+--- ./src/sudo.h.symbolic-link-attack-3	2019-10-28 13:28:52.000000000 +0100
++++ ./src/sudo.h	2021-02-02 17:11:32.382020407 +0100
+@@ -135,12 +135,11 @@ struct user_details {
+ #define CD_USE_PTY		0x001000
+ #define CD_SET_UTMP		0x002000
+ #define CD_EXEC_BG		0x004000
+-#define CD_SUDOEDIT_COPY	0x008000
+-#define CD_SUDOEDIT_FOLLOW	0x010000
+-#define CD_SUDOEDIT_CHECKDIR	0x020000
+-#define CD_SET_GROUPS		0x040000
+-#define CD_LOGIN_SHELL		0x080000
+-#define CD_OVERRIDE_UMASK	0x100000
++#define CD_SUDOEDIT_FOLLOW	0x008000
++#define CD_SUDOEDIT_CHECKDIR	0x010000
++#define CD_SET_GROUPS		0x020000
++#define CD_LOGIN_SHELL		0x040000
++#define CD_OVERRIDE_UMASK	0x080000
+ 
+ struct preserved_fd {
+     TAILQ_ENTRY(preserved_fd) entries;
+@@ -240,7 +239,8 @@ int os_init_openbsd(int argc, char *argv
+ /* selinux.c */
+ int selinux_restore_tty(void);
+ int selinux_setup(const char *role, const char *type, const char *ttyn,
+-    int ttyfd);
++    int ttyfd, bool label_tty);
++int selinux_setcon(void);
+ void selinux_execve(int fd, const char *path, char *const argv[],
+     char *envp[], bool noexec);
+ 
diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23240-4.patch b/SOURCES/sudo-1.9.5-CVE-2021-23240-4.patch
new file mode 100644
index 0000000..b6d2813
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23240-4.patch
@@ -0,0 +1,380 @@
+diff -up ./src/copy_file.c.symbolic-link-attack-4 ./src/copy_file.c
+--- ./src/copy_file.c.symbolic-link-attack-4	2021-02-02 16:35:18.453036846 +0100
++++ ./src/copy_file.c	2021-02-02 16:38:09.430731749 +0100
+@@ -23,6 +23,7 @@
+ 
+ #include <config.h>
+ 
++#include <sys/stat.h>
+ #include <sys/types.h>
+ 
+ #include <stdio.h>
+@@ -126,3 +127,35 @@ write_error:
+ 	debug_return_int(-1);
+     }
+ }
++
++#ifdef HAVE_SELINUX
++bool
++sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
++{
++    struct stat sbuf;
++    debug_decl(sudo_check_temp_file, SUDO_DEBUG_UTIL);
++
++    if (sb == NULL)
++	sb = &sbuf;
++
++    if (fstat(tfd, sb) == -1) {
++	sudo_warn(U_("unable to stat %s"), tfile);
++	debug_return_bool(false);
++    }
++    if (!S_ISREG(sb->st_mode)) {
++	sudo_warnx(U_("%s: not a regular file"), tfile);
++	debug_return_bool(false);
++    }
++    if ((sb->st_mode & ALLPERMS) != (S_IRUSR|S_IWUSR)) {
++	sudo_warnx(U_("%s: bad file mode: 0%o"), tfile,
++	    (unsigned int)(sb->st_mode & ALLPERMS));
++	debug_return_bool(false);
++    }
++    if (sb->st_uid != uid) {
++	sudo_warnx(U_("%s is owned by uid %u, should be %u"),
++	    tfile, (unsigned int)sb->st_uid, (unsigned int)uid);
++	debug_return_bool(false);
++    }
++    debug_return_bool(true);
++}
++#endif /* SELINUX */
+diff -up ./src/sesh.c.symbolic-link-attack-4 ./src/sesh.c
+--- ./src/sesh.c.symbolic-link-attack-4	2021-02-02 16:35:18.450036887 +0100
++++ ./src/sesh.c	2021-02-02 16:38:52.907146897 +0100
+@@ -134,7 +134,7 @@ main(int argc, char *argv[], char *envp[
+ static int
+ sesh_sudoedit(int argc, char *argv[])
+ {
+-    int i, oflags_dst, post, ret = SESH_ERR_FAILURE;
++    int i, oflags_src, oflags_dst, post, ret = SESH_ERR_FAILURE;
+     int fd_src = -1, fd_dst = -1, follow = 0;
+     ssize_t nread, nwritten;
+     struct stat sb;
+@@ -178,10 +178,12 @@ sesh_sudoedit(int argc, char *argv[])
+ 	debug_return_int(SESH_ERR_BAD_PATHS);
+ 
+     /*
+-     * Use O_EXCL if we are not in the post editing stage
+-     * so that it's ensured that the temporary files are
+-     * created by us and that we are not opening any symlinks.
++     * In the pre-editing stage, use O_EXCL to ensure that the temporary
++     * files are created by us and that we are not opening any symlinks.
++     * In the post-editing stage, use O_NOFOLLOW so we don't follow symlinks
++     * when opening the temporary files.
+      */
++    oflags_src = O_RDONLY|(post ? O_NONBLOCK|O_NOFOLLOW : follow);
+     oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
+     for (i = 0; i < argc - 1; i += 2) {
+ 	const char *path_src = argv[i];
+@@ -191,7 +193,7 @@ sesh_sudoedit(int argc, char *argv[])
+ 	 * doesn't exist, that's OK, we'll create an empty
+ 	 * destination file.
+ 	 */
+-	if ((fd_src = open(path_src, O_RDONLY|follow, S_IRUSR|S_IWUSR)) < 0) {
++	if ((fd_src = open(path_src, oflags_src, S_IRUSR|S_IWUSR)) < 0) {
+ 	    if (errno != ENOENT) {
+ 		sudo_warn("%s", path_src);
+ 		if (post) {
+@@ -201,6 +203,14 @@ sesh_sudoedit(int argc, char *argv[])
+ 		    goto cleanup_0;
+ 	    }
+ 	}
++	if (post) {
++	    /* Make sure the temporary file is safe and has the proper owner. */
++	    if (!sudo_check_temp_file(fd_src, path_src, geteuid(), &sb)) {
++		ret = SESH_ERR_SOME_FILES;
++		goto nocleanup;
++	    }
++	    fcntl(fd_src, F_SETFL, fcntl(fd_src, F_GETFL, 0) & ~O_NONBLOCK);
++	}
+ 
+ 	if ((fd_dst = open(path_dst, oflags_dst, post ?
+ 	    (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR))) < 0) {
+@@ -218,10 +228,7 @@ sesh_sudoedit(int argc, char *argv[])
+ 	    off_t len_dst = -1;
+ 
+ 	    if (post) {
+-		if (fstat(fd_src, &sb) != 0) {
+-		    ret = SESH_ERR_SOME_FILES;
+-		    goto nocleanup;
+-		}
++		/* sudo_check_temp_file() filled in sb for us. */
+ 		len_src = sb.st_size;
+ 		if (fstat(fd_dst, &sb) != 0) {
+ 		    ret = SESH_ERR_SOME_FILES;
+diff -up ./src/sudo_edit.c.symbolic-link-attack-4 ./src/sudo_edit.c
+--- ./src/sudo_edit.c.symbolic-link-attack-4	2021-02-02 16:35:18.452036860 +0100
++++ ./src/sudo_edit.c	2021-02-02 16:54:25.943429580 +0100
+@@ -253,8 +253,10 @@ sudo_edit_mktemp(const char *ofile, char
+     } else {
+ 	len = asprintf(tfile, "%s/%s.XXXXXXXX", edit_tmpdir, cp);
+     }
+-    if (len == -1)
+-	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
++    if (len == -1) {
++	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
++	debug_return_int(-1);
++    }
+     tfd = mkstemps(*tfile, suff ? strlen(suff) : 0);
+     sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ 	"%s -> %s, fd %d", ofile, *tfile, tfd);
+@@ -757,7 +759,8 @@ bad:
+ 
+ #ifdef HAVE_SELINUX
+ static int
+-selinux_run_helper(char *argv[], char *envp[])
++selinux_run_helper(uid_t uid, gid_t gid, int ngroups, GETGROUPS_T *groups,
++    char *const argv[], char *const envp[])
+ {
+     int status, ret = SESH_ERR_FAILURE;
+     const char *sesh;
+@@ -777,8 +780,10 @@ selinux_run_helper(char *argv[], char *e
+ 	break;
+     case 0:
+ 	/* child runs sesh in new context */
+-	if (selinux_setcon() == 0)
++	if (selinux_setcon() == 0) {
++	    switch_user(uid, gid, ngroups, groups);
+ 	    execve(sesh, argv, envp);
++	}
+ 	_exit(SESH_ERR_FAILURE);
+     default:
+ 	/* parent waits */
+@@ -797,7 +802,7 @@ selinux_edit_create_tfiles(struct comman
+     struct tempfile *tf, char *files[], int nfiles)
+ {
+     char **sesh_args, **sesh_ap;
+-    int i, rc, sesh_nargs;
++    int i, rc, error, sesh_nargs, ret = -1;
+     struct stat sb;
+     debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
+ 
+@@ -809,7 +814,7 @@ selinux_edit_create_tfiles(struct comman
+     sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
+     if (sesh_args == NULL) {
+ 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+-	debug_return_int(-1);
++	goto done;
+     }
+     *sesh_ap++ = "sesh";
+     *sesh_ap++ = "-e";
+@@ -817,7 +822,6 @@ selinux_edit_create_tfiles(struct comman
+ 	*sesh_ap++ = "-h";
+     *sesh_ap++ = "0";
+ 
+-    /* XXX - temp files should be created with user's context */
+     for (i = 0; i < nfiles; i++) {
+ 	char *tfile, *ofile = files[i];
+ 	int tfd;
+@@ -835,8 +839,7 @@ selinux_edit_create_tfiles(struct comman
+ 	if (tfd == -1) {
+ 	    sudo_warn("mkstemps");
+ 	    free(tfile);
+-	    free(sesh_args);
+-	    debug_return_int(-1);
++	    goto done;
+ 	}
+ 	/* Helper will re-create temp file with proper security context. */
+ 	close(tfd);
+@@ -847,8 +850,10 @@ selinux_edit_create_tfiles(struct comman
+     *sesh_ap = NULL;
+ 
+     /* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
+-    rc = selinux_run_helper(sesh_args, command_details->envp);
+-    switch (rc) {
++    error = selinux_run_helper(command_details->uid, command_details->gid,
++	command_details->ngroups, command_details->groups, sesh_args,
++	command_details->envp);
++    switch (error) {
+     case SESH_SUCCESS:
+ 	break;
+     case SESH_ERR_BAD_PATHS:
+@@ -858,21 +863,34 @@ selinux_edit_create_tfiles(struct comman
+     case SESH_ERR_KILLED:
+ 	sudo_fatalx(U_("sesh: killed by a signal"));
+     default:
+-	sudo_fatalx(U_("sesh: unknown error %d"), rc);
++	sudo_fatalx(U_("sesh: unknown error %d"), error);
++    goto done;
+     }
+ 
+-    /* Chown to user's UID so they can edit the temporary files. */
+     for (i = 0; i < nfiles; i++) {
+-	if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
+-	    sudo_warn("unable to chown(%s) to %d:%d for editing",
+-		tf[i].tfile, user_details.uid, user_details.gid);
+-	}
++        int tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW);
++        if (tfd == -1) {
++            sudo_warn(U_("unable to open %s"), tf[i].tfile);
++            goto done;
++        }
++        if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->uid, NULL)) {
++            close(tfd);
++            goto done;
++        }
++        if (fchown(tfd, user_details.uid, user_details.gid) != 0) {
++            sudo_warn("unable to chown(%s) to %d:%d for editing",
++                      tf[i].tfile, user_details.uid, user_details.gid);
++            close(tfd);
++            goto done;
++        }
++        close(tfd);
+     }
+ 
++done:
+     /* Contents of tf will be freed by caller. */
+     free(sesh_args);
+ 
+-    return (nfiles);
++    debug_return_int(ret);
+ }
+ 
+ static int
+@@ -880,7 +898,8 @@ selinux_edit_copy_tfiles(struct command_
+     struct tempfile *tf, int nfiles, struct timespec *times)
+ {
+     char **sesh_args, **sesh_ap;
+-    int i, rc, sesh_nargs, ret = 1;
++    int i, rc, error, sesh_nargs, ret = 1;
++    int tfd = -1;
+     struct timespec ts;
+     struct stat sb;
+     debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
+@@ -901,33 +920,43 @@ selinux_edit_copy_tfiles(struct command_
+ 
+     /* Construct args for sesh -e 1 */
+     for (i = 0; i < nfiles; i++) {
+-	if (stat(tf[i].tfile, &sb) == 0) {
+-	    mtim_get(&sb, ts);
+-	    if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
+-		/*
+-		 * If mtime and size match but the user spent no measurable
+-		 * time in the editor we can't tell if the file was changed.
+-		 */
+-		if (sudo_timespeccmp(&times[0], &times[1], !=)) {
+-		    sudo_warnx(U_("%s unchanged"), tf[i].ofile);
+-		    unlink(tf[i].tfile);
+-		    continue;
+-		}
++	if (tfd != -1)
++	    close(tfd);
++	if ((tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW)) == -1) {
++	    sudo_warn(U_("unable to open %s"), tf[i].tfile);
++	    continue;
++	}
++	if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb))
++	    continue;
++	mtim_get(&sb, ts);
++	if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
++	    /*
++	     * If mtime and size match but the user spent no measurable
++	     * time in the editor we can't tell if the file was changed.
++	     */
++	    if (sudo_timespeccmp(&times[0], &times[1], !=)) {
++		sudo_warnx(U_("%s unchanged"), tf[i].ofile);
++		unlink(tf[i].tfile);
++		continue;
+ 	    }
+ 	}
+ 	*sesh_ap++ = tf[i].tfile;
+ 	*sesh_ap++ = tf[i].ofile;
+-	if (chown(tf[i].tfile, command_details->uid, command_details->gid) != 0) {
++	if (fchown(tfd, command_details->uid, command_details->gid) != 0) {
+ 	    sudo_warn("unable to chown(%s) back to %d:%d", tf[i].tfile,
+ 		command_details->uid, command_details->gid);
+ 	}
+     }
+     *sesh_ap = NULL;
++    if (tfd != -1)
++	close(tfd);
+ 
+     if (sesh_ap - sesh_args > 3) {
+ 	/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
+-	rc = selinux_run_helper(sesh_args, command_details->envp);
+-	switch (rc) {
++	error = selinux_run_helper(command_details->uid, command_details->gid,
++	    command_details->ngroups, command_details->groups, sesh_args,
++	    command_details->envp);
++	switch (error) {
+ 	case SESH_SUCCESS:
+ 	    ret = 0;
+ 	    break;
+@@ -941,7 +970,7 @@ selinux_edit_copy_tfiles(struct command_
+ 	    sudo_warnx(U_("sesh: killed by a signal"));
+ 	    break;
+ 	default:
+-	    sudo_warnx(U_("sesh: unknown error %d"), rc);
++	    sudo_warnx(U_("sesh: unknown error %d"), error);
+ 	    break;
+ 	}
+ 	if (ret != 0)
+@@ -963,7 +992,7 @@ sudo_edit(struct command_details *comman
+ {
+     struct command_details saved_command_details;
+     char **nargv = NULL, **ap, **files = NULL;
+-    int errors, i, ac, nargc, rc;
++    int errors, i, ac, nargc, ret;
+     int editor_argc = 0, nfiles = 0;
+     struct timespec times[2];
+     struct tempfile *tf = NULL;
+@@ -1058,7 +1087,7 @@ sudo_edit(struct command_details *comman
+     command_details->ngroups = user_details.ngroups;
+     command_details->groups = user_details.groups;
+     command_details->argv = nargv;
+-    rc = run_command(command_details);
++    ret = run_command(command_details);
+     if (sudo_gettime_real(&times[1]) == -1) {
+ 	sudo_warn(U_("unable to read the clock"));
+ 	goto cleanup;
+@@ -1080,14 +1109,16 @@ sudo_edit(struct command_details *comman
+     else
+ #endif
+ 	errors = sudo_edit_copy_tfiles(command_details, tf, nfiles, times);
+-    if (errors)
+-	goto cleanup;
++    if (errors) {
++        /* Preserve the edited temporary files. */
++        ret = W_EXITCODE(1, 0);
++    }
+ 
+     for (i = 0; i < nfiles; i++)
+ 	free(tf[i].tfile);
+     free(tf);
+     free(nargv);
+-    debug_return_int(rc);
++    debug_return_int(ret);
+ 
+ cleanup:
+     /* Clean up temp files and return. */
+diff -up ./src/sudo_exec.h.symbolic-link-attack-4 ./src/sudo_exec.h
+--- ./src/sudo_exec.h.symbolic-link-attack-4	2021-02-02 16:35:18.452036860 +0100
++++ ./src/sudo_exec.h	2021-02-02 16:35:18.454036833 +0100
+@@ -1,7 +1,7 @@
+ /*
+  * SPDX-License-Identifier: ISC
+  *
+- * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@sudo.ws>
++ * Copyright (c) 2010-2017, 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws>
+  *
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -84,9 +84,11 @@
+  */
+ struct command_details;
+ struct command_status;
++struct stat;
+ 
+ /* copy_file.c */
+ int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
++bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb);
+ 
+ /* exec.c */
+ void exec_cmnd(struct command_details *details, int errfd);
diff --git a/SOURCES/sudo-1.9.5-CVE-2021-23240-5.patch b/SOURCES/sudo-1.9.5-CVE-2021-23240-5.patch
new file mode 100644
index 0000000..fd1bbd1
--- /dev/null
+++ b/SOURCES/sudo-1.9.5-CVE-2021-23240-5.patch
@@ -0,0 +1,47 @@
+diff -up ./src/copy_file.c.symbolic-link-attack-5 ./src/copy_file.c
+--- ./src/copy_file.c.symbolic-link-attack-5	2021-02-02 17:18:05.355567274 +0100
++++ ./src/copy_file.c	2021-02-02 17:19:09.904671563 +0100
+@@ -128,7 +128,6 @@ write_error:
+     }
+ }
+ 
+-#ifdef HAVE_SELINUX
+ bool
+ sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
+ {
+@@ -158,4 +157,3 @@ sudo_check_temp_file(int tfd, const char
+     }
+     debug_return_bool(true);
+ }
+-#endif /* SELINUX */
+diff -up ./src/sudo_edit.c.symbolic-link-attack-5 ./src/sudo_edit.c
+--- ./src/sudo_edit.c.symbolic-link-attack-5	2021-02-02 17:18:05.355567274 +0100
++++ ./src/sudo_edit.c	2021-02-02 17:18:05.356567260 +0100
+@@ -692,24 +692,17 @@ sudo_edit_copy_tfiles(struct command_det
+ 
+     /* Copy contents of temp files to real ones. */
+     for (i = 0; i < nfiles; i++) {
+-	int rc = -1;
+ 	sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ 	    "seteuid(%u)", (unsigned int)user_details.uid);
+ 	if (seteuid(user_details.uid) != 0)
+ 	    sudo_fatal("seteuid(%u)", (unsigned int)user_details.uid);
+ 	tfd = sudo_edit_open(tf[i].tfile, O_RDONLY,
+ 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, NULL);
+-	if (tfd != -1)
+-	    rc = fstat(tfd, &sb);
+-	sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+-	    "seteuid(%u)", ROOT_UID);
+ 	if (seteuid(ROOT_UID) != 0)
+ 	    sudo_fatal("seteuid(ROOT_UID)");
+-	if (rc == -1 || !S_ISREG(sb.st_mode)) {
+-	    if (rc == -1)
+-		sudo_warn("%s", tf[i].tfile);
+-	    else
+-		sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
++	sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
++	    "seteuid(%u)", ROOT_UID);
++	if (tfd == -1 || !sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) {
+ 	    sudo_warnx(U_("%s left unmodified"), tf[i].ofile);
+ 	    if (tfd != -1)
+ 		close(tfd);
diff --git a/SPECS/sudo.spec b/SPECS/sudo.spec
index 949aab9..6b5bddc 100644
--- a/SPECS/sudo.spec
+++ b/SPECS/sudo.spec
@@ -1,10 +1,10 @@
 Summary: Allows restricted root access for specified users
 Name: sudo
 Version: 1.8.29
-Release: 6%{?dist}.1
+Release: 7%{?dist}
 License: ISC
 Group: Applications/System
-URL: http://www.courtesan.com/sudo/
+URL: https://www.sudo.ws/
 
 Source0: https://www.sudo.ws/dist/%{name}-%{version}.tar.gz
 Source1: sudoers
@@ -56,8 +56,16 @@ Patch9: sudo-1.8.29-CVE-2019-18634.patch
 Patch10: sudo-1.8.29-expired-password-part1.patch
 Patch11: sudo-1.8.29-expired-password-part2.patch
 
-# 1917732 - EMBARGOED CVE-2021-3156 sudo: Heap-buffer overflow in argument parsing [rhel-8.3.0.z]
+# 1917734 - EMBARGOED CVE-2021-3156 sudo: Heap-buffer overflow in argument parsing [rhel-8.4.0]
 Patch12: sudo-1.8.31-CVE-2021-3156.patch
+# 1916434 - CVE-2021-23239 sudo: possible directory existence test due to race condition in sudoedit [rhel-8]
+Patch13: sudo-1.9.5-CVE-2021-23239.patch
+# 1917038 - CVE-2021-23240 sudo: symbolic link attack in SELinux-enabled sudoedit [rhel-8]
+Patch14: sudo-1.9.5-CVE-2021-23240-1.patch
+Patch15: sudo-1.9.5-CVE-2021-23240-2.patch
+Patch16: sudo-1.9.5-CVE-2021-23240-3.patch
+Patch17: sudo-1.9.5-CVE-2021-23240-4.patch
+Patch18: sudo-1.9.5-CVE-2021-23240-5.patch
 
 %description
 Sudo (superuser do) allows a system administrator to give certain
@@ -97,6 +105,14 @@ plugins that use %{name}.
 
 %patch12 -p1 -b .heap-buffer
 
+%patch13 -p1 -b .sudoedit-race
+
+%patch14 -p1 -b .symbolic-link-attack-1
+%patch15 -p1 -b .symbolic-link-attack-2
+%patch16 -p1 -b .symbolic-link-attack-3
+%patch17 -p1 -b .symbolic-link-attack-4
+%patch18 -p1 -b .symbolic-link-attack-5
+
 %build
 # Remove bundled copy of zlib
 rm -rf zlib/
@@ -255,10 +271,16 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man8/sudo_plugin.8*
 
 %changelog
-* Wed Jan 20 2021 Radovan Sroka <rsroka@redhat.com> - 1.8.29-6.1
-- RHEL 8.3.Z ERRATUM
+* Tue Feb 02 2021 Radovan Sroka <rsroka@redhat.com> - 1.8.29-7
+- RHEL 8.4 ERRATUM
 - CVE-2021-3156
-Resolves: rhbz#1917732
+Resolves: rhbz#1917734
+- CVE-2021-23239 sudo: possible directory existence test due to race condition in sudoedit
+Resolves: rhzb#1916434
+- CVE-2021-23240 sudo: symbolic link attack in SELinux-enabled sudoedit
+Resolves: rhbz#1917038
+- updated upstream url
+Resolves: rhbz#1923825
 
 * Tue Apr 28 2020 Radovan Sroka <rsroka@redhat.com> - 1.8.29-6
 - RHEL 8.3 ERRATUM