smb_server_proto_smb2/
negotiate.rs1pub const DIALECT_202: u16 = 0x0202;
6pub const DIALECT_210: u16 = 0x0210;
8pub const DIALECT_300: u16 = 0x0300;
10pub const DIALECT_302: u16 = 0x0302;
12pub const DIALECT_311: u16 = 0x0311;
14pub const DIALECT_WILDCARD: u16 = 0x02FF;
17
18pub const SIGNING_ENABLED: u16 = 0x0001;
21pub const SIGNING_REQUIRED: u16 = 0x0002;
23
24pub mod caps {
26 pub const LEASING: u32 = 0x0000_0002;
28 pub const LARGE_MTU: u32 = 0x0000_0004;
30 pub const MULTI_CHANNEL: u32 = 0x0000_0008;
32 pub const PERSISTENT_HANDLES: u32 = 0x0000_0010;
34 pub const DIRECTORY_LEASING: u32 = 0x0000_0020;
36 pub const ENCRYPTION: u32 = 0x0000_0040;
38 pub const NOTIFICATIONS: u32 = 0x0000_0080;
40}
41
42pub mod ctx_type {
44 pub const PREAUTH_INTEGRITY: u16 = 0x0001;
46 pub const ENCRYPTION: u16 = 0x0002;
48 pub const COMPRESSION: u16 = 0x0003;
50 pub const SIGNING: u16 = 0x0008;
52 pub const SIGNING_HMAC_SHA256: u16 = 0x0000;
54 pub const SIGNING_AES128_CMAC: u16 = 0x0001;
56 pub const SIGNING_AES128_GMAC: u16 = 0x0002;
58 pub const SHA512: u16 = 0x0001;
60 pub const AES128_CCM: u16 = 0x0001;
62 pub const AES128_GCM: u16 = 0x0002;
64 pub const AES256_CCM: u16 = 0x0003;
66 pub const AES256_GCM: u16 = 0x0004;
68}
69
70#[derive(Debug, Clone)]
72pub struct Context {
73 pub kind: u16,
75 pub data: Vec<u8>,
77}
78
79#[derive(Debug)]
81pub struct Request {
82 pub dialects: Vec<u16>,
84 pub client_guid: [u8; 16],
86 pub contexts: Vec<Context>,
88}
89
90impl Request {
91 pub fn parse(b: &[u8]) -> Option<Request> {
99 const KNOWN: &[u16] = &[0x0202, 0x0210, 0x0300, 0x0302, 0x0310, 0x0311];
100 if b.len() < 28 || u16::from_le_bytes([b[0], b[1]]) != 36 {
101 return None;
102 }
103 let dcount = u16::from_le_bytes([b[2], b[3]]) as usize;
104 if dcount == 0 || dcount > 64 {
105 return None;
106 }
107 let mut guid = [0u8; 16];
108 guid.copy_from_slice(b.get(12..28)?);
109
110 let read_dialects = |start: usize| -> Option<Vec<u16>> {
111 if b.len() < start + dcount * 2 {
112 return None;
113 }
114 let v: Vec<u16> = b[start..]
115 .chunks_exact(2)
116 .take(dcount)
117 .map(|c| u16::from_le_bytes([c[0], c[1]]))
118 .collect();
119 (v.iter().all(|d| KNOWN.contains(d))).then_some(v)
121 };
122
123 let dialects = read_dialects(28)
124 .or_else(|| read_dialects(36))
125 .or_else(|| read_dialects(44))?;
126
127 let mut contexts = Vec::new();
130 if b.len() >= 36 {
131 let ctx_off = u32::from_le_bytes(b[28..32].try_into().unwrap()) as usize;
132 let ctx_count = u16::from_le_bytes([b[32], b[33]]) as usize;
133 if ctx_off >= 64 && ctx_count > 0 && ctx_off <= 64 + b.len() {
134 let base = ctx_off - 64; let mut p = base;
136 for _ in 0..ctx_count.min(64) {
137 let Some(hdr) = b.get(p..p + 8) else { break };
138 let kind = u16::from_le_bytes([hdr[0], hdr[1]]);
139 let dlen = u16::from_le_bytes([hdr[2], hdr[3]]) as usize;
140 let Some(data) = b.get(p + 8..p + 8 + dlen) else { break };
141 contexts.push(Context { kind, data: data.to_vec() });
142 p += 8 + ((dlen + 7) & !7usize);
144 }
145 }
146 }
147
148 Some(Request { dialects, client_guid: guid, contexts })
149 }
150}
151
152pub fn pick(dialects: &[u16]) -> Option<u16> {
154 let mut best = None;
155 for &d in dialects {
156 if matches!(d, DIALECT_202 | DIALECT_210 | DIALECT_300 | DIALECT_302 | DIALECT_311) {
157 best = Some(best.map_or(d, |b: u16| b.max(d)));
158 }
159 }
160 best
161}
162
163pub const SUPPORTED_SIGNING: &[u16] = &[
165 ctx_type::SIGNING_AES128_GMAC,
166 ctx_type::SIGNING_AES128_CMAC,
167 ctx_type::SIGNING_HMAC_SHA256,
168];
169
170pub fn parse_signing_algos(data: &[u8]) -> Vec<u16> {
173 if data.len() < 2 {
174 return Vec::new();
175 }
176 let count = u16::from_le_bytes([data[0], data[1]]) as usize;
177 data.get(2..)
178 .map(|r| {
179 r.chunks_exact(2)
180 .take(count)
181 .map(|c| u16::from_le_bytes([c[0], c[1]]))
182 .collect()
183 })
184 .unwrap_or_default()
185}
186
187pub fn select_signing_algo(client: &[u16]) -> Option<u16> {
190 client.iter().copied().find(|a| SUPPORTED_SIGNING.contains(a))
191}
192
193#[allow(clippy::too_many_arguments)]
200pub fn build_response_full(
201 dialect: u16,
202 guid: &[u8; 16],
203 now: u64,
204 salt: &[u8; 32],
205 encryption: Option<u16>,
206 signing_algo: Option<u16>,
207 compression: &[u16],
208 compression_chained: bool,
209 require_signing: bool,
210 notifications: bool,
211) -> Vec<u8> {
212 let mut b = Vec::with_capacity(128);
213 b.extend_from_slice(&65u16.to_le_bytes()); let sec_mode = SIGNING_ENABLED | if require_signing { SIGNING_REQUIRED } else { 0 };
215 b.extend_from_slice(&sec_mode.to_le_bytes()); b.extend_from_slice(&dialect.to_le_bytes());
217 if dialect == DIALECT_311 {
218 let count = 1
221 + u16::from(signing_algo.is_some())
222 + u16::from(encryption.is_some())
223 + u16::from(!compression.is_empty());
224 b.extend_from_slice(&count.to_le_bytes()); } else {
226 b.extend_from_slice(&0u16.to_le_bytes()); }
228 b.extend_from_slice(guid); let caps = if dialect >= DIALECT_300 {
230 caps::LARGE_MTU | caps::MULTI_CHANNEL | caps::LEASING | caps::DIRECTORY_LEASING | caps::PERSISTENT_HANDLES
231 } else if dialect >= DIALECT_210 {
232 caps::LARGE_MTU | caps::LEASING
233 } else {
234 0
235 };
236 let caps = caps | if notifications { caps::NOTIFICATIONS } else { 0 };
240 b.extend_from_slice(&caps.to_le_bytes()); b.extend_from_slice(&(1024 * 1024u32).to_le_bytes()); b.extend_from_slice(&(1024 * 1024u32).to_le_bytes()); b.extend_from_slice(&(1024 * 1024u32).to_le_bytes()); b.extend_from_slice(&now.to_le_bytes()); b.extend_from_slice(&now.to_le_bytes()); b.extend_from_slice(&128u16.to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&0u32.to_le_bytes()); let mut ctx = Vec::new();
253 if dialect == DIALECT_311 {
254 let push_ctx = |ctx: &mut Vec<u8>, kind: u16, data: &[u8]| {
255 while !ctx.len().is_multiple_of(8) {
261 ctx.push(0);
262 }
263 ctx.extend_from_slice(&kind.to_le_bytes());
264 ctx.extend_from_slice(&(data.len() as u16).to_le_bytes());
265 ctx.extend_from_slice(&0u32.to_le_bytes()); ctx.extend_from_slice(data);
267 };
268
269 let mut preauth = Vec::new();
271 preauth.extend_from_slice(&1u16.to_le_bytes()); preauth.extend_from_slice(&(salt.len() as u16).to_le_bytes());
273 preauth.extend_from_slice(&ctx_type::SHA512.to_le_bytes());
274 preauth.extend_from_slice(salt);
275 push_ctx(&mut ctx, ctx_type::PREAUTH_INTEGRITY, &preauth);
276
277 if let Some(algo) = signing_algo {
280 let signing = [1u16.to_le_bytes(), algo.to_le_bytes()].concat();
281 push_ctx(&mut ctx, ctx_type::SIGNING, &signing);
282 }
283
284 if let Some(cipher) = encryption {
287 let enc = [1u16.to_le_bytes(), cipher.to_le_bytes()].concat();
288 push_ctx(&mut ctx, ctx_type::ENCRYPTION, &enc);
289 }
290
291 if !compression.is_empty() {
295 let comp = crate::compress::build_compression_caps(compression, compression_chained);
296 push_ctx(&mut ctx, ctx_type::COMPRESSION, &comp);
297 }
298
299 let ctx_off = BODY_START_FIXED; b[60..64].copy_from_slice(&(ctx_off as u32).to_le_bytes()); b.extend_from_slice(&ctx);
302 }
303 b
304}
305
306const BODY_START_FIXED: usize = 64 + 64;
309
310pub fn build_response(dialect: u16, guid: &[u8; 16], now: u64) -> Vec<u8> {
317 build_response_full(dialect, guid, now, &[0u8; 32], None, None, &[], false, false, false)
318}