changeset 10293:397722cdd7ec

Add support for using fexecve() if supported on commands that are checksummed.
author Todd C. Miller <Todd.Miller@courtesan.com>
date Mon, 04 Jan 2016 10:35:18 -0700
parents 33272418bb10
children 94ffd6b18431
files configure configure.ac doc/sudo_plugin.cat doc/sudo_plugin.man.in doc/sudo_plugin.mdoc.in doc/sudoers.cat doc/sudoers.man.in doc/sudoers.mdoc.in include/sudo_plugin.h plugins/sudoers/match.c plugins/sudoers/policy.c plugins/sudoers/sudoers.h src/exec.c src/exec_common.c src/selinux.c src/sesh.c src/sudo.c src/sudo.h src/sudo_exec.h
diffstat 19 files changed, 209 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Tue Dec 29 13:38:14 2015 -0700
+++ b/configure	Mon Jan 04 10:35:18 2016 -0700
@@ -2650,6 +2650,7 @@
 as_fn_append ac_header_list " sys/select.h"
 as_fn_append ac_header_list " sys/stropts.h"
 as_fn_append ac_header_list " sys/sysmacros.h"
+as_fn_append ac_func_list " fexecve"
 as_fn_append ac_func_list " killpg"
 as_fn_append ac_func_list " nl_langinfo"
 as_fn_append ac_func_list " strftime"
@@ -18078,6 +18079,8 @@
 
 
 
+
+
 for ac_func in getgrouplist
 do :
   ac_fn_c_check_func "$LINENO" "getgrouplist" "ac_cv_func_getgrouplist"
@@ -19903,8 +19906,8 @@
 fi
 done
 
-    # Check for fexecve, posix_spawn, and posix_spawnp
-    for ac_func in fexecve posix_spawn posix_spawnp
+    # Check for posix_spawn, and posix_spawnp
+    for ac_func in posix_spawn posix_spawnp
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
--- a/configure.ac	Tue Dec 29 13:38:14 2015 -0700
+++ b/configure.ac	Mon Jan 04 10:35:18 2016 -0700
@@ -1,7 +1,7 @@
 dnl
 dnl Use the top-level autogen.sh script to generate configure and config.h.in
 dnl
-dnl Copyright (c) 1994-1996,1998-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+dnl Copyright (c) 1994-1996,1998-2016 Todd C. Miller <Todd.Miller@courtesan.com>
 dnl
 AC_PREREQ([2.59])
 AC_INIT([sudo], [1.8.16], [https://bugzilla.sudo.ws/], [sudo])
@@ -2384,7 +2384,7 @@
 dnl Function checks
 dnl
 AC_FUNC_GETGROUPS
-AC_CHECK_FUNCS_ONCE([killpg nl_langinfo strftime pread pwrite openat])
+AC_CHECK_FUNCS_ONCE([fexecve killpg nl_langinfo strftime pread pwrite openat])
 AC_CHECK_FUNCS([getgrouplist], [], [
     case "$host_os" in
     aix*)
@@ -2676,8 +2676,8 @@
 if test X"$with_noexec" != X"no"; then
     # Check for non-standard exec functions
     AC_CHECK_FUNCS([exect execvP execvpe])
-    # Check for fexecve, posix_spawn, and posix_spawnp
-    AC_CHECK_FUNCS([fexecve posix_spawn posix_spawnp])
+    # Check for posix_spawn, and posix_spawnp
+    AC_CHECK_FUNCS([posix_spawn posix_spawnp])
 fi
 
 dnl
--- a/doc/sudo_plugin.cat	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudo_plugin.cat	Mon Jan 04 10:35:18 2016 -0700
@@ -499,6 +499,11 @@
                        This setting has no effect unless I/O logging is
                        enabled or _u_s_e___p_t_y is enabled.
 
+                 execfd=number
+                       If specified, ssuuddoo will use the fexecve(2) system call
+                       to execute the command instead of execve(2).  The
+                       specified _n_u_m_b_e_r must refer to an open file descriptor.
+
                  iolog_compress=bool
                        Set to true if the I/O logging plugins, if any, should
                        compress the log data.  This is a hint to the I/O
@@ -1505,6 +1510,9 @@
            it supports plugin API version 1.8 or higher to receive a
            conversation function pointer that supports this argument.
 
+     Version 1.9 (sudo 1.8.16)
+           The _e_x_e_c_f_d entry was added to the command_info list.
+
 SSEEEE AALLSSOO
      sudo.conf(4), sudoers(4), sudo(1m)
 
@@ -1534,4 +1542,4 @@
      file distributed with ssuuddoo or https://www.sudo.ws/license.html for
      complete details.
 
-Sudo 1.8.16                    November 20, 2015                   Sudo 1.8.16
+Sudo 1.8.16                     January 4, 2016                    Sudo 1.8.16
--- a/doc/sudo_plugin.man.in	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudo_plugin.man.in	Mon Jan 04 10:35:18 2016 -0700
@@ -1,7 +1,7 @@
 .\" DO NOT EDIT THIS FILE, IT IS NOT THE MASTER!
 .\" IT IS GENERATED AUTOMATICALLY FROM sudo_plugin.mdoc.in
 .\"
-.\" Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+.\" Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -16,7 +16,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.TH "SUDO_PLUGIN" "5" "November 20, 2015" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
+.TH "SUDO_PLUGIN" "5" "January 4, 2016" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
 .nh
 .if n .ad l
 .SH "NAME"
@@ -881,6 +881,17 @@
 \fIuse_pty\fR
 is enabled.
 .TP 6n
+execfd=number
+If specified,
+\fBsudo\fR
+will use the
+fexecve(2)
+system call to execute the command instead of
+execve(2).
+The specified
+\fInumber\fR
+must refer to an open file descriptor.
+.TP 6n
 iolog_compress=bool
 Set to true if the I/O logging plugins, if any, should compress the
 log data.
@@ -2703,6 +2714,13 @@
 definition has been updated to match.
 The plugin must specify that it supports plugin API version 1.8 or higher
 to receive a conversation function pointer that supports this argument.
+.TP 6n
+Version 1.9 (sudo 1.8.16)
+The
+\fIexecfd\fR
+entry was added to the
+\fRcommand_info\fR
+list.
 .SH "SEE ALSO"
 sudo.conf(@mansectform@),
 sudoers(@mansectform@),
--- a/doc/sudo_plugin.mdoc.in	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudo_plugin.mdoc.in	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+.\" Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 20, 2015
+.Dd January 4, 2016
 .Dt SUDO_PLUGIN @mansectform@
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
@@ -784,6 +784,16 @@
 This setting has no effect unless I/O logging is enabled or
 .Em use_pty
 is enabled.
+.It execfd=number
+If specified,
+.Nm sudo
+will use the
+.Xr fexecve 2
+system call to execute the command instead of
+.Xr execve 2 .
+The specified
+.Em number
+must refer to an open file descriptor.
 .It iolog_compress=bool
 Set to true if the I/O logging plugins, if any, should compress the
 log data.
@@ -2367,6 +2377,12 @@
 definition has been updated to match.
 The plugin must specify that it supports plugin API version 1.8 or higher
 to receive a conversation function pointer that supports this argument.
+.It Version 1.9 (sudo 1.8.16)
+The
+.Em execfd
+entry was added to the
+.Li command_info
+list.
 .El
 .Sh SEE ALSO
 .Xr sudo.conf @mansectform@ ,
--- a/doc/sudoers.cat	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudoers.cat	Mon Jan 04 10:35:18 2016 -0700
@@ -408,10 +408,12 @@
      $ openssl dgst -binary -sha224 /bin/ls | openssl base64
      EYGH2oNk1JC0p9679IMATo8+BT7JVDCd4sQaJQ==
 
-     If the user has write access to either the command itself or the
-     directory in which the command is located (directly or via a ssuuddoo
-     command) it may be possible for the user to replace the command after the
-     digest check has been performed but before the command is executed.
+     Warning, if the user has write access to the command itself (directly or
+     via a ssuuddoo command), it may be possible for the user to replace the
+     command after the digest check has been performed but before the command
+     is executed.  A similar race condition exists on systems that lack the
+     fexecve(2) system call when the directory in which the command is located
+     is writable by the user.
 
      Command digests are only supported by version 1.8.7 or higher.
 
@@ -2493,4 +2495,4 @@
      file distributed with ssuuddoo or https://www.sudo.ws/license.html for
      complete details.
 
-Sudo 1.8.16                    December 11, 2015                   Sudo 1.8.16
+Sudo 1.8.16                     January 4, 2015                    Sudo 1.8.16
--- a/doc/sudoers.man.in	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudoers.man.in	Mon Jan 04 10:35:18 2016 -0700
@@ -1,7 +1,7 @@
 .\" DO NOT EDIT THIS FILE, IT IS NOT THE MASTER!
 .\" IT IS GENERATED AUTOMATICALLY FROM sudoers.mdoc.in
 .\"
-.\" Copyright (c) 1994-1996, 1998-2005, 2007-2015
+.\" Copyright (c) 1994-1996, 1998-2005, 2007-2016
 .\"	Todd C. Miller <Todd.Miller@courtesan.com>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
@@ -21,7 +21,7 @@
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\"
-.TH "SUDOERS" "5" "December 11, 2015" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
+.TH "SUDOERS" "5" "January 4, 2015" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
 .nh
 .if n .ad l
 .SH "NAME"
@@ -874,12 +874,14 @@
 .RE
 .fi
 .PP
-If the user has write access to either the command itself or the
-directory in which the command is located (directly or via a
+Warning, if the user has write access to the command itself (directly or via a
 \fBsudo\fR
-command) it may be possible for the user to replace the command
-after the digest check has been performed but before the command
-is executed.
+command), it may be possible for the user to replace the command after the
+digest check has been performed but before the command is executed.
+A similar race condition exists on systems that lack the
+fexecve(2)
+system call when the directory in which the command is located
+is writable by the user.
 .PP
 Command digests are only supported by version 1.8.7 or higher.
 .SS "Defaults"
--- a/doc/sudoers.mdoc.in	Tue Dec 29 13:38:14 2015 -0700
+++ b/doc/sudoers.mdoc.in	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (c) 1994-1996, 1998-2005, 2007-2015
+.\" Copyright (c) 1994-1996, 1998-2005, 2007-2016
 .\"	Todd C. Miller <Todd.Miller@courtesan.com>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
@@ -19,7 +19,7 @@
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\"
-.Dd December 11, 2015
+.Dd January 4, 2015
 .Dt SUDOERS @mansectform@
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
@@ -831,12 +831,14 @@
 EYGH2oNk1JC0p9679IMATo8+BT7JVDCd4sQaJQ==
 .Ed
 .Pp
-If the user has write access to either the command itself or the
-directory in which the command is located (directly or via a
+Warning, if the user has write access to the command itself (directly or via a
 .Nm sudo
-command) it may be possible for the user to replace the command
-after the digest check has been performed but before the command
-is executed.
+command), it may be possible for the user to replace the command after the
+digest check has been performed but before the command is executed.
+A similar race condition exists on systems that lack the
+.Xr fexecve 2
+system call when the directory in which the command is located
+is writable by the user.
 .Pp
 Command digests are only supported by version 1.8.7 or higher.
 .Ss Defaults
--- a/include/sudo_plugin.h	Tue Dec 29 13:38:14 2015 -0700
+++ b/include/sudo_plugin.h	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,7 +19,7 @@
 
 /* API version major/minor */
 #define SUDO_API_VERSION_MAJOR 1
-#define SUDO_API_VERSION_MINOR 8
+#define SUDO_API_VERSION_MINOR 9
 #define SUDO_API_MKVERSION(x, y) (((x) << 16) | (y))
 #define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR)
 
--- a/plugins/sudoers/match.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/plugins/sudoers/match.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 1998-2005, 2007-2015
+ * Copyright (c) 1996, 1998-2005, 2007-2016
  *	Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -55,6 +55,7 @@
 # include <netdb.h>
 #endif /* HAVE_NETGROUP_H */
 #include <dirent.h>
+#include <fcntl.h>
 #include <pwd.h>
 #include <grp.h>
 #include <errno.h>
@@ -583,17 +584,18 @@
 };
 
 static bool
-digest_matches(const char *file, const struct sudo_digest *sd)
+digest_matches(const char *file, const struct sudo_digest *sd, int *fd)
 {
     unsigned char file_digest[SHA512_DIGEST_LENGTH];
     unsigned char sudoers_digest[SHA512_DIGEST_LENGTH];
     unsigned char buf[32 * 1024];
     struct digest_function *func = NULL;
+    bool first = true;
+    bool is_script = false;
     size_t nread;
     SHA2_CTX ctx;
     FILE *fp;
     unsigned int i;
-    int h;
     debug_decl(digest_matches, SUDOERS_DEBUG_MATCH)
 
     for (i = 0; digest_functions[i].digest_name != NULL; i++) {
@@ -609,7 +611,7 @@
     if (strlen(sd->digest_str) == func->digest_len * 2) {
 	/* Convert the command digest from ascii hex to binary. */
 	for (i = 0; i < func->digest_len; i++) {
-	    h = hexchar(&sd->digest_str[i + i]);
+	    const int h = hexchar(&sd->digest_str[i + i]);
 	    if (h == -1)
 		goto bad_format;
 	    sudoers_digest[i] = (unsigned char)h;
@@ -633,6 +635,12 @@
 
     func->init(&ctx);
     while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) {
+	/* Check for #! cookie and set is_script. */
+	if (first) {
+	    first = false;
+	    if (nread >= 2 && buf[0] == '#' && buf[1] == '!')
+		is_script = true;
+	}
 	func->update(&ctx, buf, nread);
     }
     if (ferror(fp)) {
@@ -640,15 +648,36 @@
 	fclose(fp);
 	debug_return_bool(false);
     }
-    fclose(fp);
     func->final(file_digest, &ctx);
 
-    if (memcmp(file_digest, sudoers_digest, func->digest_len) == 0)
-	debug_return_bool(true);
-    sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
-	"%s digest mismatch for %s, expecting %s",
-	func->digest_name, file, sd->digest_str);
-    debug_return_bool(false);
+    if (memcmp(file_digest, sudoers_digest, func->digest_len) != 0) {
+	fclose(fp);
+	sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
+	    "%s digest mismatch for %s, expecting %s",
+	    func->digest_name, file, sd->digest_str);
+	debug_return_bool(false);
+    }
+
+#ifdef HAVE_FEXECVE
+    /*
+     * On systems with fexecve(2) we can use that to execute the
+     * matching command even when the directory is writable.
+     */
+    if ((*fd = dup(fileno(fp))) == -1) {
+	sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s",
+	    file, strerror(errno));
+	fclose(fp);
+	debug_return_bool(false);
+    }
+    /*
+     * Shell scripts go through namei twice and so we can't set the close
+     * on exec flag on the fd for fexecve(2).
+     */
+    if (!is_script)
+	fcntl(*fd, F_SETFD, FD_CLOEXEC);
+#endif /* HAVE_FEXECVE */
+    fclose(fp);
+    debug_return_bool(true);
 bad_format:
     sudo_warnx(U_("digest for %s (%s) is not in %s form"), file,
 	sd->digest_str, func->digest_name);
@@ -690,7 +719,11 @@
 	debug_return_bool(false);
     if (!command_args_match(sudoers_cmnd, sudoers_args))
 	debug_return_bool(false);
-    if (digest != NULL && !digest_matches(sudoers_cmnd, digest)) {
+    if (cmnd_fd != -1) {
+	close(cmnd_fd);
+	cmnd_fd = -1;
+    }
+    if (digest != NULL && !digest_matches(sudoers_cmnd, digest, &cmnd_fd)) {
 	/* XXX - log functions not available but we should log very loudly */
 	debug_return_bool(false);
     }
--- a/plugins/sudoers/policy.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/plugins/sudoers/policy.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -377,6 +377,9 @@
     user_umask = umask(SUDO_UMASK);
     umask(user_umask);
 
+    /* Some systems support fexecve() which we use for digest matches. */
+    cmnd_fd = -1;
+
     /* Dump settings and user info (XXX - plugin args) */
     for (cur = info->settings; *cur != NULL; cur++)
 	sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s", *cur);
@@ -551,6 +554,16 @@
 	if (asprintf(&command_info[info_len++], "umask=0%o", (unsigned int)cmnd_umask) == -1)
 	    goto oom;
     }
+    if (cmnd_fd != -1) {
+	if (sudo_version < SUDO_API_MKVERSION(1, 9)) {
+	    /* execfd only supported by plugin API 1.9 and higher */
+	    close(cmnd_fd);
+	    cmnd_fd = -1;
+	} else {
+	    if (asprintf(&command_info[info_len++], "execfd=%d", cmnd_fd) == -1)
+		goto oom;
+	}
+    }
 #ifdef HAVE_LOGIN_CAP_H
     if (def_use_loginclass) {
 	if ((command_info[info_len++] = sudo_new_key_val("login_class", login_class)) == NULL)
--- a/plugins/sudoers/sudoers.h	Tue Dec 29 13:38:14 2015 -0700
+++ b/plugins/sudoers/sudoers.h	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1993-1996, 1998-2005, 2007-2015
+ * Copyright (c) 1993-1996, 1998-2005, 2007-2016
  *	Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -90,6 +90,7 @@
     const char *cwd;
     char *iolog_file;
     GETGROUPS_T *gids;
+    int   execfd;
     int   ngids;
     int   closefrom;
     int   lines;
@@ -197,6 +198,7 @@
 #define user_srunhost		(sudo_user.srunhost)
 #define user_ccname		(sudo_user.krb5_ccname)
 #define safe_cmnd		(sudo_user.cmnd_safe)
+#define cmnd_fd			(sudo_user.execfd)
 #define login_class		(sudo_user.class_name)
 #define runas_pw		(sudo_user._runas_pw)
 #define runas_gr		(sudo_user._runas_gr)
--- a/src/exec.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/exec.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -176,13 +176,13 @@
 	}
 #ifdef HAVE_SELINUX
 	if (ISSET(details->flags, CD_RBAC_ENABLED)) {
-	    selinux_execve(details->command, details->argv, details->envp,
-		ISSET(details->flags, CD_NOEXEC));
+	    selinux_execve(details->execfd, details->command, details->argv,
+		details->envp, ISSET(details->flags, CD_NOEXEC));
 	} else
 #endif
 	{
-	    sudo_execve(details->command, details->argv, details->envp,
-		ISSET(details->flags, CD_NOEXEC));
+	    sudo_execve(details->execfd, details->command, details->argv,
+		details->envp, ISSET(details->flags, CD_NOEXEC));
 	}
     }
     cstat->type = CMD_ERRNO;
--- a/src/exec_common.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/exec_common.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -179,14 +179,19 @@
  * ala execvp(3) if we get ENOEXEC.
  */
 int
-sudo_execve(const char *path, char *const argv[], char *envp[], bool noexec)
+sudo_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec)
 {
     /* Modify the environment as needed to disable further execve(). */
     if (noexec)
 	envp = disable_execute(envp);
 
-    execve(path, argv, envp);
-    if (errno == ENOEXEC) {
+#ifdef HAVE_FEXECVE
+    if (fd != -1)
+	    fexecve(fd, argv, envp);
+    else
+#endif
+	    execve(path, argv, envp);
+    if (fd == -1 && errno == ENOEXEC) {
 	int argc;
 	char **nargv;
 
--- a/src/selinux.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/selinux.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  * Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
  *
  * Borrowed heavily from newrole source code
@@ -373,7 +373,7 @@
 }
 
 void
-selinux_execve(const char *path, char *const argv[], char *const envp[],
+selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
     bool noexec)
 {
     char **nargv;
@@ -409,6 +409,8 @@
      */
     for (argc = 0; argv[argc] != NULL; argc++)
 	continue;
+    if (fd != -1)
+	argc++;
     nargv = reallocarray(NULL, argc + 2, sizeof(char *));
     if (nargv == NULL) {
 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
@@ -418,11 +420,16 @@
 	nargv[0] = *argv[0] == '-' ? "-sesh-noexec" : "sesh-noexec";
     else
 	nargv[0] = *argv[0] == '-' ? "-sesh" : "sesh";
-    nargv[1] = (char *)path;
-    memcpy(&nargv[2], &argv[1], argc * sizeof(char *)); /* copies NULL */
+    argc = 1;
+    if (fd != -1 && asprintf(&nargv[argc++], "--execfd=%d", fd) == -1) {
+	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+	debug_return;
+    }
+    nargv[argc] = (char *)path;
+    memcpy(&nargv[argc + 1], &argv[argc], argc * sizeof(char *)); /* copies NULL */
 
     /* sesh will handle noexec for us. */
-    sudo_execve(sesh, nargv, envp, false);
+    sudo_execve(-1, sesh, nargv, envp, false);
     serrno = errno;
     free(nargv);
     errno = serrno;
--- a/src/sesh.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/sesh.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2008, 2010-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -87,6 +87,7 @@
     } else {
 	bool login_shell, noexec = false;
 	char *cp, *cmnd;
+	int fd = -1;
 
 	/* If the first char of argv[0] is '-', we are running a login shell. */
 	login_shell = argv[0][0] == '-';
@@ -95,6 +96,18 @@
 	if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0])
 	    noexec = strcmp(cp, "-noexec") == 0;
 
+	/* If argv[1] is --execfd=%d, extract the fd to exec with. */
+	if (strncmp(argv[1], "--execfd=", 9) == 0) {
+	    const char *errstr;
+
+	    cp = argv[1] + 9;
+	    fd = strtonum(cp, 0, INT_MAX, &errstr);
+	    if (errstr != NULL)
+		sudo_fatalx(U_("invalid file descriptor number: %s"), cp);
+	    argv++;
+	    argc--;
+	}
+
 	/* Shift argv and make a copy of the command to execute. */
 	argv++;
 	argc--;
@@ -108,7 +121,7 @@
 	    *cp = '-';
 	    argv[0] = cp;
 	}
-	sudo_execve(cmnd, argv, envp, noexec);
+	sudo_execve(fd, cmnd, argv, envp, noexec);
 	sudo_warn(U_("unable to execute %s"), cmnd);
 	ret = SESH_ERR_FAILURE;
     }
--- a/src/sudo.c	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/sudo.c	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -585,6 +585,7 @@
 
     memset(details, 0, sizeof(*details));
     details->closefrom = -1;
+    details->execfd = -1;
     TAILQ_INIT(&details->preserved_fds);
 
 #define SET_STRING(s, n) \
@@ -615,6 +616,21 @@
 			SET(details->flags, CD_EXEC_BG);
 		    break;
 		}
+		if (strncmp("execfd=", info[i], sizeof("execfd=") - 1) == 0) {
+		    cp = info[i] + sizeof("execfd=") - 1;
+		    details->execfd = strtonum(cp, 0, INT_MAX, &errstr);
+		    if (errstr != NULL)
+			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
+#ifdef HAVE_FEXECVE
+		    /* Must keep fd open during exec. */
+		    add_preserved_fd(&details->preserved_fds, details->execfd);
+#else
+		    /* Plugin thinks we support fexecve() but we don't. */
+		    fcntl(details->execfd, F_SETFD, FD_CLOEXEC);
+		    details->execfd = -1;
+#endif
+		    break;
+		}
 		break;
 	    case 'l':
 		SET_STRING("login_class=", login_class)
--- a/src/sudo.h	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/sudo.h	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1993-1996, 1998-2005, 2007-2014
+ * Copyright (c) 1993-1996, 1998-2005, 2007-2016
  *	Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -149,6 +149,7 @@
     int ngroups;
     int closefrom;
     int flags;
+    int execfd;
     struct preserved_fd_list preserved_fds;
     struct passwd *pw;
     GETGROUPS_T *groups;
@@ -221,8 +222,8 @@
 int selinux_restore_tty(void);
 int selinux_setup(const char *role, const char *type, const char *ttyn,
     int ttyfd);
-void selinux_execve(const char *path, char *const argv[], char *const envp[],
-    bool noexec);
+void selinux_execve(int fd, const char *path, char *const argv[],
+    char *envp[], bool noexec);
 
 /* solaris.c */
 void set_project(struct passwd *);
--- a/src/sudo_exec.h	Tue Dec 29 13:38:14 2015 -0700
+++ b/src/sudo_exec.h	Mon Jan 04 10:35:18 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -74,7 +74,7 @@
 
 /* exec.c */
 struct sudo_event_base;
-int sudo_execve(const char *path, char *const argv[], char *envp[], bool noexec);
+int sudo_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec);
 extern volatile pid_t cmnd_pid;
 
 /* exec_pty.c */