Skip to content

Commit b175fc8

Browse files
MggMugginssquell
authored andcommitted
sudo: Implement passwd_timeout
1 parent 9e069dc commit b175fc8

File tree

8 files changed

+42
-10
lines changed

8 files changed

+42
-10
lines changed

src/defaults/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ defaults! {
5252

5353
verifypw = all (!= never) [all, always, any, never] #ignored
5454

55+
passwd_timeout = (5*60) (!= 0) {fractional_minutes}
5556
timestamp_timeout = (15*60) (!= 0) {fractional_minutes}
5657

5758
env_keep = ["COLORS", "DISPLAY", "HOSTNAME", "KRB5CCNAME", "LS_COLORS", "PATH",

src/pam/converse.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::io;
22

33
use crate::cutils::string_from_ptr;
4+
use crate::system::time::Duration;
45

56
use super::sys::*;
67

@@ -100,6 +101,7 @@ pub struct CLIConverser {
100101
pub(super) use_stdin: bool,
101102
pub(super) bell: bool,
102103
pub(super) password_feedback: bool,
104+
pub(super) password_timeout: Option<Duration>,
103105
}
104106

105107
use rpassword::Terminal;
@@ -128,9 +130,9 @@ impl Converser for CLIConverser {
128130
}
129131
tty.prompt(msg)?;
130132
if self.password_feedback {
131-
tty.read_password_with_feedback()
133+
tty.read_password_with_feedback(self.password_timeout)
132134
} else {
133-
tty.read_password()
135+
tty.read_password(self.password_timeout)
134136
}
135137
.map_err(|err| {
136138
if let io::ErrorKind::TimedOut = err.kind() {

src/pam/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::{
66
ptr::NonNull,
77
};
88

9+
use crate::system::time::Duration;
10+
911
use converse::ConverserData;
1012
use error::pam_err;
1113
pub use error::{PamError, PamErrorType, PamResult};
@@ -43,20 +45,23 @@ impl PamContext {
4345
/// username.
4446
///
4547
/// This function will error when initialization of the PAM session somehow failed.
48+
#[allow(clippy::too_many_arguments)]
4649
pub fn new_cli(
4750
converser_name: &str,
4851
service_name: &str,
4952
use_stdin: bool,
5053
bell: bool,
5154
no_interact: bool,
5255
password_feedback: bool,
56+
password_timeout: Option<Duration>,
5357
target_user: Option<&str>,
5458
) -> PamResult<PamContext> {
5559
let converser = CLIConverser {
5660
bell,
5761
name: converser_name.to_owned(),
5862
use_stdin,
5963
password_feedback,
64+
password_timeout,
6065
};
6166

6267
let c_service_name = CString::new(service_name)?;

src/pam/rpassword.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,24 +262,27 @@ impl Terminal<'_> {
262262
}
263263

264264
/// Reads input with TTY echo disabled
265-
pub fn read_password(&mut self) -> io::Result<PamBuffer> {
266-
let mut input = self.source_timeout(None);
265+
pub fn read_password(&mut self, timeout: Option<Duration>) -> io::Result<PamBuffer> {
266+
let mut input = self.source_timeout(timeout);
267267
let _hide_input = HiddenInput::new(false)?;
268268
read_unbuffered(&mut input)
269269
}
270270

271271
/// Reads input with TTY echo disabled, but do provide visual feedback while typing.
272-
pub fn read_password_with_feedback(&mut self) -> io::Result<PamBuffer> {
272+
pub fn read_password_with_feedback(
273+
&mut self,
274+
timeout: Option<Duration>,
275+
) -> io::Result<PamBuffer> {
273276
match (HiddenInput::new(true)?, self) {
274277
(Some(hide_input), Terminal::StdIE(stdin, stdout)) => {
275-
let mut reader = TimeoutRead::new(stdin.as_fd(), None);
278+
let mut reader = TimeoutRead::new(stdin.as_fd(), timeout);
276279
read_unbuffered_with_feedback(&mut reader, stdout, &hide_input)
277280
}
278281
(Some(hide_input), Terminal::Tty(file)) => {
279-
let mut reader = TimeoutRead::new(file.as_fd(), None);
282+
let mut reader = TimeoutRead::new(file.as_fd(), timeout);
280283
read_unbuffered_with_feedback(&mut reader, &mut &*file, &hide_input)
281284
}
282-
(None, term) => read_unbuffered(&mut term.source_timeout(None)),
285+
(None, term) => read_unbuffered(&mut term.source_timeout(timeout)),
283286
}
284287
}
285288

src/su/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,16 @@ fn authenticate(requesting_user: &str, user: &str, login: bool) -> Result<PamCon
2929
"su"
3030
};
3131
let use_stdin = true;
32-
let mut pam = PamContext::new_cli("su", context, use_stdin, false, false, false, Some(user))?;
32+
let mut pam = PamContext::new_cli(
33+
"su",
34+
context,
35+
use_stdin,
36+
false,
37+
false,
38+
false,
39+
None,
40+
Some(user),
41+
)?;
3342
pam.set_requesting_user(requesting_user)?;
3443

3544
// attempt to set the TTY this session is communicating on

src/sudo/pam.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ use crate::common::context::LaunchType;
44
use crate::common::error::Error;
55
use crate::log::{dev_info, user_warn};
66
use crate::pam::{PamContext, PamError, PamErrorType, PamResult};
7-
use crate::system::term::current_tty_name;
7+
use crate::system::{term::current_tty_name, time::Duration};
88

99
pub(super) struct InitPamArgs<'a> {
1010
pub(super) launch: LaunchType,
1111
pub(super) use_stdin: bool,
1212
pub(super) bell: bool,
1313
pub(super) non_interactive: bool,
1414
pub(super) password_feedback: bool,
15+
pub(super) password_timeout: Option<Duration>,
1516
pub(super) auth_prompt: Option<String>,
1617
pub(super) auth_user: &'a str,
1718
pub(super) requesting_user: &'a str,
@@ -26,6 +27,7 @@ pub(super) fn init_pam(
2627
bell,
2728
non_interactive,
2829
password_feedback,
30+
password_timeout,
2931
auth_prompt,
3032
auth_user,
3133
requesting_user,
@@ -44,6 +46,7 @@ pub(super) fn init_pam(
4446
bell,
4547
non_interactive,
4648
password_feedback,
49+
password_timeout,
4750
Some(auth_user),
4851
)?;
4952
pam.mark_silent(matches!(launch, LaunchType::Direct));

src/sudo/pipeline.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ fn auth_and_update_record_file(
149149
must_authenticate,
150150
prior_validity,
151151
allowed_attempts,
152+
password_timeout,
152153
ref credential,
153154
pwfeedback,
154155
}: Authentication,
@@ -178,6 +179,7 @@ fn auth_and_update_record_file(
178179
bell: context.bell,
179180
non_interactive: context.non_interactive,
180181
password_feedback: pwfeedback,
182+
password_timeout,
181183
auth_prompt: context.prompt.clone(),
182184
auth_user: &auth_user.name,
183185
requesting_user: &context.current_user.name,

src/sudoers/policy.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct Authentication {
3030
pub allowed_attempts: u16,
3131
pub prior_validity: Duration,
3232
pub pwfeedback: bool,
33+
pub password_timeout: Option<Duration>,
3334
}
3435

3536
impl super::Settings {
@@ -39,6 +40,10 @@ impl super::Settings {
3940
allowed_attempts: self.passwd_tries().try_into().unwrap(),
4041
prior_validity: Duration::seconds(self.timestamp_timeout()),
4142
pwfeedback: self.pwfeedback(),
43+
password_timeout: match self.passwd_timeout() {
44+
0 => None,
45+
timeout => Some(Duration::seconds(timeout)),
46+
},
4247
credential: if self.rootpw() {
4348
AuthenticatingUser::Root
4449
} else if self.targetpw() {
@@ -183,6 +188,7 @@ mod test {
183188
prior_validity: Duration::minutes(15),
184189
credential: AuthenticatingUser::InvokingUser,
185190
pwfeedback: false,
191+
password_timeout: Some(Duration::seconds(300)),
186192
},
187193
);
188194

@@ -199,6 +205,7 @@ mod test {
199205
prior_validity: Duration::minutes(15),
200206
credential: AuthenticatingUser::InvokingUser,
201207
pwfeedback: false,
208+
password_timeout: Some(Duration::seconds(300)),
202209
},
203210
);
204211
assert_eq!(restrictions, restrictions2);

0 commit comments

Comments
 (0)