# HG changeset patch # User Todd C. Miller # Date 1451928918 25200 # Node ID 397722cdd7eceef0aec561909418215e275ccd44 # Parent 33272418bb10ee780184dbd2d966a4e5c3bc597e Add support for using fexecve() if supported on commands that are checksummed. Reference to upstream patch: https://www.sudo.ws/repos/sudo/rev/397722cdd7ec CVE: CVE-2015-8239 Upstream-Status: Backport Signed-off-by: Sona Sarmadi --- diff -ruN a/configure b/configure --- a/configure 2015-11-01 00:35:24.000000000 +0100 +++ b/configure 2016-08-08 12:56:03.441681854 +0200 @@ -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" diff -ruN a/configure.ac b/configure.ac --- a/configure.ac 2016-08-08 12:55:08.781888802 +0200 +++ b/configure.ac 2016-08-08 12:56:03.445681547 +0200 @@ -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 +dnl Copyright (c) 1994-1996,1998-2016 Todd C. Miller dnl AC_PREREQ([2.59]) AC_INIT([sudo], [1.8.15], [http://www.sudo.ws/bugs/], [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 diff -ruN a/doc/sudoers.cat b/doc/sudoers.cat --- a/doc/sudoers.cat 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudoers.cat 2016-08-08 12:56:03.445681547 +0200 @@ -410,6 +410,13 @@ $ openssl dgst -binary -sha224 /bin/ls | openssl base64 EYGH2oNk1JC0p9679IMATo8+BT7JVDCd4sQaJQ== + Warning, if the user has write access to the command itself (directly or + via a 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. 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. DDeeffaauullttss diff -ruN a/doc/sudoers.man.in b/doc/sudoers.man.in --- a/doc/sudoers.man.in 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudoers.man.in 2016-08-08 12:56:03.445681547 +0200 @@ -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 .\" .\" Permission to use, copy, modify, and distribute this software for any @@ -877,6 +877,15 @@ .RE .fi .PP +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. +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" Certain configuration options may be changed from their default diff -ruN a/doc/sudoers.mdoc.in b/doc/sudoers.mdoc.in --- a/doc/sudoers.mdoc.in 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudoers.mdoc.in 2016-08-08 12:56:03.449681239 +0200 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 1994-1996, 1998-2005, 2007-2015 +.\" Copyright (c) 1994-1996, 1998-2005, 2007-2016 .\" Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any @@ -834,6 +834,15 @@ EYGH2oNk1JC0p9679IMATo8+BT7JVDCd4sQaJQ== .Ed .Pp +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. +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 Certain configuration options may be changed from their default diff -ruN a/doc/sudo_plugin.cat b/doc/sudo_plugin.cat --- a/doc/sudo_plugin.cat 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudo_plugin.cat 2016-08-08 12:56:03.449681239 +0200 @@ -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) diff -ruN a/doc/sudo_plugin.man.in b/doc/sudo_plugin.man.in --- a/doc/sudo_plugin.man.in 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudo_plugin.man.in 2016-08-08 12:56:03.449681239 +0200 @@ -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 +.\" Copyright (c) 2009-2016 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -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@), diff -ruN a/doc/sudo_plugin.mdoc.in b/doc/sudo_plugin.mdoc.in --- a/doc/sudo_plugin.mdoc.in 2016-08-08 12:55:08.781888802 +0200 +++ b/doc/sudo_plugin.mdoc.in 2016-08-08 12:56:03.453680931 +0200 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 2009-2015 Todd C. Miller +.\" Copyright (c) 2009-2016 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -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@ , diff -ruN a/include/sudo_plugin.h b/include/sudo_plugin.h --- a/include/sudo_plugin.h 2016-08-08 12:55:08.781888802 +0200 +++ b/include/sudo_plugin.h 2016-08-08 12:56:03.453680931 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Todd C. Miller + * Copyright (c) 2009-2016 Todd C. Miller * * 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) diff -ruN a/plugins/sudoers/match.c b/plugins/sudoers/match.c --- a/plugins/sudoers/match.c 2016-08-08 12:55:08.781888802 +0200 +++ b/plugins/sudoers/match.c 2016-08-08 12:56:03.453680931 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 1998-2005, 2007-2015 + * Copyright (c) 1996, 1998-2005, 2007-2016 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -55,6 +55,7 @@ # include #endif /* HAVE_NETGROUP_H */ #include +#include #include #include #include @@ -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); } diff -ruN a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c --- a/plugins/sudoers/policy.c 2016-08-08 12:55:08.781888802 +0200 +++ b/plugins/sudoers/policy.c 2016-08-08 12:56:03.457680623 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Todd C. Miller + * Copyright (c) 2010-2016 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -371,6 +371,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); @@ -545,6 +548,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) diff -ruN a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h --- a/plugins/sudoers/sudoers.h 2016-08-08 12:55:08.781888802 +0200 +++ b/plugins/sudoers/sudoers.h 2016-08-08 12:56:03.457680623 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993-1996, 1998-2005, 2007-2015 + * Copyright (c) 1993-1996, 1998-2005, 2007-2016 * Todd C. Miller * * 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) diff -ruN a/src/exec.c b/src/exec.c --- a/src/exec.c 2016-08-08 12:55:08.781888802 +0200 +++ b/src/exec.c 2016-08-08 12:56:03.457680623 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Todd C. Miller + * Copyright (c) 2009-2016 Todd C. Miller * * 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; diff -ruN a/src/exec_common.c b/src/exec_common.c --- a/src/exec_common.c 2016-08-08 12:55:08.781888802 +0200 +++ b/src/exec_common.c 2016-08-08 12:56:03.457680623 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Todd C. Miller + * Copyright (c) 2009-2016 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -134,14 +134,19 @@ * ala execvp(3) if we get ENOEXEC. */ int -sudo_execve(const char *path, char *const argv[], char *const envp[], bool noexec) +sudo_execve(int fd, const char *path, char *const argv[], char *const 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; diff -ruN a/src/selinux.c b/src/selinux.c --- a/src/selinux.c 2016-08-08 12:55:08.781888802 +0200 +++ b/src/selinux.c 2016-08-08 12:56:03.461680315 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Todd C. Miller + * Copyright (c) 2009-2016 Todd C. Miller * Copyright (c) 2008 Dan Walsh * * 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 *const envp[], int 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; diff -ruN a/src/sesh.c b/src/sesh.c --- a/src/sesh.c 2016-08-08 12:55:08.781888802 +0200 +++ b/src/sesh.c 2016-08-08 12:56:03.461680315 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010-2015 Todd C. Miller + * Copyright (c) 2008, 2010-2016 Todd C. Miller * * 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; } diff -ruN a/src/sudo.c b/src/sudo.c --- a/src/sudo.c 2016-08-08 12:55:08.781888802 +0200 +++ b/src/sudo.c 2016-08-08 12:56:03.461680315 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Todd C. Miller + * Copyright (c) 2009-2016 Todd C. Miller * * 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) diff -ruN a/src/sudo_exec.h b/src/sudo_exec.h --- a/src/sudo_exec.h 2016-08-08 12:55:08.781888802 +0200 +++ b/src/sudo_exec.h 2016-08-08 13:04:19.127533565 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Todd C. Miller + * Copyright (c) 2010-2016 Todd C. Miller * * 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 *const envp[], bool noexec); +int sudo_execve(int fd, const char *path, char *const argv[], char *const envp[], bool noexec); extern volatile pid_t cmnd_pid; /* exec_pty.c */ diff -ruN a/src/sudo.h b/src/sudo.h --- a/src/sudo.h 2016-08-08 12:55:08.781888802 +0200 +++ b/src/sudo.h 2016-08-08 12:56:03.465680007 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993-1996, 1998-2005, 2007-2014 + * Copyright (c) 1993-1996, 1998-2005, 2007-2016 * Todd C. Miller * * 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,7 +222,7 @@ 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[], +void selinux_execve(int fd, const char *path, char *const argv[], char *envp[], int noexec); /* solaris.c */