-
Notifications
You must be signed in to change notification settings - Fork 259
(Bugfix) lib/btrfs.c: Remove hardcoded Paths and increase portability #1517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Is this safe? We should be pulling the binary via PATH rather than using env, and there might be a good reason for the path lockdown on the binaries... |
You need root priviledges in the first place to even get to this Code Path tho? |
The env source code just does So I would argue it might actually be safer then to iterate through a list of hardcoded Paths. Imagine an Attacker places a fake binary into one of the Path places before the actual real btrfs binary was iterated through. env basically does the same logic but more predictable and in some scenarios even safer. |
|
I've done a light review of the C code (mostly, style), but I prefer if @hallyn reviews the core idea. |
|
My main concern: /usr/bin/env will use PATH. glibc will sanitize PATH for setuid-root binaries, but musl will not. Google/gemini tells me the right way to handle this in nix would be using a nix derivation to set the path, and rebuild every time btrfs is upgraded. I don't think this patch is safe, especially in the nix world. |
The idea that /usr/bin/env is inherently “unsafe” is mostly a misunderstanding of what it actually does and where the real trust boundary lies. env does not introduce any new behavior — it simply calls execvp(), which performs a PATH lookup and then execve(). Using /usr/bin/env foo is therefore functionally equivalent to invoking foo via execvp() in C. If PATH is compromised, that problem already exists regardless of whether env is involved. On NixOS in particular, /usr/bin/env exists precisely because the system is not FHS-compliant. It provides a minimal compatibility layer so upstream software does not need to be rewritten to hardcode store paths. Replacing this with hardcoded paths in a derivation means patching and rebuilding every time a dependency changes, which quickly becomes a nightmare. There is also an important practical issue here: shadow on NixOS is not actively updated, and currently lags behind upstream. If we rely on substituting paths via the nix macros, we risk pulling in older, hardcoded versions of tools like btrfs-progs. Using outdated filesystem tooling is not just inconvenient — it’s actively dangerous. Filesystem utilities are tightly coupled to kernel and on-disk format behavior, and running old userspace against newer kernels is a well-known source of corruption and data loss. It’s also important to distinguish between setuid binaries and binaries that are simply executed as root. useradd, which is the only binary consuming this function, is not setuid. In that case there is no privilege transition happening at exec time, so PATH lookup does not introduce a new security boundary and only roots enviroment matters. If an attacker can influence root’s PATH, then they can just as easily place a fake sudo, useradd, or btrfs binary earlier in PATH. At that point the system is already compromised. On NixOS, PATH is explicitly constructed and /usr/bin is mostly empty, with /usr/bin/env existing specifically to bridge non-FHS software. Using env here does not weaken security; it improves portability and avoids fragile downstream patching. In short: there is no meaningful security gain in hardcoding paths here, but there is a very real maintenance and safety cost. If roots PATH is attacker-controlled, the system is already lost — removing /usr/bin/env does not change that. |
There is also Also, keep in mind that these tools can be setuid through |
Its off by default and there is probably not even a single distro turning on setuid on porpuse if not needed tho? |
I hope so, but as long as shadow offers this configure option (and I would like to see it gone), these programs can be setuid and should be treated as setuid binaries. |
The fact that an suid binary can call btrfs commands in the first place is concerning. The option should defenitly be removed. |
Feel free to send a patch removing it. :) |
I could do it. |
…ase portability Signed-off-by: Hadi Chokr <hadichokr@icloud.com>
|
Thanks! The style is good; now it's only a matter of whether this is safe or not. A patch for removing |
Opened now. |
This comment was marked as off-topic.
This comment was marked as off-topic.
@silverhadch, do these absolute calls in |
This comment was marked as resolved.
This comment was marked as resolved.
They are replaced with the Nix Macros. |
This comment was marked as resolved.
This comment was marked as resolved.
|
Wouldn't it be a sane and simple solution to strip this PATH handling in Then, this very issue could be resolved by NixOS by placing a custom shell script into the installation directory of shadow and configure shadow with Would that be an acceptable approach? |
Yesnt. If every Distro where to define their options then yes but currently its a mix of basically supporting FHS only Distros. If the appraoch were to change to like sssd and nscd then Its a simple nix macro and for other distros in their package build a variable. But currently it tries to be portable across everything. So this PR when useradd and userdel no longer are capable of being setuid is and we want to go the work everywhere route or go the nscd route and tell distros about it. Or maybe both? Have an set Path Macro and if empty try the looping through the hard coded path and as a last resort use env. |
Where is that done? In a nix-specific patch? I do think some autoconfigure magic which sets an array of possible Dropping setuid does remove my main objection to using env. |
Yes — the Nix package does a string replacement on the path and injects a pkg macro that resolves to the current Nix store path of the tools, adding them as a dependency. The problem is that here we don’t have a single path, but a list of paths. In theory, we could just add /run/current-system/sw/bin/btrfs to the list and it would work on Nix, but that’s incredibly unclean. Instead, I wanted to go for the cleanest solution that works across all distros, including NixOS, by resolving the binary via the environment (i.e. using $PATH). If you resolve via $PATH in a normal binary, only the user environment matters — and since root access is required anyway, that’s fine. If the root environment is compromised, things are already broken, so there’s no additional security downside compared to hardcoding paths. However, if the binary is setuid, then the user’s environment gets elevated instead of root’s, which is a very bad idea. So the options are basically:
That last option is the cleanest overall. |
|
@alejandro-colomar @hallyn |
This is a bit scary to me, so I prefer if @hallyn reviews this. I don't see anything obviously wrong, FWIW. |
This Bugfixes NixOS/nixpkgs#483407 and increases portability in general and removes an unclean hack.