Harden deferred audit LOWs: provisioned-verifier cost cap, BOT-nick validation, gameserv board-delivery account guard, empty KICK/PART/QUIT reason parse, and relink SASL/enforce cleanup
All checks were successful
CI / check (push) Successful in 5m4s

This commit is contained in:
Jean Chevronnet 2026-07-20 04:40:42 +00:00
parent 1b569fdfa3
commit 4027242c1e
No known key found for this signature in database
13 changed files with 144 additions and 19 deletions

View file

@ -162,6 +162,16 @@ pub fn parse_verifier(encoded: &str) -> Option<Verifier> {
})
}
// An externally supplied verifier (a gRPC Provision backfill) is untrusted for
// its cost parameter: a huge `i=` would make every later PBKDF2 verify burn a
// core — a compromised-authority DoS. Accept only a well-formed verifier whose
// iteration count is in a sane range (the floor matches the config minimum, the
// ceiling is a few multiples of the default so a real backfill still fits).
pub const MAX_VERIFIER_ITERATIONS: u32 = 5_000_000;
pub fn verifier_ok(encoded: &str) -> bool {
parse_verifier(encoded).is_some_and(|v| (1000..=MAX_VERIFIER_ITERATIONS).contains(&v.iterations))
}
// --- exchange ---
pub struct ClientFirst {
@ -351,4 +361,19 @@ mod tests {
assert!(prove(PASSWORD).is_some(), "the real password authenticates against the authority's verifier");
assert!(prove("wrong").is_none(), "a wrong password is rejected");
}
#[test]
fn verifier_ok_rejects_absurd_iteration_cost() {
// Derive one cheap verifier, then swap the `i=` to probe the range — never
// actually run PBKDF2 at an absurd count (that IS the DoS we're bounding).
let cheap = make_verifier(Hash::Sha256, "pw", 4096);
assert!(verifier_ok(&cheap));
let with_iters = |i: &str| cheap.replacen("i=4096", &format!("i={i}"), 1);
// A huge iteration count (compromised-authority CPU DoS) is refused.
assert!(!verifier_ok(&with_iters("4294967295")));
assert!(!verifier_ok(&with_iters(&(MAX_VERIFIER_ITERATIONS + 1).to_string())));
// A too-cheap or malformed verifier is refused.
assert!(!verifier_ok(&with_iters("100")));
assert!(!verifier_ok("not a verifier"));
}
}