Chg: Test long-term and short-term key.

This commit is contained in:
ghost 2025-12-28 22:24:43 +01:00
parent f000ef6a76
commit 95c06a4dae
2 changed files with 37 additions and 2 deletions

View File

@ -3,7 +3,7 @@
use crate::config::AuthOptions;
use crate::constants::{ATTR_NONCE, ATTR_REALM, ATTR_USERNAME};
use crate::models::stun::StunMessage;
use crate::stun::{find_message_integrity, validate_message_integrity};
use crate::stun::{find_message_integrity, validate_message_integrity, validate_message_integrity_len_preserved};
use crate::traits::CredentialStore;
use async_trait::async_trait;
use base64::Engine;
@ -187,7 +187,9 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
// Workaround: also accept short-term style (raw password as key) for test clients like turnutils_uclient.
let short_key = password.as_bytes();
if validate_message_integrity(msg, short_key) {
if validate_message_integrity(msg, short_key)
|| validate_message_integrity_len_preserved(msg, short_key)
{
warn!("auth accept via short-term key username={} realm={} peer={} (workaround)", username, realm, peer);
return AuthStatus::Granted {
username,
@ -195,6 +197,13 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
};
}
// Additional interop fallback: some clients miscompute length when adding FINGERPRINT;
// try validation without adjusting the header length.
if validate_message_integrity_len_preserved(msg, &key) {
warn!("auth accept via len-preserved MI username={} realm={} peer={} (interop fallback)", username, realm, peer);
return AuthStatus::Granted { username, key };
}
let key_hex = hex::encode(&key);
warn!("auth reject: bad credentials username={} realm={} peer={} a1_md5={} (debug)", username, realm, peer, key_hex);
AuthStatus::Reject {

View File

@ -386,6 +386,32 @@ pub fn validate_message_integrity(msg: &StunMessage, key: &[u8]) -> bool {
false
}
/// 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.
pub fn validate_message_integrity_len_preserved(msg: &StunMessage, key: &[u8]) -> bool {
if let Some(mi) = find_message_integrity(msg) {
if mi.value.len() != HMAC_SHA1_LEN {
return false;
}
let mi_end = mi.offset + 4 + HMAC_SHA1_LEN;
if mi_end > msg.raw.len() {
return false;
}
let mut signed = msg.raw[..mi_end].to_vec();
let value_start = mi.offset + 4;
for b in &mut signed[value_start..value_start + HMAC_SHA1_LEN] {
*b = 0;
}
let computed = crate::stun::compute_message_integrity(key, &signed);
return &computed[..HMAC_SHA1_LEN] == mi.value.as_slice();
}
false
}
fn append_message_integrity(buf: &mut bytes::BytesMut, key: &[u8]) {
// Append attribute header and placeholder; set length to end-of-MI, then compute
// HMAC over the message slice up to end-of-MI (with the MI placeholder still zero).