Add support for using fexecve() if supported on commands that are

checksummed.
This commit is contained in:
Todd C. Miller
2016-01-04 10:35:18 -07:00
parent 97e86c74ff
commit b20977d445
19 changed files with 209 additions and 68 deletions

View File

@@ -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);
}