Add: More MI variants.
This commit is contained in:
parent
563f5156e6
commit
a79d0f2a95
47
src/auth.rs
47
src/auth.rs
@ -6,10 +6,13 @@ use crate::models::stun::StunMessage;
|
||||
use crate::stun::{
|
||||
compute_message_integrity_adjusted,
|
||||
compute_message_integrity_adjusted_nozero,
|
||||
compute_message_integrity_before_mi,
|
||||
compute_message_integrity_full,
|
||||
compute_message_integrity_full_nozero,
|
||||
compute_message_integrity_full_len_to_mi_end,
|
||||
compute_message_integrity_len_preserved as compute_mi_len_preserved,
|
||||
compute_message_integrity_len_preserved_nozero,
|
||||
compute_message_integrity_through_mi_header,
|
||||
find_message_integrity,
|
||||
validate_message_integrity,
|
||||
validate_message_integrity_len_preserved_nozero,
|
||||
@ -356,10 +359,25 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
||||
let mi_short_full_adj_nozero = compute_message_integrity_full_nozero(msg, short_key, true, false).map(hex::encode);
|
||||
let mi_short_full_adj_nozero_zfp = compute_message_integrity_full_nozero(msg, short_key, true, true).map(hex::encode);
|
||||
|
||||
let mi_long_full_len_to_mi_end =
|
||||
compute_message_integrity_full_len_to_mi_end(msg, &key, true, false).map(hex::encode);
|
||||
let mi_long_full_len_to_mi_end_nozero =
|
||||
compute_message_integrity_full_len_to_mi_end(msg, &key, false, false).map(hex::encode);
|
||||
let mi_long_full_len_to_mi_end_nozero_zfp =
|
||||
compute_message_integrity_full_len_to_mi_end(msg, &key, false, true).map(hex::encode);
|
||||
let mi_short_full_len_to_mi_end =
|
||||
compute_message_integrity_full_len_to_mi_end(msg, short_key, true, false).map(hex::encode);
|
||||
|
||||
let mi_long_before_mi_len_to_mi_end =
|
||||
compute_message_integrity_before_mi(msg, &key, true).map(hex::encode);
|
||||
let mi_long_before_mi_len_before_mi =
|
||||
compute_message_integrity_before_mi(msg, &key, false).map(hex::encode);
|
||||
let mi_long_through_mi_hdr = compute_message_integrity_through_mi_header(msg, &key).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<Vec<u8>>); 12] = [
|
||||
let variants: [(&str, Option<Vec<u8>>); 28] = [
|
||||
("long_adj", compute_message_integrity_adjusted(msg, &key)),
|
||||
("long_len", compute_mi_len_preserved(msg, &key)),
|
||||
("long_adj_nozero", compute_message_integrity_adjusted_nozero(msg, &key)),
|
||||
@ -372,6 +390,22 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
||||
("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)),
|
||||
("long_full_adj_nozero", compute_message_integrity_full_nozero(msg, &key, true, false)),
|
||||
("long_full_adj_nozero_zfp", compute_message_integrity_full_nozero(msg, &key, true, true)),
|
||||
("short_full_adj_nozero", compute_message_integrity_full_nozero(msg, short_key, true, false)),
|
||||
("short_full_adj_nozero_zfp", compute_message_integrity_full_nozero(msg, short_key, true, true)),
|
||||
("long_full_len_to_mi_end", compute_message_integrity_full_len_to_mi_end(msg, &key, true, false)),
|
||||
("long_full_len_to_mi_end_nozero", compute_message_integrity_full_len_to_mi_end(msg, &key, false, false)),
|
||||
("long_full_len_to_mi_end_nozero_zfp", compute_message_integrity_full_len_to_mi_end(msg, &key, false, true)),
|
||||
("short_full_len_to_mi_end", compute_message_integrity_full_len_to_mi_end(msg, short_key, true, false)),
|
||||
("short_full_len_to_mi_end_nozero", compute_message_integrity_full_len_to_mi_end(msg, short_key, false, false)),
|
||||
("short_full_len_to_mi_end_nozero_zfp", compute_message_integrity_full_len_to_mi_end(msg, short_key, false, true)),
|
||||
("long_before_mi_len_to_mi_end", compute_message_integrity_before_mi(msg, &key, true)),
|
||||
("long_before_mi_len_before_mi", compute_message_integrity_before_mi(msg, &key, false)),
|
||||
("long_through_mi_header", compute_message_integrity_through_mi_header(msg, &key)),
|
||||
("short_before_mi_len_to_mi_end", compute_message_integrity_before_mi(msg, short_key, true)),
|
||||
("short_before_mi_len_before_mi", compute_message_integrity_before_mi(msg, short_key, false)),
|
||||
("short_through_mi_header", compute_message_integrity_through_mi_header(msg, short_key)),
|
||||
];
|
||||
|
||||
for (label, cand) in variants.iter() {
|
||||
@ -385,7 +419,7 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
||||
}
|
||||
|
||||
warn!(
|
||||
"auth reject: bad credentials username={} realm={} peer={} a1_md5={} mi_attr={:?} mi_long_adj={:?} mi_long_len={:?} mi_long_adj_nozero={:?} mi_long_len_nozero={:?} mi_short_adj={:?} mi_short_len={:?} mi_short_adj_nozero={:?} mi_short_len_nozero={:?} mi_long_full_adj={:?} mi_long_full_len={:?} mi_short_full_adj={:?} mi_short_full_len={:?} mi_long_full_adj_nozero={:?} mi_long_full_adj_nozero_zfp={:?} mi_short_full_adj_nozero={:?} mi_short_full_adj_nozero_zfp={:?}",
|
||||
"auth reject: bad credentials username={} realm={} peer={} a1_md5={} mi_attr={:?} mi_long_adj={:?} mi_long_len={:?} mi_long_adj_nozero={:?} mi_long_len_nozero={:?} mi_short_adj={:?} mi_short_len={:?} mi_short_adj_nozero={:?} mi_short_len_nozero={:?} mi_long_full_adj={:?} mi_long_full_len={:?} mi_short_full_adj={:?} mi_short_full_len={:?} mi_long_full_adj_nozero={:?} mi_long_full_adj_nozero_zfp={:?} mi_short_full_adj_nozero={:?} mi_short_full_adj_nozero_zfp={:?} mi_long_full_len_to_mi_end={:?} mi_long_full_len_to_mi_end_nozero={:?} mi_long_full_len_to_mi_end_nozero_zfp={:?} mi_short_full_len_to_mi_end={:?} mi_long_before_mi_len_to_mi_end={:?} mi_long_before_mi_len_before_mi={:?} mi_long_through_mi_hdr={:?}",
|
||||
username,
|
||||
realm,
|
||||
peer,
|
||||
@ -406,7 +440,14 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
||||
mi_long_full_adj_nozero,
|
||||
mi_long_full_adj_nozero_zfp,
|
||||
mi_short_full_adj_nozero,
|
||||
mi_short_full_adj_nozero_zfp
|
||||
mi_short_full_adj_nozero_zfp,
|
||||
mi_long_full_len_to_mi_end,
|
||||
mi_long_full_len_to_mi_end_nozero,
|
||||
mi_long_full_len_to_mi_end_nozero_zfp,
|
||||
mi_short_full_len_to_mi_end,
|
||||
mi_long_before_mi_len_to_mi_end,
|
||||
mi_long_before_mi_len_before_mi,
|
||||
mi_long_through_mi_hdr
|
||||
);
|
||||
AuthStatus::Reject {
|
||||
code: 401,
|
||||
|
||||
112
src/stun.rs
112
src/stun.rs
@ -722,6 +722,118 @@ pub fn compute_message_integrity(key: &[u8], msg: &[u8]) -> Vec<u8> {
|
||||
mac.finalize().into_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Compute MESSAGE-INTEGRITY over the full raw message, but force the STUN header length
|
||||
/// field to the end-of-MESSAGE-INTEGRITY value (i.e. exclude any attributes after MI
|
||||
/// from the length field), while still MAC'ing the full buffer.
|
||||
///
|
||||
/// This is non-standard, but it matches a class of buggy implementations where the
|
||||
/// length field is adjusted correctly, yet the HMAC input accidentally includes bytes
|
||||
/// after MESSAGE-INTEGRITY (e.g. FINGERPRINT).
|
||||
pub fn compute_message_integrity_full_len_to_mi_end(
|
||||
msg: &StunMessage,
|
||||
key: &[u8],
|
||||
zero_mi: bool,
|
||||
zero_fingerprint: bool,
|
||||
) -> Option<Vec<u8>> {
|
||||
let mi = find_message_integrity(msg)?;
|
||||
if mi.value.len() != HMAC_SHA1_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mi_end = mi.offset + 4 + HMAC_SHA1_LEN;
|
||||
if mi_end > msg.raw.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut signed = msg.raw.clone();
|
||||
let len = (mi_end - 20) as u16;
|
||||
signed[2..4].copy_from_slice(&len.to_be_bytes());
|
||||
|
||||
if zero_mi {
|
||||
let v = mi.offset + 4;
|
||||
if v + HMAC_SHA1_LEN > signed.len() {
|
||||
return None;
|
||||
}
|
||||
signed[v..v + HMAC_SHA1_LEN].fill(0);
|
||||
}
|
||||
|
||||
if zero_fingerprint {
|
||||
if let Some(fp) = find_fingerprint(msg) {
|
||||
if fp.value.len() == 4 && fp.offset + 8 == msg.raw.len() {
|
||||
let v = fp.offset + 4;
|
||||
if v + 4 <= signed.len() {
|
||||
signed[v..v + 4].fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(crate::stun::compute_message_integrity(key, &signed))
|
||||
}
|
||||
|
||||
/// Compute MESSAGE-INTEGRITY over the bytes *before* the MESSAGE-INTEGRITY attribute.
|
||||
///
|
||||
/// Non-standard interop variant: some clients appear to MAC only the prefix and ignore
|
||||
/// the MI attribute itself.
|
||||
///
|
||||
/// If `len_to_mi_end` is true, the header length is set to end-of-MI (RFC-ish length)
|
||||
/// even though the MAC input ends before MI. If false, the header length is set to the
|
||||
/// prefix length (i.e. start-of-MI - 20).
|
||||
pub fn compute_message_integrity_before_mi(
|
||||
msg: &StunMessage,
|
||||
key: &[u8],
|
||||
len_to_mi_end: bool,
|
||||
) -> Option<Vec<u8>> {
|
||||
let mi = find_message_integrity(msg)?;
|
||||
if mi.value.len() != HMAC_SHA1_LEN {
|
||||
return None;
|
||||
}
|
||||
let mi_end = mi.offset + 4 + HMAC_SHA1_LEN;
|
||||
if mi_end > msg.raw.len() {
|
||||
return None;
|
||||
}
|
||||
if mi.offset < 20 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut signed = msg.raw[..mi.offset].to_vec();
|
||||
let len = if len_to_mi_end {
|
||||
(mi_end - 20) as u16
|
||||
} else {
|
||||
(mi.offset - 20) as u16
|
||||
};
|
||||
signed[2..4].copy_from_slice(&len.to_be_bytes());
|
||||
Some(crate::stun::compute_message_integrity(key, &signed))
|
||||
}
|
||||
|
||||
/// Compute MESSAGE-INTEGRITY over the bytes up to the MI attribute header (type+len),
|
||||
/// excluding the MI value bytes.
|
||||
///
|
||||
/// Non-standard interop variant: clients that include the MI header but not its value.
|
||||
pub fn compute_message_integrity_through_mi_header(
|
||||
msg: &StunMessage,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
let mi = find_message_integrity(msg)?;
|
||||
if mi.value.len() != HMAC_SHA1_LEN {
|
||||
return None;
|
||||
}
|
||||
let mi_end = mi.offset + 4 + HMAC_SHA1_LEN;
|
||||
if mi_end > msg.raw.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let end = mi.offset + 4;
|
||||
if end > msg.raw.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut signed = msg.raw[..end].to_vec();
|
||||
let len = (mi_end - 20) as u16;
|
||||
signed[2..4].copy_from_slice(&len.to_be_bytes());
|
||||
Some(crate::stun::compute_message_integrity(key, &signed))
|
||||
}
|
||||
|
||||
/// STUN/TURN attribute type for XOR-RELAYED-ADDRESS per RFC5766
|
||||
/// (use ATTR_XOR_RELAYED_ADDRESS from crate::constants)
|
||||
// no-op; refer to constants::ATTR_XOR_RELAYED_ADDRESS where needed
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user