use crate::flags::NTLM_NEG_VERSION;
use crate::messages::NTLM_SIGN;
use crate::utils;
use crate::LmResponse;
use crate::Lmv1Response;
use crate::Lmv2Response;
use crate::NtlmAuth;
use crate::NtlmResponse;
use crate::Ntlmv1Response;
use crate::Ntlmv2Response;
use crate::Result;
use crate::Version;
use nom::bytes::complete::{tag, take};
use nom::number::complete::{le_u16, le_u32};
use std::convert::TryInto;
const NTLM_MSG_AUTH: u32 = 0x00000003;
const NTLM_AUTHENTICATE_FIXED_SIZE: u32 = 64;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AuthenticateMsg {
pub flags: u32,
pub version: Option<Version>,
pub mic: Option<[u8; 16]>,
pub lm_response: Option<LmResponse>,
pub ntlm_response: Option<NtlmResponse>,
pub domain_name: String,
pub username: String,
pub workstation: String,
pub encrypted_session_key: Option<Vec<u8>>,
}
impl AuthenticateMsg {
pub fn build(&self) -> Vec<u8> {
let mut raw = NTLM_SIGN.to_vec();
raw.extend(&NTLM_MSG_AUTH.to_le_bytes());
let mut offset = NTLM_AUTHENTICATE_FIXED_SIZE;
let raw_mic = self.mic.map(|m| m.to_vec()).unwrap_or(Vec::new());
offset += raw_mic.len() as u32;
let raw_version = match &self.version {
Some(v) => v.build(),
None => Vec::new(),
};
offset += raw_version.len() as u32;
let raw_dn = utils::to_neg_enc(&self.domain_name, self.flags);
let dn_offset = offset;
offset += raw_dn.len() as u32;
let raw_un = utils::to_neg_enc(&self.username, self.flags);
let un_offset = offset;
offset += raw_un.len() as u32;
let raw_ws = utils::to_neg_enc(&self.workstation, self.flags);
let ws_offset = offset;
offset += raw_ws.len() as u32;
let raw_lm_resp = self
.lm_response
.as_ref()
.map(|l| l.build())
.unwrap_or(Vec::new());
let lm_resp_offset = offset;
offset += raw_lm_resp.len() as u32;
let raw_nt_resp = self
.ntlm_response
.as_ref()
.map(|n| n.build())
.unwrap_or(Vec::new());
let nt_resp_offset = offset;
offset += raw_nt_resp.len() as u32;
let raw_enc_key = self
.encrypted_session_key
.as_ref()
.map(|v| v.clone())
.unwrap_or(Vec::new());
let enc_key_offset = offset;
enc_len_offset!(raw, raw_lm_resp, lm_resp_offset);
enc_len_offset!(raw, raw_nt_resp, nt_resp_offset);
enc_len_offset!(raw, raw_dn, dn_offset);
enc_len_offset!(raw, raw_un, un_offset);
enc_len_offset!(raw, raw_ws, ws_offset);
enc_len_offset!(raw, raw_enc_key, enc_key_offset);
raw.extend(&self.flags.to_le_bytes());
raw.extend(raw_version);
raw.extend(raw_mic);
raw.extend(raw_dn);
raw.extend(raw_un);
raw.extend(raw_ws);
raw.extend(raw_lm_resp);
raw.extend(raw_nt_resp);
raw.extend(raw_enc_key);
return raw;
}
pub fn parse(raw: &[u8], auth_version: NtlmAuth) -> Result<Self> {
let (rest, _) = tag(NTLM_SIGN)(raw)?;
let (rest, _) = tag(NTLM_MSG_AUTH.to_le_bytes())(rest)?;
let (rest, lm_resp_len, lm_resp_offset) = dec_len_offset!(rest);
let (rest, nt_resp_len, nt_resp_offset) = dec_len_offset!(rest);
let (rest, dn_len, dn_offset) = dec_len_offset!(rest);
let (rest, un_len, un_offset) = dec_len_offset!(rest);
let (rest, ws_len, ws_offset) = dec_len_offset!(rest);
let (rest, enc_key_len, enc_key_offset) = dec_len_offset!(rest);
let (rest, flags) = le_u32(rest)?;
let (rest, version) = if flag!(flags, NTLM_NEG_VERSION) {
let (r, v) = Version::parse(rest)?;
(r, Some(v))
} else {
(rest, None)
};
let (_, raw_mic) = take(16usize)(rest)?;
let mic = Some(raw_mic.try_into().unwrap());
let (rest_dn, _) = take(dn_offset as usize)(raw)?;
let (_, raw_dn) = take(dn_len as usize)(rest_dn)?;
let domain_name = utils::from_neg_enc(raw_dn, flags)?;
let (rest_un, _) = take(un_offset as usize)(raw)?;
let (_, raw_un) = take(un_len as usize)(rest_un)?;
let username = utils::from_neg_enc(raw_un, flags)?;
let (rest_ws, _) = take(ws_offset as usize)(raw)?;
let (_, raw_ws) = take(ws_len as usize)(rest_ws)?;
let workstation = utils::from_neg_enc(raw_ws, flags)?;
let encrypted_session_key = match enc_key_len {
0 => None,
_ => {
let (rest_ws, _) = take(enc_key_offset as usize)(raw)?;
let (_, raw_enc_key) = take(enc_key_len as usize)(rest_ws)?;
Some(raw_enc_key.to_vec())
}
};
let lm_response = match lm_resp_len {
0 => None,
_ => {
let (rest_lm_resp, _) = take(lm_resp_offset as usize)(raw)?;
let (_, raw_lm_resp) =
take(lm_resp_len as usize)(rest_lm_resp)?;
Some(match auth_version {
NtlmAuth::V1 => {
LmResponse::V1(Lmv1Response::parse(raw_lm_resp)?)
}
NtlmAuth::V2 => {
LmResponse::V2(Lmv2Response::parse(raw_lm_resp)?)
}
})
}
};
let ntlm_response = match nt_resp_len {
0 => None,
_ => {
let (rest_nt_resp, _) = take(nt_resp_offset as usize)(raw)?;
let (_, raw_nt_resp) =
take(nt_resp_len as usize)(rest_nt_resp)?;
Some(match auth_version {
NtlmAuth::V1 => {
NtlmResponse::V1(Ntlmv1Response::parse(raw_nt_resp)?)
}
NtlmAuth::V2 => {
NtlmResponse::V2(Ntlmv2Response::parse(raw_nt_resp)?)
}
})
}
};
return Ok(Self {
flags,
version,
mic,
lm_response,
ntlm_response,
domain_name,
username,
workstation,
encrypted_session_key,
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::flags::*;
use crate::time::new_time;
use crate::AvPair;
use crate::SingleHost;
use crate::{NtlmClientChallenge, Ntlmv2Response};
const RAW_AUTHENTICATE: &'static [u8] = &[
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00,
0x18, 0x00, 0x18, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x42, 0x01, 0x42, 0x01,
0x94, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x58, 0x00, 0x00, 0x00,
0x08, 0x00, 0x08, 0x00, 0x66, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00,
0x6e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd6, 0x01, 0x00, 0x00,
0x15, 0x82, 0x88, 0xe2, 0x0a, 0x00, 0x61, 0x4a, 0x00, 0x00, 0x00, 0x0f,
0x08, 0x20, 0x71, 0x80, 0x34, 0x9a, 0xc5, 0xd0, 0xf6, 0x7b, 0xc3, 0x57,
0xa7, 0x37, 0x7c, 0x26, 0x57, 0x00, 0x53, 0x00, 0x30, 0x00, 0x31, 0x00,
0x2d, 0x00, 0x31, 0x00, 0x30, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00,
0x72, 0x00, 0x57, 0x00, 0x53, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2d, 0x00,
0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0xdc, 0xd0, 0xec, 0xfb, 0x56, 0xa7,
0xbe, 0x41, 0x11, 0xb1, 0x1a, 0x9e, 0x9a, 0xd8, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf6, 0x4c, 0xaf, 0xcd, 0x87, 0xf1, 0xd6, 0x01,
0x4d, 0x5d, 0xf8, 0x0f, 0x00, 0x31, 0xba, 0xeb, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x0e, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x54, 0x00,
0x4f, 0x00, 0x53, 0x00, 0x4f, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x57, 0x00,
0x53, 0x00, 0x30, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x37, 0x00, 0x04, 0x00,
0x1a, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x6f, 0x00,
0x73, 0x00, 0x6f, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00,
0x61, 0x00, 0x6c, 0x00, 0x03, 0x00, 0x28, 0x00, 0x77, 0x00, 0x73, 0x00,
0x30, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x63, 0x00,
0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00,
0x05, 0x00, 0x1a, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00,
0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00,
0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x07, 0x00, 0x08, 0x00, 0xf6, 0x4c,
0xaf, 0xcd, 0x87, 0xf1, 0xd6, 0x01, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00,
0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x5c, 0x74,
0x92, 0x2f, 0x73, 0x8e, 0x04, 0x40, 0xa1, 0x1c, 0x94, 0x89, 0xfa, 0x40,
0xba, 0xea, 0x73, 0x8e, 0x34, 0xdd, 0x43, 0x7e, 0xfd, 0x32, 0xb1, 0x7d,
0xe6, 0x1a, 0x56, 0x31, 0xf9, 0x40, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x63, 0x00, 0x69, 0x00, 0x66, 0x00,
0x73, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00,
0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00,
0x30, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x9d, 0x2d, 0xd3, 0x9c, 0x81, 0x5d, 0xec, 0xce, 0xee, 0x76,
0x3b, 0x08, 0xab, 0xc1, 0x98, 0x8d,
];
fn gen_auth_msg() -> AuthenticateMsg {
let mut auth = AuthenticateMsg::default();
auth.flags = NTLM_NEG_56
| NTLM_NEG_KEY_EXCH
| NTLM_NEG_128
| NTLM_NEG_VERSION
| NTLM_NEG_TARGET_INFO
| NTLM_NEG_EXTENDED_SECURITY
| NTLM_NEG_ALWAYS_SIGN
| NTLM_NEG_NTLM
| NTLM_NEG_SIGN
| NTLM_REQUEST_TARGET
| NTLM_NEG_UNICODE;
auth.domain_name = "WS01-10".to_string();
auth.username = "user".to_string();
auth.workstation = "WS01-10".to_string();
auth.version = Some(Version {
major: 10,
minor: 0,
build: 19041,
revision: 15,
});
auth.encrypted_session_key = Some(vec![
0x9d, 0x2d, 0xd3, 0x9c, 0x81, 0x5d, 0xec, 0xce, 0xee, 0x76, 0x3b,
0x08, 0xab, 0xc1, 0x98, 0x8d,
]);
auth.mic = Some([
0x08, 0x20, 0x71, 0x80, 0x34, 0x9a, 0xc5, 0xd0, 0xf6, 0x7b, 0xc3,
0x57, 0xa7, 0x37, 0x7c, 0x26,
]);
auth.lm_response =
Some(LmResponse::V2(Lmv2Response::new([0; 16], [0; 8])));
auth.ntlm_response = Some(NtlmResponse::V2(Ntlmv2Response {
response: [
0xb0, 0x00, 0xdc, 0xd0, 0xec, 0xfb, 0x56, 0xa7, 0xbe, 0x41,
0x11, 0xb1, 0x1a, 0x9e, 0x9a, 0xd8,
],
client_challenge: NtlmClientChallenge {
timestamp: new_time(2021, 1, 23, 13, 01, 02, 770507800),
client_challenge: [
0x4d, 0x5d, 0xf8, 0x0f, 0x00, 0x31, 0xba, 0xeb,
],
av_pairs: vec![
AvPair::NbDomainName("CONTOSO".to_string()),
AvPair::NbComputerName("WS02-7".to_string()),
AvPair::DnsDomainName("contoso.local".to_string()),
AvPair::DnsComputerName("ws02-7.contoso.local".to_string()),
AvPair::DnsTreeName("contoso.local".to_string()),
AvPair::Timestamp(new_time(
2021, 1, 23, 13, 01, 02, 770507800,
)),
AvPair::Flags(0x00000002),
AvPair::SingleHost(SingleHost {
data: [0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00],
machine_id: [
0x5c, 0x74, 0x92, 0x2f, 0x73, 0x8e, 0x04, 0x40,
0xa1, 0x1c, 0x94, 0x89, 0xfa, 0x40, 0xba, 0xea,
0x73, 0x8e, 0x34, 0xdd, 0x43, 0x7e, 0xfd, 0x32,
0xb1, 0x7d, 0xe6, 0x1a, 0x56, 0x31, 0xf9, 0x40,
],
}),
AvPair::ChannelBindings([0; 16]),
AvPair::TargetName("cifs/192.168.100.7".to_string()),
]
.into(),
},
}));
return auth;
}
#[test]
fn test_build_authenticate_msg() {
let auth = gen_auth_msg();
assert_eq!(RAW_AUTHENTICATE.to_vec(), auth.build());
}
#[test]
fn test_parse_authenticate_msg() {
let auth = gen_auth_msg();
assert_eq!(
auth,
AuthenticateMsg::parse(RAW_AUTHENTICATE, NtlmAuth::V2).unwrap()
);
}
}