Hacker News new | ask | show | jobs
by josephg 959 days ago
This would be a great use case for a capability security model. Essentially what you really want is the sudo command to acquire a temporary capability token to edit that specific file. Then run your editor and pass it the capability. (And revoke the capability when the editor process closes).

It’s a pity this isn’t more straight forward to implement on Linux.

2 comments

Are there no overwrite/seek bugs in Unix that could be exploited in that case? It seems to me like only using sudo for a cp command would reduce the attack surface.
I’d certainly hope not. A capability based security system in an OS is only secure if it doesn’t have bugs. Just like most security-critical software.

However, I’m not sure that Linux is capable of masquerading as a general purpose capability based operating system. I think it’s missing a bunch of APIs.

> Essentially what you really want is the sudo command to acquire a temporary capability token to edit that specific file.

This should be doable with an XDG portal model, right?

It's doable by opening the file in a privileged process (sudo) and passing the file descriptor to a non-privileged process.

Maybe one could make a sudoedit that opens a file in sudo process and then spawns a non-privileged editor process which inherits the file descriptor and is given the /dev/fd/ path on the command line, so it stays none the wiser about the whole process.

Sounds like a bit of recipe for accidentally handing access to an unintended privileged fd through inheritance (ignoring the /dev/fd one) such that a compromised unprivileged SUDO_EDITOR value gives you sudo access. Maybe not likely, but I’d really be hesitant about any feature that relies on implicit fd inheritance…
Another option could be to open a UNIX socket in the privileged sudo process, spawn an unprivileged child process 'shim' that connects to that socket, and then the sudo process can pass the file descriptor over the socket. Since the child shim is 'clean', it should have nothing more than stdin/out/err open, plus now this passed FD. Then the shim can spawn the target program and allow it to inherit just the passed FD.

I think the larger issue is that I doubt many (if any?) editors allow opening a file via an inherited file descriptor! I guess some will read stdin (the shim could close stdin and then dup2() it into its place), but then there's no way to save the file back when finished.

Close all other fds between fork and exec then (you can look at the code of base::LaunchProcess in Chromium for an example). It’s a minuscule amount of code to audit compared to XDG portals. And it’s backwards-compatible with decades of unix programs.

For a more complicated solution: spawn a zygote process early with a unix socket which you’ll use to send the fd later. Zygote at start drops provileges. When it receives the fd, it closes the socket and execs the editor.

I’m not saying it’s not possible to do correctly. But do you not agree that the first is hard to correctly (can overlook an fd) while the latter is a lot of complexity?

There is the CLOEXEC flag which is the intended way to manage this but it’s not the default and you have to be diligent about setting it which again carries its own set of challenges.

What you’d really want is CLOEXEC implicitly on all fds and having to explicitly opt in for fd inheritance.

Rust stdlib opens all fds with CLOEXEC set. You have to explicitly disable it when you want the child to inhert the fd.
That would be ideal, but we'd need a new syscall for that.