Add support for using fexecve() if supported on commands that are
checksummed.
This commit is contained in:
@@ -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 struct digest_function {
|
||||
};
|
||||
|
||||
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 @@ digest_matches(const char *file, const struct sudo_digest *sd)
|
||||
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 @@ digest_matches(const char *file, const struct sudo_digest *sd)
|
||||
|
||||
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 @@ digest_matches(const char *file, const struct sudo_digest *sd)
|
||||
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 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user