Allow a list of digests to be specified for a command.

This commit is contained in:
Todd C. Miller
2020-03-11 11:17:52 -06:00
parent 8c08f5ef03
commit 4eca443246
19 changed files with 662 additions and 520 deletions

View File

@@ -887,6 +887,9 @@ Digest_Spec ::= "sha224" ':' digest |
"sha384" ':' digest |
"sha512" ':' digest
Digest_List ::= Digest_Spec |
Digest_Spec ',' Digest_List
Cmnd_List ::= Cmnd |
Cmnd ',' Cmnd_List
@@ -894,7 +897,7 @@ command name ::= file name |
file name args |
file name '""'
Cmnd ::= Digest_Spec? '!'* command name |
Cmnd ::= Digest_List? '!'* command name |
'!'* directory |
'!'* "sudoedit" |
'!'* Cmnd_Alias
@@ -967,12 +970,17 @@ A fully-qualified path for
is treated as an error by
\fBvisudo\fR.
.PP
If a
A
\fRcommand name\fR
is prefixed with a
\fRDigest_Spec\fR,
the command will only match successfully if it can be verified
using the specified SHA-2 digest.
may be preceded by a
\fRDigest_List\fR,
a comma-separated list of one or more
\fRDigest_Spec\fR
entries.
If a
\fRDigest_List\fR
is present, the command will only match successfully if it can be verified
using one of the SHA-2 digests in the list.
The following digest formats are supported: sha224, sha256, sha384 and sha512.
The string may be specified in either hex or base64 format
(base64 is more compact).

View File

@@ -852,6 +852,9 @@ Digest_Spec ::= "sha224" ':' digest |
"sha384" ':' digest |
"sha512" ':' digest
Digest_List ::= Digest_Spec |
Digest_Spec ',' Digest_List
Cmnd_List ::= Cmnd |
Cmnd ',' Cmnd_List
@@ -859,7 +862,7 @@ command name ::= file name |
file name args |
file name '""'
Cmnd ::= Digest_Spec? '!'* command name |
Cmnd ::= Digest_List? '!'* command name |
'!'* directory |
'!'* "sudoedit" |
'!'* Cmnd_Alias
@@ -931,12 +934,17 @@ A fully-qualified path for
is treated as an error by
.Nm visudo .
.Pp
If a
A
.Li command name
is prefixed with a
.Li Digest_Spec ,
the command will only match successfully if it can be verified
using the specified SHA-2 digest.
may be preceded by a
.Li Digest_List ,
a comma-separated list of one or more
.Li Digest_Spec
entries.
If a
.Li Digest_List
is present, the command will only match successfully if it can be verified
using one of the SHA-2 digests in the list.
The following digest formats are supported: sha224, sha256, sha384 and sha512.
The string may be specified in either hex or base64 format
(base64 is more compact).

View File

@@ -70,6 +70,7 @@ static void
print_command_json(struct json_container *json, const char *name, bool negated)
{
struct sudo_command *c = (struct sudo_command *)name;
struct command_digest *digest;
struct json_value value;
char *cmnd = c->cmnd;
const char *digest_name;
@@ -85,7 +86,7 @@ print_command_json(struct json_container *json, const char *name, bool negated)
value.type = JSON_STRING;
value.u.string = cmnd;
if (!negated && c->digest == NULL) {
if (!negated && TAILQ_EMPTY(&c->digests)) {
/* Print as { "command": "command and args" } */
sudo_json_add_value_as_object(json, "command", &value);
} else {
@@ -93,11 +94,11 @@ print_command_json(struct json_container *json, const char *name, bool negated)
sudo_json_open_object(json, NULL);
sudo_json_add_value(json, "command", &value);
/* Optional digest. */
if (c->digest != NULL) {
digest_name = digest_type_to_name(c->digest->digest_type);
/* Optional digest list. */
TAILQ_FOREACH(digest, &c->digests, entries) {
digest_name = digest_type_to_name(digest->digest_type);
value.type = JSON_STRING;
value.u.string = c->digest->digest_str;
value.u.string = digest->digest_str;
sudo_json_add_value(json, digest_name, &value);
}

View File

@@ -209,6 +209,52 @@ print_global_defaults_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree,
debug_return_bool(!ferror(fp));
}
/*
* Format a sudo_command as a string.
* Returns the formatted, dynamically allocated string or dies on error.
*/
static char *
format_cmnd(struct sudo_command *c, bool negated)
{
struct command_digest *digest;
char *buf, *cp;
size_t bufsiz;
int len;
debug_decl(format_cmnd, SUDOERS_DEBUG_UTIL);
bufsiz = negated + strlen(c->cmnd) + 1;
if (c->args != NULL)
bufsiz += 1 + strlen(c->args);
TAILQ_FOREACH(digest, &c->digests, entries) {
bufsiz += strlen(digest_type_to_name(digest->digest_type)) + 1 +
strlen(digest->digest_str) + 1;
if (TAILQ_NEXT(digest, entries) != NULL)
bufsiz += 2;
}
if ((buf = malloc(bufsiz)) == NULL) {
sudo_fatalx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
}
cp = buf;
TAILQ_FOREACH(digest, &c->digests, entries) {
len = snprintf(cp, bufsiz - (cp - buf), "%s:%s%s ",
digest_type_to_name(digest->digest_type), digest->digest_str,
TAILQ_NEXT(digest, entries) ? "," : "");
if (len < 0 || len >= (int)bufsiz - (cp - buf))
sudo_fatalx(U_("internal error, %s overflow"), __func__);
cp += len;
}
len = snprintf(cp, bufsiz - (cp - buf), "%s%s%s%s", negated ? "!" : "",
c->cmnd, c->args ? " " : "", c->args ? c->args : "");
if (len < 0 || len >= (int)bufsiz - (cp - buf))
sudo_fatalx(U_("internal error, %s overflow"), __func__);
debug_return_str(buf);
}
/*
* Print struct member in LDIF format as the specified attribute.
* See print_member_int() in parse.c.
@@ -219,7 +265,6 @@ print_member_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, char *name,
{
struct alias *a;
struct member *m;
struct sudo_command *c;
char *attr_val;
int len;
debug_decl(print_member_ldif, SUDOERS_DEBUG_UTIL);
@@ -233,16 +278,7 @@ print_member_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, char *name,
print_attribute_ldif(fp, attr_name, "");
break;
case COMMAND:
c = (struct sudo_command *)name;
len = asprintf(&attr_val, "%s%s%s%s%s%s%s%s",
c->digest ? digest_type_to_name(c->digest->digest_type) : "",
c->digest ? ":" : "", c->digest ? c->digest->digest_str : "",
c->digest ? " " : "", negated ? "!" : "", c->cmnd,
c->args ? " " : "", c->args ? c->args : "");
if (len == -1) {
sudo_fatalx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
}
attr_val = format_cmnd((struct sudo_command *)name, negated);
print_attribute_ldif(fp, attr_name, attr_val);
free(attr_val);
break;

View File

@@ -51,6 +51,7 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf,
struct alias *a;
struct member *m;
struct sudo_command *c;
struct command_digest *digest;
debug_decl(sudoers_format_member_int, SUDOERS_DEBUG_UTIL);
switch (type) {
@@ -63,10 +64,10 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf,
break;
case COMMAND:
c = (struct sudo_command *) name;
if (c->digest != NULL) {
sudo_lbuf_append(lbuf, "%s:%s ",
digest_type_to_name(c->digest->digest_type),
c->digest->digest_str);
TAILQ_FOREACH(digest, &c->digests, entries) {
sudo_lbuf_append(lbuf, "%s:%s%s ",
digest_type_to_name(digest->digest_type),
digest->digest_str, TAILQ_NEXT(digest, entries) ? "," : "");
}
if (negated)
sudo_lbuf_append(lbuf, "!");

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2018
* Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2020
* Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -174,7 +174,8 @@ static struct command_digest *new_digest(int, char *);
%type <string> timeoutspec
%type <string> notbeforespec
%type <string> notafterspec
%type <digest> digest
%type <digest> digestspec
%type <digest> digestlist
%%
@@ -451,7 +452,7 @@ cmndspec : runasspec options cmndtag digcmnd {
}
;
digest : SHA224_TOK ':' DIGEST {
digestspec : SHA224_TOK ':' DIGEST {
$$ = new_digest(SUDO_DIGEST_SHA224, $3);
if ($$ == NULL) {
sudoerserror(N_("unable to allocate memory"));
@@ -481,16 +482,25 @@ digest : SHA224_TOK ':' DIGEST {
}
;
digestlist : digestspec
| digestlist ',' digestspec {
HLTQ_CONCAT($1, $3, entries);
$$ = $1;
}
;
digcmnd : opcmnd {
$$ = $1;
}
| digest opcmnd {
| digestlist opcmnd {
struct sudo_command *c =
(struct sudo_command *) $2->name;
if ($2->type != COMMAND) {
sudoerserror(N_("a digest requires a path name"));
YYERROR;
}
/* XXX - yuck */
((struct sudo_command *) $2->name)->digest = $1;
HLTQ_TO_TAILQ(&c->digests, $1, entries);
$$ = $2;
}
;
@@ -730,6 +740,7 @@ cmnd : ALL {
}
c->cmnd = $1.cmnd;
c->args = $1.args;
TAILQ_INIT(&c->digests);
$$ = new_member((char *)c, COMMAND);
if ($$ == NULL) {
free(c);
@@ -992,6 +1003,7 @@ new_digest(int digest_type, char *digest_str)
debug_return_ptr(NULL);
}
HLTQ_INIT(digest, entries);
digest->digest_type = digest_type;
digest->digest_str = digest_str;
if (digest->digest_str == NULL) {
@@ -1080,12 +1092,14 @@ free_member(struct member *m)
debug_decl(free_member, SUDOERS_DEBUG_PARSER);
if (m->type == COMMAND) {
struct command_digest *digest;
struct sudo_command *c = (struct sudo_command *)m->name;
free(c->cmnd);
free(c->args);
if (c->digest != NULL) {
free(c->digest->digest_str);
free(c->digest);
while ((digest = TAILQ_FIRST(&c->digests)) != NULL) {
TAILQ_REMOVE(&c->digests, digest, entries);
free(digest->digest_str);
free(digest);
}
}
free(m->name);

View File

@@ -288,6 +288,110 @@ oom:
debug_return_bool(false);
}
/*
* If a digest prefix is present, add it to struct command_digest_list
* and update cmnd to point to the command after the digest.
* Returns 1 if a digest was parsed, 0 if not and -1 on error.
*/
static int
sudo_ldap_extract_digest(char **cmnd, struct command_digest_list *digests)
{
char *ep, *cp = *cmnd;
struct command_digest *digest;
int digest_type = SUDO_DIGEST_INVALID;
debug_decl(sudo_ldap_extract_digest, SUDOERS_DEBUG_LDAP);
/*
* Check for and extract a digest prefix, e.g.
* sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
*/
if (cp[0] == 's' && cp[1] == 'h' && cp[2] == 'a') {
switch (cp[3]) {
case '2':
if (cp[4] == '2' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA224;
else if (cp[4] == '5' && cp[5] == '6')
digest_type = SUDO_DIGEST_SHA256;
break;
case '3':
if (cp[4] == '8' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA384;
break;
case '5':
if (cp[4] == '1' && cp[5] == '2')
digest_type = SUDO_DIGEST_SHA512;
break;
}
if (digest_type != SUDO_DIGEST_INVALID) {
cp += 6;
while (isblank((unsigned char)*cp))
cp++;
if (*cp == ':') {
cp++;
while (isblank((unsigned char)*cp))
cp++;
ep = cp;
while (*ep != '\0' && !isblank((unsigned char)*ep) && *ep != ',')
ep++;
if (isblank((unsigned char)*ep) || *ep == ',') {
if ((digest = malloc(sizeof(*digest))) == NULL) {
sudo_warnx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
debug_return_int(-1);
}
digest->digest_type = digest_type;
digest->digest_str = strndup(cp, (size_t)(ep - cp));
if (digest->digest_str == NULL) {
sudo_warnx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
free(digest);
debug_return_int(-1);
}
while (isblank((unsigned char)*ep))
ep++;
*cmnd = ep;
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s digest %s for %s",
digest_type_to_name(digest_type),
digest->digest_str, cp);
TAILQ_INSERT_TAIL(digests, digest, entries);
debug_return_int(1);
}
}
}
}
debug_return_int(0);
}
/*
* If a digest list is present, fill in struct command_digest_list
* and update cmnd to point to the command after the digest.
* Returns false on error, else true.
*/
static bool
sudo_ldap_extract_digests(char **cmnd, struct command_digest_list *digests)
{
char *cp = *cmnd;
int rc;
debug_decl(sudo_ldap_extract_digests, SUDOERS_DEBUG_LDAP);
for (;;) {
rc = sudo_ldap_extract_digest(&cp, digests);
if (rc != 1)
break;
/* Check for additional digestspecs, separated by a comma. */
if (*cp != ',')
break;
do {
cp++;
} while (isblank((unsigned char)*cp));
}
*cmnd = cp;
debug_return_bool(rc != -1);
}
/*
* Convert an LDAP sudoRole to a sudoers privilege.
* Pass in struct berval ** for LDAP or char *** for SSSD.
@@ -358,6 +462,7 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers,
goto oom;
}
m->name = (char *)c;
TAILQ_INIT(&c->digests);
}
/* Negated commands have precedence so insert them at the end. */
@@ -481,17 +586,13 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers,
if (cmndspec->tags.setenv == UNSPEC)
cmndspec->tags.setenv = IMPLIED;
} else {
struct command_digest digest;
char *args;
m->type = COMMAND;
/* Fill in command with optional digest. */
if (sudo_ldap_extract_digest(&cmnd, &digest) != NULL) {
if ((c->digest = malloc(sizeof(*c->digest))) == NULL)
/* Fill in command with optional digests. */
if (!sudo_ldap_extract_digests(&cmnd, &c->digests))
goto oom;
*c->digest = digest;
}
if ((args = strpbrk(cmnd, " \t")) != NULL) {
*args++ = '\0';
if ((c->args = strdup(args)) == NULL)
@@ -515,70 +616,3 @@ oom:
}
debug_return_ptr(NULL);
}
/*
* If a digest prefix is present, fills in struct command_digest
* and returns a pointer to it, updating cmnd to point to the
* command after the digest.
*/
struct command_digest *
sudo_ldap_extract_digest(char **cmnd, struct command_digest *digest)
{
char *ep, *cp = *cmnd;
int digest_type = SUDO_DIGEST_INVALID;
debug_decl(sudo_ldap_check_command, SUDOERS_DEBUG_LDAP);
/*
* Check for and extract a digest prefix, e.g.
* sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
*/
if (cp[0] == 's' && cp[1] == 'h' && cp[2] == 'a') {
switch (cp[3]) {
case '2':
if (cp[4] == '2' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA224;
else if (cp[4] == '5' && cp[5] == '6')
digest_type = SUDO_DIGEST_SHA256;
break;
case '3':
if (cp[4] == '8' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA384;
break;
case '5':
if (cp[4] == '1' && cp[5] == '2')
digest_type = SUDO_DIGEST_SHA512;
break;
}
if (digest_type != SUDO_DIGEST_INVALID) {
cp += 6;
while (isblank((unsigned char)*cp))
cp++;
if (*cp == ':') {
cp++;
while (isblank((unsigned char)*cp))
cp++;
ep = cp;
while (*ep != '\0' && !isblank((unsigned char)*ep))
ep++;
if (*ep != '\0') {
digest->digest_type = digest_type;
digest->digest_str = strndup(cp, (size_t)(ep - cp));
if (digest->digest_str == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_ptr(NULL);
}
cp = ep + 1;
while (isblank((unsigned char)*cp))
cp++;
*cmnd = cp;
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s digest %s for %s",
digest_type_to_name(digest_type),
digest->digest_str, cp);
debug_return_ptr(digest);
}
}
}
}
debug_return_ptr(NULL);
}

View File

@@ -411,7 +411,7 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m)
break;
case COMMAND:
c = (struct sudo_command *)m->name;
if (command_matches(c->cmnd, c->args, c->digest))
if (command_matches(c->cmnd, c->args, &c->digests))
matched = !m->negated;
break;
}

View File

@@ -126,18 +126,18 @@ is_script(int fd)
* Returns false on error, else true.
*/
static bool
open_cmnd(const char *path, const struct command_digest *digest, int *fdp)
open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp)
{
int fd = -1;
debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH);
/* Only open the file for fdexec or for digest matching. */
if (def_fdexec != always && digest == NULL)
if (def_fdexec != always && TAILQ_EMPTY(digests))
debug_return_bool(true);
fd = open(path, O_RDONLY|O_NONBLOCK);
# ifdef O_EXEC
if (fd == -1 && errno == EACCES && digest == NULL) {
if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests))
/* Try again with O_EXEC if no digest is specified. */
const int saved_errno = errno;
if ((fd = open(path, O_EXEC)) == -1)
@@ -197,7 +197,7 @@ set_cmnd_fd(int fd)
*/
static bool
command_matches_dir(const char *sudoers_dir, size_t dlen,
const struct command_digest *digest)
const struct command_digest_list *digests)
{
struct stat sudoers_stat;
struct dirent *dent;
@@ -233,7 +233,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
continue;
/* Open the file for fdexec or for digest matching. */
if (!open_cmnd(buf, digest, &fd))
if (!open_cmnd(buf, digests, &fd))
continue;
if (!do_stat(fd, buf, &sudoers_stat))
continue;
@@ -241,7 +241,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
if (user_stat == NULL ||
(user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino)) {
if (digest != NULL && !digest_matches(fd, buf, digest))
if (!digest_matches(fd, buf, digests))
continue;
free(safe_cmnd);
if ((safe_cmnd = strdup(buf)) == NULL) {
@@ -265,7 +265,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
static bool
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
const struct command_digest *digest)
const struct command_digest_list *digests)
{
struct stat sb; /* XXX - unused */
int fd = -1;
@@ -282,12 +282,12 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
debug_return_bool(false);
if (command_args_match(sudoers_cmnd, sudoers_args)) {
/* Open the file for fdexec or for digest matching. */
if (!open_cmnd(user_cmnd, digest, &fd))
if (!open_cmnd(user_cmnd, digests, &fd))
goto bad;
if (!do_stat(fd, user_cmnd, &sb))
goto bad;
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
if (digest != NULL && !digest_matches(fd, user_cmnd, digest))
if (!digest_matches(fd, user_cmnd, digests))
goto bad;
set_cmnd_fd(fd);
@@ -305,7 +305,7 @@ bad:
static bool
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
const struct command_digest *digest)
const struct command_digest_list *digests)
{
struct stat sudoers_stat;
bool bad_digest = false;
@@ -349,7 +349,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
if (strcmp(cp, user_cmnd) != 0)
continue;
/* Open the file for fdexec or for digest matching. */
if (!open_cmnd(cp, digest, &fd))
if (!open_cmnd(cp, digests, &fd))
continue;
if (!do_stat(fd, cp, &sudoers_stat))
continue;
@@ -357,7 +357,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
(user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino)) {
/* There could be multiple matches, check digest early. */
if (digest != NULL && !digest_matches(fd, cp, digest)) {
if (!digest_matches(fd, cp, digests)) {
bad_digest = true;
continue;
}
@@ -385,7 +385,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
/* If it ends in '/' it is a directory spec. */
dlen = strlen(cp);
if (cp[dlen - 1] == '/') {
if (command_matches_dir(cp, dlen, digest))
if (command_matches_dir(cp, dlen, digests))
debug_return_bool(true);
continue;
}
@@ -399,14 +399,14 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
continue;
/* Open the file for fdexec or for digest matching. */
if (!open_cmnd(cp, digest, &fd))
if (!open_cmnd(cp, digests, &fd))
continue;
if (!do_stat(fd, cp, &sudoers_stat))
continue;
if (user_stat == NULL ||
(user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino)) {
if (digest != NULL && !digest_matches(fd, cp, digest))
if (!digest_matches(fd, cp, digests))
continue;
free(safe_cmnd);
if ((safe_cmnd = strdup(cp)) == NULL) {
@@ -433,7 +433,7 @@ done:
}
static bool
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest)
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests)
{
struct stat sudoers_stat;
const char *base;
@@ -444,7 +444,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
/* If it ends in '/' it is a directory spec. */
dlen = strlen(sudoers_cmnd);
if (sudoers_cmnd[dlen - 1] == '/')
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digest));
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digests));
/* Only proceed if user_base and basename(sudoers_cmnd) match */
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
@@ -455,7 +455,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
debug_return_bool(false);
/* Open the file for fdexec or for digest matching. */
if (!open_cmnd(sudoers_cmnd, digest, &fd))
if (!open_cmnd(sudoers_cmnd, digests, &fd))
goto bad;
/*
@@ -476,7 +476,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
}
if (!command_args_match(sudoers_cmnd, sudoers_args))
goto bad;
if (digest != NULL && !digest_matches(fd, sudoers_cmnd, digest)) {
if (!digest_matches(fd, sudoers_cmnd, digests)) {
/* XXX - log functions not available but we should log very loudly */
goto bad;
}
@@ -498,7 +498,7 @@ bad:
* otherwise, return true if user_cmnd names one of the inodes in path.
*/
bool
command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest)
command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests)
{
bool rc = false;
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
@@ -526,11 +526,11 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct
* use glob(3) and/or fnmatch(3) to do the matching.
*/
if (def_fast_glob)
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digest);
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests);
else
rc = command_matches_glob(sudoers_cmnd, sudoers_args, digest);
rc = command_matches_glob(sudoers_cmnd, sudoers_args, digests);
} else {
rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest);
rc = command_matches_normal(sudoers_cmnd, sudoers_args, digests);
}
done:
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 1996, 1998-2005, 2007-2019
* Copyright (c) 1996, 1998-2005, 2007-2020
* Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -41,21 +41,36 @@
#include <unistd.h>
#include "sudoers.h"
#include "sudo_digest.h"
#include <gram.h>
bool
digest_matches(int fd, const char *file, const struct command_digest *digest)
digest_matches(int fd, const char *file, const struct command_digest_list *digests)
{
unsigned int digest_type = SUDO_DIGEST_INVALID;
unsigned char *file_digest = NULL;
unsigned char *sudoers_digest = NULL;
struct command_digest *digest;
bool matched = false;
size_t digest_len;
debug_decl(digest_matches, SUDOERS_DEBUG_MATCH);
if (fd == -1)
goto done;
if (TAILQ_EMPTY(digests)) {
/* No digest, no problem. */
debug_return_bool(true);
}
file_digest = sudo_filedigest(fd, file, digest->digest_type, &digest_len);
if (fd == -1) {
/* No file, no match. */
goto done;
}
TAILQ_FOREACH(digest, digests, entries) {
/* Compute file digest if needed. */
if (digest->digest_type != digest_type) {
free(file_digest);
file_digest = sudo_filedigest(fd, file, digest->digest_type,
&digest_len);
if (lseek(fd, (off_t)0, SEEK_SET) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
"unable to rewind digest fd");
@@ -64,6 +79,8 @@ digest_matches(int fd, const char *file, const struct command_digest *digest)
/* Warning (if any) printed by sudo_filedigest() */
goto done;
}
digest_type = digest->digest_type;
}
/* Convert the command digest from ascii to binary. */
if ((sudoers_digest = malloc(digest_len)) == NULL) {
@@ -89,13 +106,16 @@ digest_matches(int fd, const char *file, const struct command_digest *digest)
goto bad_format;
}
}
if (memcmp(file_digest, sudoers_digest, digest_len) == 0) {
matched = true;
} else {
break;
}
sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
"%s digest mismatch for %s, expecting %s",
digest_type_to_name(digest->digest_type), file, digest->digest_str);
free(sudoers_digest);
sudoers_digest = NULL;
}
goto done;

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 1996, 1998-2000, 2004, 2007-2018
* Copyright (c) 1996, 1998-2000, 2004, 2007-2020
* Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -105,20 +105,11 @@
(cs1)->runasgrouplist != (cs2)->runasgrouplist)
struct command_digest {
TAILQ_ENTRY(command_digest) entries;
unsigned int digest_type;
char *digest_str;
};
/*
* A command with option args and digest.
* XXX - merge into struct member
*/
struct sudo_command {
char *cmnd;
char *args;
struct command_digest *digest;
};
/*
* Tags associated with a command.
* Possible values: true, false, IMPLIED, UNSPEC.
@@ -164,13 +155,14 @@ struct command_options {
*/
/*
* Tail queue list head structure.
* Tail queue list head structures.
*/
TAILQ_HEAD(defaults_list, defaults);
TAILQ_HEAD(userspec_list, userspec);
TAILQ_HEAD(member_list, member);
TAILQ_HEAD(privilege_list, privilege);
TAILQ_HEAD(cmndspec_list, cmndspec);
TAILQ_HEAD(command_digest_list, command_digest);
STAILQ_HEAD(comment_list, sudoers_comment);
/*
@@ -196,6 +188,16 @@ struct privilege {
struct defaults_list defaults; /* list of sudoOptions */
};
/*
* A command with option args and digest.
* XXX - merge into struct member
*/
struct sudo_command {
char *cmnd;
char *args;
struct command_digest_list digests;
};
/*
* Structure describing a linked list of Cmnd_Specs.
* XXX - include struct command_options instad of its contents inline
@@ -306,10 +308,10 @@ void reparent_parse_tree(struct sudoers_parse_tree *new_tree);
bool addr_matches(char *n);
/* match_command.c */
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest);
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests);
/* match_digest.c */
bool digest_matches(int fd, const char *file, const struct command_digest *digest);
bool digest_matches(int fd, const char *file, const struct command_digest_list *digests);
/* match.c */
struct group;

View File

@@ -1,4 +1,4 @@
Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh
Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill

View File

@@ -3,13 +3,15 @@
"LS": [
{
"command": "/bin/ls",
"sha224": "d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1"
"sha224": "d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1",
"sha224": "d7910e1967342b4605cb73a550944044c631cd3514001900966962ac"
}
],
"SH": [
{
"command": "/bin/sh",
"sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM="
"sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=",
"sha256": "1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4="
}
]
},

View File

@@ -4,8 +4,8 @@ objectClass: sudoRole
cn: millert
sudoUser: millert
sudoHost: ALL
sudoCommand: sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh
sudoCommand: sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
sudoCommand: sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill
sudoOrder: 1

View File

@@ -1,5 +1,7 @@
# sudoRole millert
millert ALL = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1\
/bin/ls, sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh,\
millert ALL = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1,\
sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls,\
sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=,\
sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh,\
sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw\
/bin/kill

View File

@@ -1,6 +1,6 @@
Parses OK.
Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh
Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill

View File

@@ -1,4 +1,4 @@
CMNDALIAS ALIAS = SHA224_TOK : DIGEST COMMAND
CMNDALIAS ALIAS = SHA256_TOK : DIGEST COMMAND
CMNDALIAS ALIAS = SHA224_TOK : DIGEST , SHA224_TOK : DIGEST COMMAND
CMNDALIAS ALIAS = SHA256_TOK : DIGEST , SHA256_TOK : DIGEST COMMAND
WORD(5) ALL = ALIAS , ALIAS , SHA512_TOK : DIGEST COMMAND

View File

@@ -27,6 +27,5 @@ bool sudo_ldap_is_negated(char **valp);
bool sudo_ldap_add_default(const char *var, const char *val, int op, char *source, struct defaults_list *defs);
int sudo_ldap_parse_option(char *optstr, char **varp, char **valp);
struct privilege *sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, void *runasgroups, void *cmnds, void *opts, const char *notbefore, const char *notafter, bool warnings, bool store_options, sudo_ldap_iter_t iter);
struct command_digest *sudo_ldap_extract_digest(char **cmnd, struct command_digest *digest);
#endif /* SUDOERS_LDAP_H */