From 55e41a4aedbb63453e45871ef1d5ca32189587b0 Mon Sep 17 00:00:00 2001 From: iefa m Date: Wed, 28 Oct 2026 21:31:21 +0530 Subject: [PATCH] default_binding_matches: match Defaults>runas by runas user only default_binding_matches() checked a Defaults>runas_list entry with runaslist_matches(), which also looks at the group given with -g. When the requested group is not one of the runas user's groups it returns UNSPEC, so "sudo -u root -g users cmd" skipped every Defaults>root setting (rootpw, timestamp_timeout, log_output, ...) even though the command still runs as root. A Defaults> entry is documented to apply to commands run as a specific user, so only match the runas user. Adds a testsudoers regression test. --- MANIFEST | 2 ++ plugins/sudoers/defaults.c | 3 +- plugins/sudoers/match.c | 2 +- plugins/sudoers/parse.h | 1 + .../sudoers/regress/testsudoers/test35.out.ok | 29 +++++++++++++++++ plugins/sudoers/regress/testsudoers/test35.sh | 31 +++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 plugins/sudoers/regress/testsudoers/test35.out.ok create mode 100755 plugins/sudoers/regress/testsudoers/test35.sh diff --git a/MANIFEST b/MANIFEST index 8ef4c345ea..422b79af0b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1137,6 +1137,8 @@ plugins/sudoers/regress/testsudoers/test33.out.ok plugins/sudoers/regress/testsudoers/test33.sh plugins/sudoers/regress/testsudoers/test34.out.ok plugins/sudoers/regress/testsudoers/test34.sh +plugins/sudoers/regress/testsudoers/test35.out.ok +plugins/sudoers/regress/testsudoers/test35.sh plugins/sudoers/regress/testsudoers/test4.out.ok plugins/sudoers/regress/testsudoers/test4.sh plugins/sudoers/regress/testsudoers/test5.out.ok diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 32226c8415..5f6472a1ff 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -740,7 +740,8 @@ default_binding_matches(const struct sudoers_context *ctx, debug_return_bool(true); break; case DEFAULTS_RUNAS: - if (runaslist_matches(parse_tree, &d->binding->members, NULL) == ALLOW) + /* Only the runas user is matched, not the runas group. */ + if (runas_userlist_matches(parse_tree, &d->binding->members) == ALLOW) debug_return_bool(true); break; case DEFAULTS_HOST: diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index 0f69dafe3d..3c52edd3bf 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -147,7 +147,7 @@ runas_getgroups(const struct sudoers_context *ctx) * list containing runas_default is used. * Returns ALLOW, DENY or UNSPEC. */ -static int +int runas_userlist_matches(const struct sudoers_parse_tree *parse_tree, const struct member_list *user_list) { diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index ef721e741c..f8b95c8c53 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -434,6 +434,7 @@ int cmndlist_matches(const struct sudoers_parse_tree *parse_tree, const struct m int host_matches(const struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m); int hostlist_matches(const struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); int runaslist_matches(const struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list); +int runas_userlist_matches(const struct sudoers_parse_tree *parse_tree, const struct member_list *user_list); int user_matches(const struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member *m); int userlist_matches(const struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); const char *sudo_getdomainname(void); diff --git a/plugins/sudoers/regress/testsudoers/test35.out.ok b/plugins/sudoers/regress/testsudoers/test35.out.ok new file mode 100644 index 0000000000..ea8188576e --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test35.out.ok @@ -0,0 +1,29 @@ +Testing with runas user root: + +Parses OK + +Entries for user admin: + +ALL = (ALL : ALL) ALL + host allowed + runas allowed + cmnd allowed + +Password required + +Command allowed + +Testing with runas user root and runas group users: + +Parses OK + +Entries for user admin: + +ALL = (ALL : ALL) ALL + host allowed + runas allowed + cmnd allowed + +Password required + +Command allowed diff --git a/plugins/sudoers/regress/testsudoers/test35.sh b/plugins/sudoers/regress/testsudoers/test35.sh new file mode 100755 index 0000000000..b7d76dbf64 --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test35.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Verify that a Defaults>runas entry is applied based on the runas +# user even when a runas group the runas user is not a member of +# is specified with -g. +# + +: ${TESTSUDOERS=testsudoers} + +exec 2>&1 + +echo "Testing with runas user root:" +echo "" +$TESTSUDOERS -u root -p ${TESTDIR}/passwd -P ${TESTDIR}/group \ + admin /bin/ls <<'EOF' +Defaults:admin !authenticate +Defaults>root authenticate +admin ALL = (ALL:ALL) ALL +EOF + +echo "" +echo "Testing with runas user root and runas group users:" +echo "" +$TESTSUDOERS -u root -g users -p ${TESTDIR}/passwd -P ${TESTDIR}/group \ + admin /bin/ls <<'EOF' +Defaults:admin !authenticate +Defaults>root authenticate +admin ALL = (ALL:ALL) ALL +EOF + +exit 0