Serialize TTY access with flock during password prompting#1649
Serialize TTY access with flock during password prompting#1649MoyashiWithDevice wants to merge 3 commits into
Conversation
When multiple sudo processes are piped together (e.g. `sudo a | sudo b`), both processes open /dev/tty and call poll()+read() concurrently. Because the terminal delivers each byte to only one reader, keystrokes from the user get split between the two processes, resulting in both receiving an incomplete password and authentication failure. Fix this by acquiring an exclusive flock(LOCK_EX) on the TTY fd before reading the password and releasing it when done. A process that reads from the same controlling terminal will block until the first finishes, ensuring only one process reads user input at a time. If flock(2) is unavailable or fails (e.g. on filesystems that do not support it), the process proceeds without the lock -- a graceful degradation rather than a hard failure.
| /// (e.g. the filesystem does not support `flock`). | ||
| fn new(fd: BorrowedFd<'_>) -> Option<Self> { | ||
| // SAFETY: `fd` is a valid open file descriptor. | ||
| if unsafe { libc::flock(fd.as_raw_fd(), libc::LOCK_EX) } == 0 { |
There was a problem hiding this comment.
nit: You can use crate::cutils::flock here, avoiding the introduction of unsafe {}:
sudo-rs/src/system/file/lock.rs
Line 50 in f48bb86
We prefer libc calls to be wrapped in the system/ cutils/ modules.
| impl Drop for FlockGuard { | ||
| fn drop(&mut self) { | ||
| // SAFETY: `self.0` is a valid file descriptor and `LOCK_UN` always succeeds. | ||
| unsafe { libc::flock(self.0, libc::LOCK_UN) }; |
|
Thanks for the PR! Per our contributing guidelines this is a substantial enough change to warrant an issue. (The description is already listed above, more or less, and can be simply copy-pasted there). I will create an issue for that based on the above description. (In particular, I have a question about this behaviour that doesn't necessarily need to turn into a change in this particular PR immediately.:) Edit: I have opened #1650 |
When sudo processes are piped together (e.g. sudo a | sudo b), both processes prompt for a password on the same TTY simultaneously, corrupting input. Acquire an exclusive flock on the TTY before PAM authentication so only one process prompts at a time. After acquiring the lock, re-check the timestamp file via the existing touch() mechanism. If another process already authenticated while we were waiting, skip PAM entirely — the existing 'bypass if already authenticated' logic in determine_auth_status() handles this naturally. Removes the earlier AuthAlreadyDone / recheck closure approach from the PAM conversation layer as it is no longer needed.
|
@squell Thank you for the review and for opening #1650. Based on your suggestion, I have revised the implementation. The TTY lock has been moved higher up the call chain into |
|
I can see from an earlier GitHub comment and the general nature of these changes that these changes are being performed using an LLM. That's something we do not accept per our contributing guidelines. Issue #1650 will be kept open and I am working on a PR of my own. |
|
Since some messages have been deleted I will re-post one here:
|
Commit message
PR description