sspi_bobbobbio/ntlm/messages/client/
negotiate.rs1use std::io;
2
3use byteorder::{LittleEndian, WriteBytesExt};
4
5use crate::ntlm::messages::{MessageFields, MessageTypes, NTLM_SIGNATURE, NTLM_VERSION_SIZE};
6use crate::ntlm::{NegotiateFlags, NegotiateMessage, Ntlm, NtlmState};
7use crate::SecurityStatus;
8
9const HEADER_SIZE: usize = 32;
10const NEGO_MESSAGE_OFFSET: usize = HEADER_SIZE + NTLM_VERSION_SIZE;
11
12struct NegotiateMessageFields {
13 domain_name: MessageFields,
14 workstation: MessageFields,
15}
16
17impl NegotiateMessageFields {
18 pub fn new(offset: u32, workstation: Option<Vec<u8>>) -> Self {
19 let mut domain_name = MessageFields::new();
20 let mut workstation = MessageFields::with_buffer(workstation.unwrap_or_default());
21
22 domain_name.buffer_offset = offset;
23 workstation.buffer_offset = domain_name.buffer_offset + domain_name.buffer.len() as u32;
24
25 NegotiateMessageFields {
26 domain_name,
27 workstation,
28 }
29 }
30
31 pub fn data_len(&self) -> usize {
32 self.workstation.buffer_offset as usize + self.workstation.buffer.len()
33 }
34}
35
36fn check_state(state: NtlmState) -> crate::Result<()> {
37 if state != NtlmState::Negotiate {
38 Err(crate::Error::new(
39 crate::ErrorKind::OutOfSequence,
40 String::from("Write negotiate was fired but the state is not a Negotiate"),
41 ))
42 } else {
43 Ok(())
44 }
45}
46
47pub fn write_negotiate(context: &mut Ntlm, mut transport: impl io::Write) -> crate::Result<SecurityStatus> {
48 check_state(context.state)?;
49
50 let negotiate_flags = get_flags(context);
51 let message_fields = NegotiateMessageFields::new(
52 NEGO_MESSAGE_OFFSET as u32,
53 context
54 .config
55 .workstation
56 .as_ref()
57 .map(|workstation| workstation.as_bytes().to_vec()),
58 );
59
60 let mut buffer = Vec::with_capacity(message_fields.data_len());
61
62 write_header(negotiate_flags, context.version.as_ref(), &message_fields, &mut buffer)?;
63 write_payload(&message_fields, &mut buffer)?;
64 context.flags = negotiate_flags;
65
66 let message = buffer;
67
68 transport.write_all(message.as_slice())?;
69 transport.flush()?;
70
71 context.negotiate_message = Some(NegotiateMessage::new(message));
72 context.state = NtlmState::Challenge;
73
74 Ok(crate::SecurityStatus::ContinueNeeded)
75}
76
77fn get_flags(context: &Ntlm) -> NegotiateFlags {
78 let mut flags = NegotiateFlags::NTLM_SSP_NEGOTIATE56
80 | NegotiateFlags::NTLM_SSP_NEGOTIATE_LM_KEY
81 | NegotiateFlags::NTLM_SSP_NEGOTIATE_OEM
82 | NegotiateFlags::NTLM_SSP_NEGOTIATE_SEAL
84 | NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH
86 | NegotiateFlags::NTLM_SSP_NEGOTIATE128
87 | NegotiateFlags::NTLM_SSP_NEGOTIATE_EXTENDED_SESSION_SECURITY
88 | NegotiateFlags::NTLM_SSP_NEGOTIATE_ALWAYS_SIGN
89 | NegotiateFlags::NTLM_SSP_NEGOTIATE_NTLM
90 | NegotiateFlags::NTLM_SSP_NEGOTIATE_SIGN
91 | NegotiateFlags::NTLM_SSP_NEGOTIATE_REQUEST_TARGET
92 | NegotiateFlags::NTLM_SSP_NEGOTIATE_UNICODE
93 | NegotiateFlags::NTLM_SSP_NEGOTIATE_VERSION;
94
95 if context.config().workstation.is_some() {
96 flags |= NegotiateFlags::NTLM_SSP_NEGOTIATE_WORKSTATION_SUPPLIED;
97 }
98
99 flags
100}
101
102fn write_header(
103 negotiate_flags: NegotiateFlags,
104 version: &[u8],
105 message_fields: &NegotiateMessageFields,
106 mut buffer: impl io::Write,
107) -> io::Result<()> {
108 buffer.write_all(NTLM_SIGNATURE)?; buffer.write_u32::<LittleEndian>(MessageTypes::Negotiate as u32)?; buffer.write_u32::<LittleEndian>(negotiate_flags.bits())?; message_fields.domain_name.write_to(&mut buffer)?; message_fields.workstation.write_to(&mut buffer)?; buffer.write_all(version)?;
114
115 Ok(())
116}
117
118fn write_payload(message_fields: &NegotiateMessageFields, mut buffer: impl io::Write) -> io::Result<()> {
119 message_fields.domain_name.write_buffer_to(&mut buffer)?;
120 message_fields.workstation.write_buffer_to(&mut buffer)?;
121
122 Ok(())
123}