Add support for digest matching when the command is a glob-style
pattern or a directory. For example: millert ALL = sha224:TmUvLkp3a2txliSC2X6CiK42626qdKsH72m/PQ== /bin/ millert ALL = sha224:TmUvLkp3a2txliSC2X6CiK42626qdKsH72m/PQ== /bin/* would only match /bin/ls (assuming the digest matches). Previously, only explicit path matches checked the digest.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 1998-2005, 2007-2016
|
* Copyright (c) 1996, 1998-2005, 2007-2017
|
||||||
* Todd C. Miller <Todd.Miller@courtesan.com>
|
* Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
@@ -77,12 +77,13 @@
|
|||||||
|
|
||||||
static struct member_list empty = TAILQ_HEAD_INITIALIZER(empty);
|
static struct member_list empty = TAILQ_HEAD_INITIALIZER(empty);
|
||||||
|
|
||||||
static bool command_matches_dir(const char *sudoers_dir, size_t dlen);
|
static bool command_matches_dir(const char *sudoers_dir, size_t dlen, const struct sudo_digest *digest);
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args);
|
static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
|
||||||
#endif
|
#endif
|
||||||
static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args);
|
static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
|
||||||
static bool command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
|
static bool command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
|
||||||
|
static bool digest_matches(const char *file, const struct sudo_digest *sd, int *fd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if string 's' contains meta characters.
|
* Returns true if string 's' contains meta characters.
|
||||||
@@ -413,12 +414,12 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct
|
|||||||
* use glob(3) and/or fnmatch(3) to do the matching.
|
* use glob(3) and/or fnmatch(3) to do the matching.
|
||||||
*/
|
*/
|
||||||
#ifdef SUDOERS_NAME_MATCH
|
#ifdef SUDOERS_NAME_MATCH
|
||||||
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args);
|
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digest);
|
||||||
#else
|
#else
|
||||||
if (def_fast_glob)
|
if (def_fast_glob)
|
||||||
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args);
|
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digest);
|
||||||
else
|
else
|
||||||
rc = command_matches_glob(sudoers_cmnd, sudoers_args);
|
rc = command_matches_glob(sudoers_cmnd, sudoers_args, digest);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest);
|
rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest);
|
||||||
@@ -433,7 +434,8 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args)
|
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
|
const struct sudo_digest *digest)
|
||||||
{
|
{
|
||||||
debug_decl(command_matches_fnmatch, SUDOERS_DEBUG_MATCH)
|
debug_decl(command_matches_fnmatch, SUDOERS_DEBUG_MATCH)
|
||||||
|
|
||||||
@@ -447,6 +449,13 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
|
if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
|
||||||
debug_return_bool(false);
|
debug_return_bool(false);
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
|
if (cmnd_fd != -1) {
|
||||||
|
close(cmnd_fd);
|
||||||
|
cmnd_fd = -1;
|
||||||
|
}
|
||||||
|
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
||||||
|
if (digest != NULL && !digest_matches(user_cmnd, digest, &cmnd_fd))
|
||||||
|
debug_return_bool(false);
|
||||||
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
@@ -455,11 +464,14 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
|
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
static bool
|
static bool
|
||||||
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
|
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
|
const struct sudo_digest *digest)
|
||||||
{
|
{
|
||||||
struct stat sudoers_stat;
|
struct stat sudoers_stat;
|
||||||
size_t dlen;
|
bool bad_digest = false;
|
||||||
char **ap, *base, *cp;
|
char **ap, *base, *cp;
|
||||||
|
int fd = -1;
|
||||||
|
size_t dlen;
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH)
|
debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH)
|
||||||
|
|
||||||
@@ -495,6 +507,15 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
if (user_stat == NULL ||
|
if (user_stat == NULL ||
|
||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
|
if (fd != -1) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
/* There could be multiple matches, check digest early. */
|
||||||
|
if (digest != NULL && !digest_matches(cp, digest, &fd)) {
|
||||||
|
bad_digest = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(cp)) == NULL) {
|
if ((safe_cmnd = strdup(cp)) == NULL) {
|
||||||
sudo_warnx(U_("%s: %s"), __func__,
|
sudo_warnx(U_("%s: %s"), __func__,
|
||||||
@@ -509,11 +530,12 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* No exact match, compare basename, st_dev and st_ino. */
|
/* No exact match, compare basename, st_dev and st_ino. */
|
||||||
|
if (!bad_digest) {
|
||||||
for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
|
for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
|
||||||
/* If it ends in '/' it is a directory spec. */
|
/* If it ends in '/' it is a directory spec. */
|
||||||
dlen = strlen(cp);
|
dlen = strlen(cp);
|
||||||
if (cp[dlen - 1] == '/') {
|
if (cp[dlen - 1] == '/') {
|
||||||
if (command_matches_dir(cp, dlen))
|
if (command_matches_dir(cp, dlen, digest))
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -529,6 +551,12 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
if (user_stat == NULL ||
|
if (user_stat == NULL ||
|
||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
|
if (fd != -1) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
if (digest != NULL && !digest_matches(cp, digest, &fd))
|
||||||
|
continue;
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(cp)) == NULL) {
|
if ((safe_cmnd = strdup(cp)) == NULL) {
|
||||||
sudo_warnx(U_("%s: %s"), __func__,
|
sudo_warnx(U_("%s: %s"), __func__,
|
||||||
@@ -538,15 +566,23 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
globfree(&gl);
|
globfree(&gl);
|
||||||
if (cp == NULL)
|
if (cp != NULL) {
|
||||||
debug_return_bool(false);
|
|
||||||
|
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
/* safe_cmnd was set above. */
|
/* safe_cmnd was set above. */
|
||||||
|
if (cmnd_fd != -1) {
|
||||||
|
close(cmnd_fd);
|
||||||
|
cmnd_fd = -1;
|
||||||
|
}
|
||||||
|
if (fd != -1)
|
||||||
|
cmnd_fd = fd;
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
debug_return_bool(false);
|
debug_return_bool(false);
|
||||||
}
|
}
|
||||||
#endif /* SUDOERS_NAME_MATCH */
|
#endif /* SUDOERS_NAME_MATCH */
|
||||||
@@ -562,10 +598,11 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
|||||||
|
|
||||||
/* If it ends in '/' it is a directory spec. */
|
/* If it ends in '/' it is a directory spec. */
|
||||||
if (sudoers_cmnd[dlen - 1] == '/')
|
if (sudoers_cmnd[dlen - 1] == '/')
|
||||||
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
|
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digest));
|
||||||
|
|
||||||
if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
|
if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
|
/* XXX - check digest */
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(sudoers_cmnd)) != NULL)
|
if ((safe_cmnd = strdup(sudoers_cmnd)) != NULL)
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
@@ -733,7 +770,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
|||||||
/* If it ends in '/' it is a directory spec. */
|
/* If it ends in '/' it is a directory spec. */
|
||||||
dlen = strlen(sudoers_cmnd);
|
dlen = strlen(sudoers_cmnd);
|
||||||
if (sudoers_cmnd[dlen - 1] == '/')
|
if (sudoers_cmnd[dlen - 1] == '/')
|
||||||
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
|
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digest));
|
||||||
|
|
||||||
/* Only proceed if user_base and basename(sudoers_cmnd) match */
|
/* Only proceed if user_base and basename(sudoers_cmnd) match */
|
||||||
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
|
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
|
||||||
@@ -780,9 +817,11 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
|||||||
* Note that sudoers_dir include the trailing '/'
|
* Note that sudoers_dir include the trailing '/'
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
command_matches_dir(const char *sudoers_dir, size_t dlen)
|
command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||||
|
const struct sudo_digest *digest)
|
||||||
{
|
{
|
||||||
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH)
|
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH)
|
||||||
|
/* XXX - check digest */
|
||||||
debug_return_bool(strncmp(user_cmnd, sudoers_dir, dlen) == 0);
|
debug_return_bool(strncmp(user_cmnd, sudoers_dir, dlen) == 0);
|
||||||
}
|
}
|
||||||
#else /* !SUDOERS_NAME_MATCH */
|
#else /* !SUDOERS_NAME_MATCH */
|
||||||
@@ -790,11 +829,13 @@ command_matches_dir(const char *sudoers_dir, size_t dlen)
|
|||||||
* Return true if user_cmnd names one of the inodes in dir, else false.
|
* Return true if user_cmnd names one of the inodes in dir, else false.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
command_matches_dir(const char *sudoers_dir, size_t dlen)
|
command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||||
|
const struct sudo_digest *digest)
|
||||||
{
|
{
|
||||||
struct stat sudoers_stat;
|
struct stat sudoers_stat;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
|
int fd = -1;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH)
|
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH)
|
||||||
|
|
||||||
@@ -822,6 +863,12 @@ command_matches_dir(const char *sudoers_dir, size_t dlen)
|
|||||||
if (user_stat == NULL ||
|
if (user_stat == NULL ||
|
||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
|
if (fd != -1) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
if (digest != NULL && !digest_matches(buf, digest, &fd))
|
||||||
|
continue;
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(buf)) == NULL) {
|
if ((safe_cmnd = strdup(buf)) == NULL) {
|
||||||
sudo_warnx(U_("%s: %s"), __func__,
|
sudo_warnx(U_("%s: %s"), __func__,
|
||||||
@@ -831,9 +878,20 @@ command_matches_dir(const char *sudoers_dir, size_t dlen)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
debug_return_bool(dent != NULL);
|
|
||||||
|
if (dent != NULL) {
|
||||||
|
if (cmnd_fd != -1) {
|
||||||
|
close(cmnd_fd);
|
||||||
|
cmnd_fd = -1;
|
||||||
|
}
|
||||||
|
if (fd != -1)
|
||||||
|
cmnd_fd = fd;
|
||||||
|
debug_return_bool(true);
|
||||||
|
}
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
debug_return_bool(false);
|
||||||
}
|
}
|
||||||
#endif /* SUDOERS_NAME_MATCH */
|
#endif /* SUDOERS_NAME_MATCH */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user