Skip to content

Commit 9e91993

Browse files
MggMugginssquell
authored andcommitted
test: passwd_timeout
1 parent b175fc8 commit 9e91993

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

test-framework/sudo-compliance-tests/src/sudo/sudoers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod host_list;
1313
mod include;
1414
mod includedir;
1515
mod noexec;
16+
mod passwd_timeout;
1617
mod run_as;
1718
mod runas_alias;
1819
mod secure_path;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::thread;
2+
use std::time::Duration;
3+
4+
use sudo_test::{Command, Env, TextFile, User};
5+
6+
use crate::{Result, PASSWORD, USERNAME};
7+
8+
#[test]
9+
fn time_out() -> Result<()> {
10+
let timeout_seconds = 6;
11+
12+
let script = include_str!("passwd_timeout.sh");
13+
let script_path = "/tmp/passwd_timeout.sh";
14+
15+
let env = Env(format!(
16+
"{USERNAME} ALL=(ALL:ALL) ALL\nDefaults passwd_timeout={}",
17+
timeout_seconds as f64 / 60.0,
18+
))
19+
.user(User(USERNAME).password(PASSWORD))
20+
.file(script_path, TextFile(script).chmod("777"))
21+
.build();
22+
23+
let mut child = Command::new("sh")
24+
.arg(script_path)
25+
.as_user(USERNAME)
26+
.spawn(&env);
27+
28+
thread::sleep(Duration::from_secs(timeout_seconds + 1));
29+
30+
match child.try_wait()? {
31+
None => {
32+
child.kill()?;
33+
panic!("passwd_timeout did not force exit: {:?}", child.wait());
34+
}
35+
Some(status) => {
36+
if status.success() {
37+
panic!("succeeded without password: {:?}", child.wait());
38+
}
39+
}
40+
}
41+
42+
let output = child.wait();
43+
let diagnostic = if sudo_test::is_original_sudo() {
44+
"timed out reading password"
45+
} else {
46+
"timed out"
47+
};
48+
assert_contains!(output.stderr(), diagnostic);
49+
Ok(())
50+
}
51+
52+
#[test]
53+
fn dont_time_out() -> Result<()> {
54+
let timeout_seconds = 5;
55+
56+
let script = include_str!("passwd_timeout.sh");
57+
let script_path = "/tmp/passwd_timeout.sh";
58+
59+
let env = Env(format!(
60+
"{USERNAME} ALL=(ALL:ALL) ALL\nDefaults passwd_timeout={}",
61+
timeout_seconds as f64 / 60.0,
62+
))
63+
.user(User(USERNAME).password(PASSWORD))
64+
.file(script_path, TextFile(script).chmod("777"))
65+
.build();
66+
67+
let mut child = Command::new("sh")
68+
.arg(script_path)
69+
.as_user(USERNAME)
70+
.spawn(&env);
71+
72+
thread::sleep(Duration::from_secs(timeout_seconds - 2));
73+
74+
child.kill()?;
75+
76+
let output = dbg!(child.wait());
77+
assert_not_contains!(output.stderr(), "timed out");
78+
Ok(())
79+
}
80+
81+
#[test]
82+
fn zero_time_out() -> Result<()> {
83+
let script = include_str!("passwd_timeout.sh");
84+
let script_path = "/tmp/passwd_timeout.sh";
85+
86+
let env = Env(format!(
87+
"{USERNAME} ALL=(ALL:ALL) ALL\nDefaults passwd_timeout=0"
88+
))
89+
.user(User(USERNAME).password(PASSWORD))
90+
.file(script_path, TextFile(script).chmod("777"))
91+
.build();
92+
93+
let mut child = Command::new("sh")
94+
.arg(script_path)
95+
.as_user(USERNAME)
96+
.spawn(&env);
97+
98+
thread::sleep(Duration::from_secs(5));
99+
100+
match child.try_wait()? {
101+
None => {
102+
child.kill()?;
103+
}
104+
Some(status) => {
105+
if let Some(code) = status.code() {
106+
println!("passwd_timeout=0 exited: {:?}", code);
107+
println!("{:?}", child.wait());
108+
panic!();
109+
}
110+
}
111+
}
112+
113+
let output = child.wait();
114+
assert_not_contains!(output.stderr(), "timed out");
115+
Ok(())
116+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/sh
2+
3+
set -ex
4+
5+
tmp="$(mktemp)"
6+
rm "${tmp}"
7+
mkfifo "${tmp}"
8+
9+
# reads against the fifo will block without returning EOF
10+
sudo -S true <> "${tmp}"

test-framework/sudo-test/src/docker/command.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ impl Child {
132132
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
133133
Ok(self.inner.try_wait()?)
134134
}
135+
136+
/// Send SIGKILL to the process.
137+
pub fn kill(&mut self) -> Result<()> {
138+
Ok(self.inner.kill()?)
139+
}
135140
}
136141

137142
/// the output of a finished `Command`

0 commit comments

Comments
 (0)