From abf9b876599deb32af89d447051b24a5a0e7c54b Mon Sep 17 00:00:00 2001 From: ghost Date: Sun, 28 Dec 2025 22:35:50 +0100 Subject: [PATCH] Accept any one MI variants. --- src/auth.rs | 38 ++++++++++++++++++++++++++++++++++++-- src/stun.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 5ed344e..476f267 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -5,6 +5,7 @@ use crate::constants::{ATTR_NONCE, ATTR_REALM, ATTR_USERNAME}; use crate::models::stun::StunMessage; use crate::stun::{ compute_message_integrity_adjusted, + compute_message_integrity_full, compute_message_integrity_len_preserved as compute_mi_len_preserved, find_message_integrity, validate_message_integrity, @@ -216,8 +217,37 @@ impl AuthManager { let mi_long_len = compute_mi_len_preserved(msg, &key).map(hex::encode); let mi_short_adj = compute_message_integrity_adjusted(msg, short_key).map(hex::encode); let mi_short_len = compute_mi_len_preserved(msg, short_key).map(hex::encode); + let mi_long_full_adj = compute_message_integrity_full(msg, &key, true).map(hex::encode); + let mi_long_full_len = compute_message_integrity_full(msg, &key, false).map(hex::encode); + let mi_short_full_adj = compute_message_integrity_full(msg, short_key, true).map(hex::encode); + let mi_short_full_len = compute_message_integrity_full(msg, short_key, false).map(hex::encode); + + // Accept if any variant matches received MI (still requires correct key). + if let Some(mi_attr_val) = find_message_integrity(msg) { + let mi_bytes = &mi_attr_val.value; + let variants: [(&str, Option>); 8] = [ + ("long_adj", compute_message_integrity_adjusted(msg, &key)), + ("long_len", compute_mi_len_preserved(msg, &key)), + ("short_adj", compute_message_integrity_adjusted(msg, short_key)), + ("short_len", compute_mi_len_preserved(msg, short_key)), + ("long_full_adj", compute_message_integrity_full(msg, &key, true)), + ("long_full_len", compute_message_integrity_full(msg, &key, false)), + ("short_full_adj", compute_message_integrity_full(msg, short_key, true)), + ("short_full_len", compute_message_integrity_full(msg, short_key, false)), + ]; + + for (label, cand) in variants.iter() { + if let Some(c) = cand { + if c.len() >= 20 && &c[..20] == mi_bytes.as_slice() { + warn!("auth accept via MI variant={} username={} realm={} peer={} (interop)", label, username, realm, peer); + return AuthStatus::Granted { username, key }; + } + } + } + } + warn!( - "auth reject: bad credentials username={} realm={} peer={} a1_md5={} mi_attr={:?} mi_long_adj={:?} mi_long_len={:?} mi_short_adj={:?} mi_short_len={:?}", + "auth reject: bad credentials username={} realm={} peer={} a1_md5={} mi_attr={:?} mi_long_adj={:?} mi_long_len={:?} mi_short_adj={:?} mi_short_len={:?} mi_long_full_adj={:?} mi_long_full_len={:?} mi_short_full_adj={:?} mi_short_full_len={:?}", username, realm, peer, @@ -226,7 +256,11 @@ impl AuthManager { mi_long_adj, mi_long_len, mi_short_adj, - mi_short_len + mi_short_len, + mi_long_full_adj, + mi_long_full_len, + mi_short_full_adj, + mi_short_full_len ); AuthStatus::Reject { code: 401, diff --git a/src/stun.rs b/src/stun.rs index 030bb8e..b559813 100644 --- a/src/stun.rs +++ b/src/stun.rs @@ -431,6 +431,33 @@ pub fn compute_message_integrity_len_preserved(msg: &StunMessage, key: &[u8]) -> Some(crate::stun::compute_message_integrity(key, &signed)) } +/// Compute MESSAGE-INTEGRITY over the full raw message (including any attributes after MI). +/// If `adjust_len` is true, the header length is set to `raw.len() - 20`, otherwise preserved. +pub fn compute_message_integrity_full(msg: &StunMessage, key: &[u8], adjust_len: bool) -> Option> { + let mi = find_message_integrity(msg)?; + if mi.value.len() != HMAC_SHA1_LEN { + return None; + } + + let mut signed = msg.raw.clone(); + if adjust_len { + let len = (signed.len() - 20) as u16; + let len_bytes = len.to_be_bytes(); + signed[2] = len_bytes[0]; + signed[3] = len_bytes[1]; + } + + let value_start = mi.offset + 4; + if value_start + HMAC_SHA1_LEN > signed.len() { + return None; + } + for b in &mut signed[value_start..value_start + HMAC_SHA1_LEN] { + *b = 0; + } + + Some(crate::stun::compute_message_integrity(key, &signed)) +} + /// Fallback validator: compute MESSAGE-INTEGRITY without adjusting the STUN header length. /// Some clients incorrectly leave the header length unchanged when appending FINGERPRINT; /// this matches that behaviour for interop.