Skip to content

Commit 9e069dc

Browse files
MggMugginssquell
authored andcommitted
pam: Handle timeout error in conversation
Regardless of what error code is returned from the conversation function, pam_authenticate always returns ConversationError. This propogates timeout error information back to the password retry logic so that sudo doesn't prompt the user again after a timeout.
1 parent aa5394f commit 9e069dc

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

src/pam/converse.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::io;
2+
13
use crate::cutils::string_from_ptr;
24

35
use super::sys::*;
@@ -126,10 +128,17 @@ impl Converser for CLIConverser {
126128
}
127129
tty.prompt(msg)?;
128130
if self.password_feedback {
129-
Ok(tty.read_password_with_feedback()?)
131+
tty.read_password_with_feedback()
130132
} else {
131-
Ok(tty.read_password()?)
133+
tty.read_password()
132134
}
135+
.map_err(|err| {
136+
if let io::ErrorKind::TimedOut = err.kind() {
137+
PamError::TimedOut
138+
} else {
139+
PamError::IoError(err)
140+
}
141+
})
133142
}
134143

135144
fn handle_error(&self, msg: &str) -> PamResult<()> {
@@ -149,6 +158,10 @@ pub(super) struct ConverserData<C> {
149158
pub(super) converser_name: String,
150159
pub(super) no_interact: bool,
151160
pub(super) auth_prompt: Option<String>,
161+
// pam_authenticate does not return error codes returned by the conversation
162+
// function; these are set by the conversation function instead of returning
163+
// multiple error codes.
164+
pub(super) timed_out: bool,
152165
pub(super) panicked: bool,
153166
}
154167

@@ -194,11 +207,16 @@ pub(super) unsafe extern "C" fn converse<C: Converser>(
194207
// send the conversation off to the Rust part
195208
// SAFETY: appdata_ptr contains the `*mut ConverserData` that is untouched by PAM
196209
let app_data = unsafe { &mut *(appdata_ptr as *mut ConverserData<C>) };
197-
let Ok(resp_buf) = handle_message(app_data, style, &msg) else {
198-
return PamErrorType::ConversationError;
199-
};
200-
201-
resp_bufs.push(resp_buf);
210+
match handle_message(app_data, style, &msg) {
211+
Ok(resp_buf) => {
212+
resp_bufs.push(resp_buf);
213+
}
214+
Err(PamError::TimedOut) => {
215+
app_data.timed_out = true;
216+
return PamErrorType::ConversationError;
217+
}
218+
Err(_) => return PamErrorType::ConversationError,
219+
}
202220
}
203221

204222
// Allocate enough memory for the responses, which are initialized with zero.
@@ -374,6 +392,7 @@ mod test {
374392
converser_name: "tux".to_string(),
375393
no_interact: false,
376394
auth_prompt: Some("authenticate".to_owned()),
395+
timed_out: false,
377396
panicked: false,
378397
});
379398
let cookie = PamConvBorrow::new(hello.as_mut());

src/pam/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ pub enum PamError {
175175
IoError(std::io::Error),
176176
EnvListFailure,
177177
InteractionRequired,
178+
TimedOut,
178179
InvalidUser(String, String),
179180
}
180181

@@ -222,6 +223,7 @@ impl fmt::Display for PamError {
222223
)
223224
}
224225
PamError::InteractionRequired => write!(f, "Interaction is required"),
226+
PamError::TimedOut => write!(f, "timed out"),
225227
PamError::InvalidUser(username, other_user) => {
226228
write!(
227229
f,

src/pam/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ impl PamContext {
7575
.ok()
7676
.filter(|s| !s.is_empty())
7777
.or(Some("authenticate".to_owned())),
78+
timed_out: false,
7879
panicked: false,
7980
}));
8081

@@ -157,6 +158,11 @@ impl PamContext {
157158
panic!("Panic during pam authentication");
158159
}
159160

161+
// SAFETY: self.data_ptr was created by Box::into_raw
162+
if unsafe { (*self.data_ptr).timed_out } {
163+
return Err(PamError::TimedOut);
164+
}
165+
160166
#[allow(clippy::question_mark)]
161167
if let Err(err) = auth_res {
162168
return Err(err);

0 commit comments

Comments
 (0)