Skip to content

Commit 5d96272

Browse files
authored
Fix potential panic with misconfiguration (#1049)
2 parents 4337071 + ccdcb3a commit 5d96272

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

docs/man/sudoers.5.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ sudo's behavior can be modified by Default_Entry lines, as explained earlier. A
368368

369369
* timestamp_timeout
370370

371-
Number of minutes that can elapse before sudo will ask for a passwd again. The timeout may include a fractional component if minute granularity is insufficient, for example 2.5. The default is 15. Set this to 0 to always prompt for a password. If set to a value less than 0 the user's timestamp will not expire until the system is rebooted. This can be used to allow users to create or delete their own timestamps via “sudo -v” and “sudo -k” respectively.
371+
Number of minutes that can elapse before sudo will ask for a passwd again. The timeout may include a fractional component if minute granularity is insufficient, for example 2.5. The default is 15. Set this to 0 to always prompt for a password.
372372

373373
## Strings that can be used in a boolean context:
374374

src/defaults/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,18 @@ defaults! {
6262
/// A custom parser to parse seconds as fractional "minutes", the format used by
6363
/// passwd_timeout and timestamp_timeout.
6464
fn fractional_minutes(input: &str) -> Option<i64> {
65-
if input.contains('.') {
66-
Some((input.parse::<f64>().ok()? * 60.0).floor() as i64)
65+
if let Some((integral, fractional)) = input.split_once('.') {
66+
// - 'input' is maximally 18 characters, making fractional.len() at most 17;
67+
// 1e17 < 2**63, so the definition of 'shift' will not overflow.
68+
// - for the same reason, if both parses in the definition of 'seconds' succeed,
69+
// we will have constructed an integer < 1e17.
70+
//- 1e17 * 60 = 6e18 < 9e18 < 2**63, so the final line also will not overflow
71+
let shift = 10i64.pow(fractional.len().try_into().ok()?);
72+
let seconds = integral.parse::<i64>().ok()? * shift + fractional.parse::<i64>().ok()?;
73+
74+
Some(seconds * 60 / shift)
6775
} else {
68-
Some(input.parse::<i64>().ok()? * 60)
76+
input.parse::<i64>().ok()?.checked_mul(60)
6977
}
7078
}
7179

@@ -123,6 +131,10 @@ mod test {
123131
panic!()
124132
};
125133
f("any").unwrap()(&mut def);
134+
let SettingKind::Integer(f) = set("timestamp_timeout").unwrap() else {
135+
panic!()
136+
};
137+
f("25.25").unwrap()(&mut def);
126138
assert_eq! { def.always_query_group_plugin, false };
127139
assert_eq! { def.always_set_home, false };
128140
assert_eq! { def.env_reset, true };
@@ -132,6 +144,7 @@ mod test {
132144
assert_eq! { def.visiblepw, false };
133145
assert_eq! { def.env_editor, true };
134146
assert_eq! { def.passwd_tries, 5 };
147+
assert_eq! { def.timestamp_timeout, 25*60 + 60/4 };
135148
assert_eq! { def.secure_path, Some("/bin".into()) };
136149
assert! { def.env_check.is_empty() };
137150
assert_eq! { def.verifypw, enums::verifypw::any };

src/sudoers/ast_names.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod names {
1717
}
1818

1919
impl UserFriendly for tokens::Numeric {
20-
const DESCRIPTION: &'static str = "number";
20+
const DESCRIPTION: &'static str = "nonnegative number";
2121
}
2222

2323
impl UserFriendly for Identifier {

0 commit comments

Comments
 (0)