runaslist_matches: split out user_list and group_list matching.

This makes it possible to call the appropriate runas user or group
list match function when resolving aliases instead of calling
runaslist_matches() itself.  Fixes a bug that prevented the group
specified via "sudo -g" from matching when a Runas_Alias was used
in the user or group portion of a Runas_Spec.
This commit is contained in:
Todd C. Miller
2023-06-13 20:13:24 -06:00
parent 4710283516
commit 78e65e14ea

View File

@@ -58,8 +58,6 @@
#include "sudoers.h" #include "sudoers.h"
#include <gram.h> #include <gram.h>
static struct member_list empty = TAILQ_HEAD_INITIALIZER(empty);
/* /*
* Check whether user described by pw matches member. * Check whether user described by pw matches member.
* Returns ALLOW, DENY or UNSPEC. * Returns ALLOW, DENY or UNSPEC.
@@ -142,37 +140,22 @@ runas_getgroups(void)
} }
/* /*
* Check for user described by pw in a list of members. * Check whether the requested runas user matches user_list, the
* If both lists are empty compare against def_runas_default. * user portion of a sudoers runaslist. If user_list is NULL, a
* list containing runas_default is used.
* Returns ALLOW, DENY or UNSPEC. * Returns ALLOW, DENY or UNSPEC.
*/ */
int static int
runaslist_matches(const struct sudoers_parse_tree *parse_tree, runas_userlist_matches(const struct sudoers_parse_tree *parse_tree,
const struct member_list *user_list, const struct member_list *group_list, const struct member_list *user_list, struct member **matching_user)
struct member **matching_user, struct member **matching_group)
{ {
const char *lhost = parse_tree->lhost ? parse_tree->lhost : user_runhost; const char *lhost = parse_tree->lhost ? parse_tree->lhost : user_runhost;
const char *shost = parse_tree->shost ? parse_tree->shost : user_srunhost; const char *shost = parse_tree->shost ? parse_tree->shost : user_srunhost;
struct member_list _user_list = TAILQ_HEAD_INITIALIZER(_user_list);
struct member m_user;
int user_matched = UNSPEC; int user_matched = UNSPEC;
int group_matched = UNSPEC;
struct member *m; struct member *m;
struct alias *a; struct alias *a;
int rc; debug_decl(runas_userlist_matches, SUDOERS_DEBUG_MATCH);
debug_decl(runaslist_matches, SUDOERS_DEBUG_MATCH);
/* If no runas user listed in sudoers, use a default value. */
if (user_list == NULL) {
m_user.name = def_runas_default;
m_user.type = WORD;
m_user.negated = false;
TAILQ_INSERT_HEAD(&_user_list, &m_user, entries);
user_list = &_user_list;
matching_user = NULL;
}
/* Check runas user. */
TAILQ_FOREACH_REVERSE(m, user_list, member_list, entries) { TAILQ_FOREACH_REVERSE(m, user_list, member_list, entries) {
switch (m->type) { switch (m->type) {
case ALL: case ALL:
@@ -192,8 +175,8 @@ runaslist_matches(const struct sudoers_parse_tree *parse_tree,
case ALIAS: case ALIAS:
a = alias_get(parse_tree, m->name, RUNASALIAS); a = alias_get(parse_tree, m->name, RUNASALIAS);
if (a != NULL) { if (a != NULL) {
rc = runaslist_matches(parse_tree, &a->members, const int rc = runas_userlist_matches(parse_tree,
&empty, matching_user, NULL); &a->members, matching_user);
if (rc != UNSPEC) if (rc != UNSPEC)
user_matched = m->negated ? !rc : rc; user_matched = m->negated ? !rc : rc;
alias_put(a); alias_put(a);
@@ -216,58 +199,107 @@ runaslist_matches(const struct sudoers_parse_tree *parse_tree,
break; break;
} }
} }
debug_return_int(user_matched);
}
/* Check runas group, if one was specified. */ /*
if (ISSET(sudo_user.flags, RUNAS_GROUP_SPECIFIED)) { * Check whether the requested runas group matches group_list, the
if (group_list != NULL) { * group portion of a sudoers runaslist, or the runas user's groups.
TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) { * Returns ALLOW, DENY or UNSPEC.
switch (m->type) { */
case ALL: static int
runas_grouplist_matches(const struct sudoers_parse_tree *parse_tree,
const struct member_list *group_list, struct member **matching_group)
{
int group_matched = UNSPEC;
struct member *m;
struct alias *a;
debug_decl(runas_grouplist_matches, SUDOERS_DEBUG_MATCH);
if (group_list != NULL) {
TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) {
switch (m->type) {
case ALL:
group_matched = !m->negated;
break;
case ALIAS:
a = alias_get(parse_tree, m->name, RUNASALIAS);
if (a != NULL) {
const int rc = runas_grouplist_matches(parse_tree,
&a->members, matching_group);
if (rc != UNSPEC)
group_matched = m->negated ? !rc : rc;
alias_put(a);
break;
}
FALLTHROUGH;
case WORD:
if (group_matches(m->name, runas_gr))
group_matched = !m->negated; group_matched = !m->negated;
break; break;
case ALIAS: }
a = alias_get(parse_tree, m->name, RUNASALIAS); if (group_matched != UNSPEC) {
if (a != NULL) { if (matching_group != NULL && m->type != ALIAS)
rc = runaslist_matches(parse_tree, &empty, *matching_group = m;
&a->members, NULL, matching_group); break;
if (rc != UNSPEC) }
group_matched = m->negated ? !rc : rc; }
alias_put(a); }
break; if (group_matched == UNSPEC) {
} struct gid_list *runas_groups;
FALLTHROUGH; /*
case WORD: * The runas group was not explicitly allowed by sudoers.
if (group_matches(m->name, runas_gr)) * Check whether it is one of the target user's groups.
group_matched = !m->negated; */
break; if (runas_pw->pw_gid == runas_gr->gr_gid) {
} group_matched = ALLOW; /* runas group matches passwd db */
if (group_matched != UNSPEC) { } else if ((runas_groups = runas_getgroups()) != NULL) {
if (matching_group != NULL && m->type != ALIAS) int i;
*matching_group = m;
for (i = 0; i < runas_groups->ngids; i++) {
if (runas_groups->gids[i] == runas_gr->gr_gid) {
group_matched = ALLOW; /* matched aux group vector */
break; break;
} }
} }
sudo_gidlist_delref(runas_groups);
} }
if (group_matched == UNSPEC) { }
struct gid_list *runas_groups;
/*
* The runas group was not explicitly allowed by sudoers.
* Check whether it is one of the target user's groups.
*/
if (runas_pw->pw_gid == runas_gr->gr_gid) {
group_matched = ALLOW; /* runas group matches passwd db */
} else if ((runas_groups = runas_getgroups()) != NULL) {
int i;
for (i = 0; i < runas_groups->ngids; i++) { debug_return_int(group_matched);
if (runas_groups->gids[i] == runas_gr->gr_gid) { }
group_matched = ALLOW; /* matched aux group vector */
break; /*
} * Check whether the sudoers runaslist, composed of user_list and
} * group_list, matches the runas user/group requested by the user.
sudo_gidlist_delref(runas_groups); * Either (or both) user_list and group_list may be NULL.
} * If user_list is NULL, a list containing runas_default is used.
} * Returns ALLOW, DENY or UNSPEC.
*/
int
runaslist_matches(const struct sudoers_parse_tree *parse_tree,
const struct member_list *user_list, const struct member_list *group_list,
struct member **matching_user, struct member **matching_group)
{
struct member_list _user_list = TAILQ_HEAD_INITIALIZER(_user_list);
int user_matched, group_matched = UNSPEC;
struct member m_user;
debug_decl(runaslist_matches, SUDOERS_DEBUG_MATCH);
/* If no runas user listed in sudoers, use the default value. */
if (user_list == NULL) {
m_user.name = def_runas_default;
m_user.type = WORD;
m_user.negated = false;
TAILQ_INSERT_HEAD(&_user_list, &m_user, entries);
user_list = &_user_list;
matching_user = NULL;
}
user_matched = runas_userlist_matches(parse_tree, user_list, matching_user);
if (ISSET(sudo_user.flags, RUNAS_GROUP_SPECIFIED)) {
group_matched = runas_grouplist_matches(parse_tree, group_list,
matching_group);
} }
if (user_matched == DENY || group_matched == DENY) if (user_matched == DENY || group_matched == DENY)