sg: run a command with a different primary group - #291
Open
pierre-warnier wants to merge 1 commit into
Open
Conversation
sg is the sixteenth tool, and the first of the nine that both Debian and
Fedora ship and we did not. It runs a single command with a different
primary group: newgrp for one command instead of for a whole shell.
sg and newgrp are not merely similar. They decide who may enter a group by
the same rules and enter it the same way, and the distributions ship sg as a
symlink to the setuid newgrp binary -- one implementation, dispatched on
argv[0]. So the shared half moves into shadow_core::group_switch and both
tools call it, which is what keeps them from drifting apart. The multicall
binary already dispatches on argv[0], so it needs only the new applet.
sg execs the command rather than forking and waiting for it. The shell then
is the process, so the command's exit status reaches the caller unaltered:
`sg staff -c 'exit 7'` exits 7 and a command that cannot be run exits 127,
without sg reconstructing a wait status. This matches what the GNU tool does
and costs a process.
Writing the shared module turned up two things wrong in newgrp:
- It refused a group with no password without prompting, and prompted when
the group had one. Whether a prompt appeared therefore told any caller
which groups have passwords set, which is a list of the ones worth
attacking. Both tools now prompt either way and refuse afterwards.
- It rebuilt the supplementary group list with initgroups() alone, which
builds it from the member lists in /etc/group -- where a primary group
never appears. `newgrp staff` therefore dropped the group the caller
started in, costing them access to their own files until they left the
shell. The original group is added back, which needs setgroups(2) and so
has to happen before the setuid privilege is given up.
Both were established by running the GNU tools, not by reading them.
sg is setuid for the reason newgrp is: a caller who is not a member has to
be checked against the group password in /etc/gshadow, which is root-only.
Removing sg from the setuid list fails six assertions in the e2e suite --
including for a plain member, since initgroups() itself needs the privilege
-- and nothing else in the suite covers it.
Verified: 763 tests on debian, alpine and fedora; 255 e2e assertions against
a real setuid install, covering a member, a non-member with the group
password, a wrong password, a passwordless group and exit-status
propagation; 43 GNU comparisons; 19 arm64 assertions under emulation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
sg, the sixteenth tool: it runs a single command with a different primary group.sgis the first of the nine tools that both Debian and Fedora ship and we did not. It was picked first because it is the one that proves the scaffolding: it shares nearly all its behaviour with a tool we already have.sg and newgrp are one implementation
They decide who may enter a group by the same rules and enter it the same way, and the distributions ship
sgas a symlink to the setuidnewgrpbinary — one implementation dispatched onargv[0]:So the shared half moves into
shadow_core::group_switchand both tools call it. That is what keeps them from drifting apart. Our multicall binary already dispatches onargv[0], so it needs only the new applet.sgexecs the command instead of forking and waiting for it. The shell then is the process, so the command's exit status reaches the caller unaltered —sg staff -c 'exit 7'exits 7, and a command that cannot be run exits 127 — withoutsghaving to reconstruct a wait status. Same observable behaviour as the GNU tool, one fewer process.Two bugs in newgrp that writing the shared module exposed
It leaked which groups have passwords.
newgrprefused a group with no password without prompting, and prompted when the group had one. Whether a prompt appeared therefore told any caller which groups have a password set — a list of the ones worth attacking. Both tools now prompt either way and refuse afterwards. The GNU tools do the same:It dropped the caller's own primary group. The supplementary list was rebuilt with
initgroups()alone, which builds it from the member lists in/etc/group— where a primary group never appears. Sonewgrp staffcost the caller access to their own files until they left the shell. Confirmed against the GNU tool, which keeps it:The original group is added back, which needs
setgroups(2)and so has to happen before the setuid privilege is given up.Both were established by running the GNU tools, not by reading them.
Privilege
sgis setuid for the reasonnewgrpis: a caller who is not a member has to be checked against the group password in/etc/gshadow, which is root-only. Removingsgfrom the setuid list fails six assertions in the e2e suite — including for a plain member, sinceinitgroups()itself needs the privilege — and nothing else in the suite covers it.Verification
cargo test --workspacemake checkpam— exit 0make test-gnu-compatmake test-arm64The e2e section covers a member, a non-member with the correct group password, a wrong password, a group with no password, an unknown group,
-cbeing optional, exit-status propagation, and that the caller's uid and original primary group both survive.