Skip to content

running_as_user: compare the runas group by gid, not by name - #592

Merged
millert merged 1 commit into
sudo-project:mainfrom
iefa-m:running-as-user-runas-gid
Nov 6, 2026
Merged

millert merged 1 commit into
sudo-project:mainfrom
iefa-m:running-as-user-runas-gid

Conversation

@iefa-m

@iefa-m iefa-m commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

running_as_user() lets check_user() skip authentication when the command runs as the invoking user, including sudo -g group when the user is already in that group. Membership was checked with user_in_group(ctx->user.pw, ctx->runas.gr->gr_name), which compares the resolved group's name with the names of the user's groups, using strcasecmp() by default because case_insensitive_group is on (that option is documented for group names written in sudoers). A different group whose name merely matches one of the user's groups was therefore treated as one the user already has. With staff (gid 50, user is a member) and Staff (gid 5000, not a member), or a local admins (gid 1001) next to a directory admins (gid 5001), sudo -g Staff cmd or sudo -g '#5001' cmd runs with gid 5000 or 5001 (policy.c passes runas_gid= from gr_gid) without asking for a password, for any user whose rule allows the group, such as the usual (ALL:ALL).

Compare the runas group-ID with the user's primary gid and group-ID list instead, the same list user_in_group() already uses for #gid names. Running a command with one of your own groups still needs no password. I checked this by calling the real running_as_user() with the testsudoers passwd/group backend: for a user in staff and the first admins, Staff and the second admins returned true before the change and false after it, while staff, admins (1001) and the primary group stay true. make check results are unchanged from main.

running_as_user() let "sudo -g group" skip authentication if the
user was in the runas group, but it checked that with user_in_group()
on the group's name.  That matches against the names of the user's
groups, case-insensitively by default, so a different group whose
name only matches one of the user's groups (Staff vs. staff, or a
second "admins" with another gid from a different NSS source) was
treated as one the user already has.  The command then ran with
that group's gid without a password.  Check the runas group-ID
against the user's primary and supplementary group-IDs instead.

@millert millert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not overly concerned about group name collisions like this but in this case, comparing by group ID is actually faster than comparing by name. Most group comparisons in sudoers are done via name to avoid having to resolve all the groups in sudoers, which can be slow on systems using LDAP. That is not a concern here as the user's group IDs have already been resolved (and cached). An alternative would be to use ctx->user.gid_list instead but your approach is probably easier to understand.

@millert
millert merged commit 28079db into sudo-project:main Nov 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants