-
Notifications
You must be signed in to change notification settings - Fork 134
Description
Having a sudoers config with passwd_tries=0 lead to an integer underflow in sudo-rs, leading to unlimited password prompt:
Defaults passwd_tries=0
To Reproduce
- Install / compile sudo-rs.
- Run
sudo-rs suor any othersudo-rscommand and see that you can not only try to login one time but also an unlimited number of times as sudo-rs keeps prompting for new password.
Expected behavior
You cannot enter any password. In presence of Defaults passwd_tries=0, sudo just exits without even asking for a password, therefore this is not present in sudo.
Environment (please complete the following information):
- Linux distribution: Ubuntu 25.10, Fedora 42
- bug present in v0.2.9 commit 2b4d403, since v0.2.0
Additional context
The fix is simple, you just have to clamp passwd_tries at a minimum of one with allowed_attempts = max(1, passwd_tries) before handing it to attempt_authenticate, or bail out immediately when the configured value is zero or negative.
The affected code is:
src/defaults/mod.rs line 55:
passwd_tries = 3 [0..=1000]
This default allows passwd_tries to be set to any value from 0 through 1000, so sudoers can configure Defaults passwd_tries=0.
src/sudoers/policy.rs line 40:
allowed_attempts: self.passwd_tries().try_into().unwrap(),
The setting is converted directly into the Authentication struct without clamping; a configured zero propagates as allowed_attempts = 0.
src/sudo/pipeline.rs lines 208 to 213:
attempt_authenticate(
&mut pam_context,
&auth_user.name,
context.non_interactive,
allowed_attempts,
)?;
The zero-valued allowed_attempts is handed to PAM authentication as the max_tries parameter.
src/sudo/pam.rs lines 138 to 166:
pub(super) fn attempt_authenticate(
pam: &mut PamContext,
auth_user: &str,
non_interactive: bool,
mut max_tries: u16,
) -> Result<(), Error> {
let mut current_try = 0;
loop {
current_try += 1;
match pam.authenticate(auth_user) {
Ok(_) => break,
Err(PamError::Pam(PamErrorType::MaxTries)) => {
return Err(Error::MaxAuthAttempts(current_try));
}
Err(PamError::Pam(PamErrorType::AuthError | PamErrorType::ConversationError)) => {
max_tries -= 1;
if max_tries == 0 {
return Err(Error::MaxAuthAttempts(current_try));
} else if non_interactive {
return Err(Error::InteractionRequired);
} else {
user_warn!("Authentication failed, try again.");
}
}
Err(e) => {
return Err(e.into());
}
}
}
Ok(())
}
max_tries is an unsigned counter that is decremented before checking for zero. When max_tries starts at 0, the first max_tries -= 1 underflows to 65535 in release builds. Technically PAM should authorize many more attempts than is normally possible, I could have infinite attemps on ubuntu 25.10. However I think this is limited somewhere in system logic in Arch linux to 3 attempts, as I cannot make more than 3 which is the default when passwd_tries is not specified in /etc/sudoers on arch.
See my suggested patch: #1313