From 50b793db8d3421bbfe2bce060a486263f18a90cb Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 20 Apr 2023 16:08:00 +0200 Subject: [PATCH] tfa: add data for rate limiting and blocking TfaUserData uses `#[serde(deny_unknown_fields)]`, so we add this now, but using it will require explicitly enabling it. If the TOTP count is high, the user should be locked out of TOTP entirely until they use a recovery key to reset the count. If a user's TFA try count is too high, they should get rate limited. In both cases they should receive some kind of notification. Signed-off-by: Wolfgang Bumiller --- proxmox-tfa/src/api/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/proxmox-tfa/src/api/mod.rs b/proxmox-tfa/src/api/mod.rs index 3a0373c0..ec9bea40 100644 --- a/proxmox-tfa/src/api/mod.rs +++ b/proxmox-tfa/src/api/mod.rs @@ -331,6 +331,15 @@ pub struct TfaUserData { /// available for PVE, where the yubico API server configuration is part if the realm. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub yubico: Vec>, + + /// Once a user runs into a TOTP limit they get locked out of TOTP until they successfully use + /// a recovery key. + #[serde(skip_serializing_if = "bool_is_false", default)] + pub totp_locked: bool, + + /// If a user hits too many 2nd factor failures, they get completely blocked for a while. + #[serde(skip_serializing_if = "Option::is_none", default)] + pub tfa_blocked_until: Option, } impl TfaUserData { @@ -924,6 +933,19 @@ pub struct TfaUserChallenges { #[serde(skip_serializing_if = "Vec::is_empty", default)] #[serde(deserialize_with = "filter_expired_challenge")] webauthn_auths: Vec, + + /// Number of consecutive TOTP failures. Too many of those will lock out a user. + #[serde(skip_serializing_if = "u32_is_zero", default)] + totp_failures: u32, + + /// Number of consecutive 2nd factor failures. When the limit is reached, the user is locked + /// out for 12 hours. + #[serde(skip_serializing_if = "u32_is_zero", default)] + tfa_failures: u32, +} + +fn u32_is_zero(n: &u32) -> bool { + *n == 0 } /// Serde helper using our `FilteredVecVisitor` to filter out expired entries directly at load